From 10a3d1bf43adb57bfc5cbf2c51fcc8704b39bc9d Mon Sep 17 00:00:00 2001 From: Julian Rueth Date: Tue, 9 Apr 2013 18:44:45 +0000 Subject: [PATCH 001/217] (HEAD, refs/heads/stefan) Trac #14432: Fix __hash__ for unions of sets --- src/sage/sets/set.py | 339 ++++++++++++++++++------------------------- 1 file changed, 143 insertions(+), 196 deletions(-) diff --git a/src/sage/sets/set.py b/src/sage/sets/set.py index 242221a3cf5..5369c0f2fb5 100644 --- a/src/sage/sets/set.py +++ b/src/sage/sets/set.py @@ -14,10 +14,14 @@ - Florent Hivert (2010-06-17) - Adapted to categories - Nicolas M. Thiery (2011-03-15) - Added subset and superset methods + +- Julian Rueth (2013-04-09) - Collected common code in Set_object_binary, fixed __hash__. + """ #***************************************************************************** # Copyright (C) 2005 William Stein +# 2013 Julian Rueth # # Distributed under the terms of the GNU General Public License (GPL) # @@ -103,9 +107,9 @@ def Set(X): Set of all prime numbers: 2, 3, 5, 7, ... sage: Set(Subsets([1,2,3])).cardinality() 8 - sage: Set(iter([1,2,3])) + sage: S = Set(iter([1,2,3])); S {1, 2, 3} - sage: type(_) + sage: type(S) sage: S = Set([]) sage: TestSuite(S).run() @@ -964,7 +968,112 @@ def symmetric_difference(self, other): return Set_object.symmetric_difference(self, other) return Set_object_enumerated(self.set().symmetric_difference(other.set())) -class Set_object_union(Set_object): +class Set_object_binary(Set_object): + """ + An abstract common base class for :class:`Set_object_union`, + :class:`Set_object_intersection`, :class:`Set_object_difference`, and + :class:`Set_object_symmetric_difference`. + + INPUT: + + - ``X``, ``Y`` -- sets, the operands to ``op`` + + - ``op`` -- a string describing the binary operation + + - ``latex_op`` -- a string used for rendering this object in LaTeX + + EXAMPLES:: + + sage: X = Set(QQ^2) + sage: Y = Set(ZZ) + sage: from sage.sets.set import Set_object_binary + sage: S = Set_object_binary(X,Y,"union","\\cup"); S + Set-theoretic union of Set of elements of Vector space of dimension 2 over Rational Field and Set of elements of Integer Ring + + """ + def __init__(self, X, Y, op, latex_op): + """ + Initialization. + + TESTS:: + + sage: from sage.sets.set import Set_object_binary + sage: X = Set(QQ^2) + sage: Y = Set(ZZ) + sage: S = Set_object_binary(X,Y,"union","\\cup") + sage: type(S) + + + """ + self._X = X + self._Y = Y + self._op = op + self._latex_op = latex_op + Set_object.__init__(self, self) + + def _repr_(self): + r""" + Return a string representation of this set. + + EXAMPLES:: + + sage: Set(ZZ).union(Set(GF(5))) + Set-theoretic union of Set of elements of Integer Ring and {0, 1, 2, 3, 4} + + """ + return "Set-theoretic %s of %s and %s"%(self._op, self._X, self._Y) + + def _latex_(self): + r""" + Return a latex representation of this set. + + EXAMPLES:: + + sage: latex(Set(ZZ).union(Set(GF(5)))) + \Bold{Z} \cup \left\{0, 1, 2, 3, 4\right\} + + """ + return '%s %s %s'%(latex(self._X), self._latex_op, latex(self._Y)) + + def cardinality(self): + """ + This tries to return the cardinality of this set. + + Note that this is not likely to work in very much generality, + and may just hang if either set involved is infinite. + + EXAMPLES:: + + sage: X = Set(GF(13)).intersection(Set(ZZ)) + sage: X.cardinality() + 13 + """ + return len(list(self)) + + def __hash__(self): + """ + The hash value of this set. + + EXAMPLES:: + + sage: X = Set(GF(13)).intersection(Set(ZZ)) + sage: hash(X) + -7116597539855252506 # 64-bit + # 32-bit + + TESTS: + + Test that :trac:`14432` has been resolved:: + + sage: S=Set(ZZ).union(Set([infinity])) + sage: hash(S) + 1474285102670710969 # 64-bit + # 32-bit + + """ + return hash((self._X,self._Y,self._op)) + +class Set_object_union(Set_object_binary): """ A formal union of two sets. """ @@ -982,9 +1091,7 @@ def __init__(self, X, Y): sage: TestSuite(X).run() """ - self.__X = X - self.__Y = Y - Set_object.__init__(self, self) + Set_object_binary.__init__(self, X, Y, "union", "\\cup") def __cmp__(self, right): r""" @@ -1018,34 +1125,11 @@ def __cmp__(self, right): return -1 if not isinstance(right, Set_object_union): return -1 - if self.__X == right.__X and self.__Y == right.__Y or \ - self.__X == right.__Y and self.__Y == right.__X: + if self._X == right._X and self._Y == right._Y or \ + self._X == right._Y and self._Y == right._X: return 0 return -1 - def _repr_(self): - r""" - Return string representation of self. - - EXAMPLES:: - - sage: Set(ZZ).union(Set(GF(5))) - Set-theoretic union of Set of elements of Integer Ring and {0, 1, 2, 3, 4} - """ - return "Set-theoretic union of %s and %s"%(self.__X, - self.__Y) - - def _latex_(self): - r""" - Return latex representation of self. - - EXAMPLES:: - - sage: latex(Set(ZZ).union(Set(GF(5)))) - \Bold{Z} \cup \left\{0, 1, 2, 3, 4\right\} - """ - return '%s \\cup %s'%(latex(self.__X), latex(self.__Y)) - def __iter__(self): """ Return iterator over the elements of self. @@ -1055,9 +1139,9 @@ def __iter__(self): sage: [x for x in Set(GF(3)).union(Set(GF(2)))] [0, 1, 2, 0, 1] """ - for x in self.__X: + for x in self._X: yield x - for y in self.__Y: + for y in self._Y: yield y def __contains__(self, x): @@ -1076,7 +1160,7 @@ def __contains__(self, x): sage: GF(5)(0) in X False """ - return x in self.__X or x in self.__Y + return x in self._X or x in self._Y def cardinality(self): """ @@ -1094,9 +1178,9 @@ def cardinality(self): sage: X.cardinality() +Infinity """ - return self.__X.cardinality() + self.__Y.cardinality() + return self._X.cardinality() + self._Y.cardinality() -class Set_object_intersection(Set_object): +class Set_object_intersection(Set_object_binary): """ Formal intersection of two sets. """ @@ -1114,10 +1198,7 @@ def __init__(self, X, Y): sage: X = Set(IntegerRange(100)).intersection(Primes()) sage: TestSuite(X).run() """ - self.__X = X - self.__Y = Y - Set_object.__init__(self, self) - + Set_object_binary.__init__(self, X, Y, "intersection", "\\cap") def __cmp__(self, right): r""" @@ -1151,38 +1232,11 @@ def __cmp__(self, right): return -1 if not isinstance(right, Set_object_intersection): return -1 - if self.__X == right.__X and self.__Y == right.__Y or \ - self.__X == right.__Y and self.__Y == right.__X: + if self._X == right._X and self._Y == right._Y or \ + self._X == right._Y and self._Y == right._X: return 0 return -1 - def _repr_(self): - """ - Return string representation of self. - - EXAMPLES:: - - sage: X = Set(ZZ).intersection(Set(QQ)); X - Set-theoretic intersection of Set of elements of Integer Ring and Set of elements of Rational Field - sage: X.rename('Z /\ Q') - sage: X - Z /\ Q - """ - return "Set-theoretic intersection of %s and %s"%(self.__X, - self.__Y) - - def _latex_(self): - r""" - Return latex representation of self. - - EXAMPLES:: - - sage: X = Set(ZZ).intersection(Set(QQ)) - sage: latex(X) - \Bold{Z} \cap \Bold{Q} - """ - return '%s \\cap %s'%(latex(self.__X), latex(self.__Y)) - def __iter__(self): """ Return iterator through elements of self. @@ -1198,8 +1252,8 @@ def __iter__(self): sage: I.next() 2 """ - for x in self.__X: - if x in self.__Y: + for x in self._X: + if x in self._Y: yield x def __contains__(self, x): @@ -1227,26 +1281,9 @@ def __contains__(self, x): sage: pi in X False """ - return x in self.__X and x in self.__Y - - def cardinality(self): - """ - This tries to return the cardinality of this formal intersection. - - Note that this is not likely to work in very much generality, - and may just hang if either set involved is infinite. - - EXAMPLES:: - - sage: X = Set(GF(13)).intersection(Set(ZZ)) - sage: X.cardinality() - 13 - """ - return len(list(self)) + return x in self._X and x in self._Y - - -class Set_object_difference(Set_object): +class Set_object_difference(Set_object_binary): """ Formal difference of two sets. """ @@ -1257,16 +1294,13 @@ def __init__(self, X, Y): sage: S = Set(QQ) sage: T = Set(ZZ) sage: X = S.difference(T); X - Set-theoretic difference between Set of elements of Rational Field and Set of elements of Integer Ring + Set-theoretic difference of Set of elements of Rational Field and Set of elements of Integer Ring sage: latex(X) \Bold{Q} - \Bold{Z} sage: TestSuite(X).run() """ - self.__X = X - self.__Y = Y - Set_object.__init__(self, self) - + Set_object_binary.__init__(self, X, Y, "difference", "-") def __cmp__(self, right): r""" @@ -1304,37 +1338,10 @@ def __cmp__(self, right): return -1 if not isinstance(right, Set_object_difference): return -1 - if self.__X == right.__X and self.__Y == right.__Y: + if self._X == right._X and self._Y == right._Y: return 0 return -1 - def _repr_(self): - """ - Return string representation of self. - - EXAMPLES:: - - sage: X = Set(QQ).difference(Set(ZZ)); X - Set-theoretic difference between Set of elements of Rational Field and Set of elements of Integer Ring - sage: X.rename('Q - Z') - sage: X - Q - Z - """ - return "Set-theoretic difference between %s and %s"%(self.__X, - self.__Y) - - def _latex_(self): - r""" - Return latex representation of self. - - EXAMPLES:: - - sage: X = Set(QQ).difference(Set(ZZ)) - sage: latex(X) - \Bold{Q} - \Bold{Z} - """ - return '%s - %s'%(latex(self.__X), latex(self.__Y)) - def __iter__(self): """ Return iterator through elements of self. @@ -1358,8 +1365,8 @@ def __iter__(self): sage: I.next() -3 """ - for x in self.__X: - if x not in self.__Y: + for x in self._X: + if x not in self._Y: yield x def __contains__(self, x): @@ -1383,24 +1390,10 @@ def __contains__(self, x): sage: 5/2 in X True """ - return x in self.__X and x not in self.__Y - - def cardinality(self): - """ - This tries to return the cardinality of this formal intersection. - - Note that this is not likely to work in very much generality, - and may just hang if either set involved is infinite. + return x in self._X and x not in self._Y - EXAMPLES:: - sage: X = Set(GF(13)).difference(Set(Primes())) - sage: X.cardinality() - 8 - """ - return len(list(self)) - -class Set_object_symmetric_difference(Set_object): +class Set_object_symmetric_difference(Set_object_binary): """ Formal symmetric difference of two sets. """ @@ -1417,10 +1410,7 @@ def __init__(self, X, Y): sage: TestSuite(X).run() """ - self.__X = X - self.__Y = Y - Set_object.__init__(self, self) - + Set_object_binary.__init__(self, X, Y, "symmetric difference", "\\bigtriangleup") def __cmp__(self, right): r""" @@ -1448,38 +1438,11 @@ def __cmp__(self, right): return -1 if not isinstance(right, Set_object_symmetric_difference): return -1 - if self.__X == right.__X and self.__Y == right.__Y or \ - self.__X == right.__Y and self.__Y == right.__X: + if self._X == right._X and self._Y == right._Y or \ + self._X == right._Y and self._Y == right._X: return 0 return -1 - def _repr_(self): - """ - Return string representation of self. - - EXAMPLES:: - - sage: X = Set(ZZ).symmetric_difference(Set(QQ)); X - Set-theoretic symmetric difference of Set of elements of Integer Ring and Set of elements of Rational Field - sage: X.rename('Z symdif Q') - sage: X - Z symdif Q - """ - return "Set-theoretic symmetric difference of %s and %s"%(self.__X, - self.__Y) - - def _latex_(self): - r""" - Return latex representation of self. - - EXAMPLES:: - - sage: X = Set(ZZ).symmetric_difference(Set(QQ)) - sage: latex(X) - \Bold{Z} \bigtriangleup \Bold{Q} - """ - return '%s \\bigtriangleup %s'%(latex(self.__X), latex(self.__Y)) - def __iter__(self): """ Return iterator through elements of self. @@ -1504,15 +1467,14 @@ def __iter__(self): sage: I.next() -3 """ - for x in self.__X: - if x not in self.__Y: + for x in self._X: + if x not in self._Y: yield x - for y in self.__Y: - if y not in self.__X: + for y in self._Y: + if y not in self._X: yield y - def __contains__(self, x): """ Return true if self contains x. @@ -1538,20 +1500,5 @@ def __contains__(self, x): sage: 3 in X False """ - return (x in self.__X and x not in self.__Y) \ - or (x in self.__Y and x not in self.__X) - - def cardinality(self): - """ - This tries to return the cardinality of this formal symmetric difference. - - Note that this is not likely to work in very much generality, - and may just hang if either set involved is infinite. - - EXAMPLES:: - - sage: X = Set(GF(13)).symmetric_difference(Set(range(5))) - sage: X.cardinality() - 8 - """ - return len(list(self)) + return (x in self._X and x not in self._Y) \ + or (x in self._Y and x not in self._X) From e851c6f179661e67e79e8aac44aa82371e7ff963 Mon Sep 17 00:00:00 2001 From: Julian Rueth Date: Wed, 28 Aug 2013 16:05:00 +0200 Subject: [PATCH 002/217] fixed doctest for the hash of a union/intersection/... of sets --- src/sage/sets/set.py | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/sage/sets/set.py b/src/sage/sets/set.py index 5369c0f2fb5..014d10227a4 100644 --- a/src/sage/sets/set.py +++ b/src/sage/sets/set.py @@ -1054,21 +1054,25 @@ def __hash__(self): """ The hash value of this set. - EXAMPLES:: + + EXAMPLES: + + The hash values of equal sets are in general not equal since it is not + decidable whether two sets are equal:: sage: X = Set(GF(13)).intersection(Set(ZZ)) - sage: hash(X) - -7116597539855252506 # 64-bit - # 32-bit + sage: Y = Set(ZZ).intersection(Set(GF(13))) + sage: hash(X) == hash(Y) + False TESTS: Test that :trac:`14432` has been resolved:: - sage: S=Set(ZZ).union(Set([infinity])) - sage: hash(S) - 1474285102670710969 # 64-bit - # 32-bit + sage: S = Set(ZZ).union(Set([infinity])) + sage: T = Set(ZZ).union(Set([infinity])) + sage: hash(S) == hash(T) + True """ return hash((self._X,self._Y,self._op)) From 96aac188ea19fdf840cb5e75b649c873a49aaefe Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Mon, 1 Apr 2013 22:51:33 +0000 Subject: [PATCH 003/217] trac #14396: ISGCI update, small graphs and recognition --- build/pkgs/graphs/SPKG.txt | 3 + build/pkgs/graphs/checksums.ini | 6 +- build/pkgs/graphs/package-version.txt | 2 +- src/sage/graphs/isgci.py | 404 +++++++++++++++++++------- 4 files changed, 298 insertions(+), 117 deletions(-) diff --git a/build/pkgs/graphs/SPKG.txt b/build/pkgs/graphs/SPKG.txt index 195a82c7905..e959d94a449 100644 --- a/build/pkgs/graphs/SPKG.txt +++ b/build/pkgs/graphs/SPKG.txt @@ -20,6 +20,9 @@ N/A == Changelog == +== graphs-20130920.p5 (Nathann Cohen, 2013-09-20) == + * #14396: Update of isgci_sage.xml, added smallgraphs.txt + == graphs-20120404.p4 (R. Andrew Ohana, 2012-05-17) == * #13123: Move SAGE_DATA to SAGE_LOCAL/share diff --git a/build/pkgs/graphs/checksums.ini b/build/pkgs/graphs/checksums.ini index d180503bde6..8978e68f0ee 100644 --- a/build/pkgs/graphs/checksums.ini +++ b/build/pkgs/graphs/checksums.ini @@ -1,4 +1,4 @@ tarball=graphs-VERSION.tar.bz2 -sha1=3454dcbb5162f937f2bb62355cdfffd31ee70eee -md5=e738851391f5351bbd5d1a0e5e952fe3 -cksum=3090942230 +sha1=f78fa61daf3baafa088921f5e578ea19f556f963 +md5=2621b6cfd61797ab0be179653d7bcf3e +cksum=2747417664 diff --git a/build/pkgs/graphs/package-version.txt b/build/pkgs/graphs/package-version.txt index 2cdd9d04b50..fbae6e7f7f5 100644 --- a/build/pkgs/graphs/package-version.txt +++ b/build/pkgs/graphs/package-version.txt @@ -1 +1 @@ -20120404.p4 +20130920.p5 diff --git a/src/sage/graphs/isgci.py b/src/sage/graphs/isgci.py index 5124bd24134..2492ea61be0 100644 --- a/src/sage/graphs/isgci.py +++ b/src/sage/graphs/isgci.py @@ -13,10 +13,6 @@ How to use it? -------------- -The current limited aim of this module is to provide a pure Sage/Python -interface which is easier to deal with than a XML file. I hope it will be -rewritten many times until it stabilizes :-) - Presently, it is possible to use this database through the variables and methods present in the :obj:`graph_classes ` object. For instance:: @@ -24,6 +20,9 @@ sage: Trees = graph_classes.Tree sage: Chordal = graph_classes.Chordal +Inclusions +^^^^^^^^^^ + It is then possible to check the inclusion of classes inside of others, if the information is available in the database:: @@ -32,28 +31,29 @@ And indeed, trees are chordal graphs. -.. WARNING:: +The ISGCI database is not all-knowing, and so comparing two classes can return +``True``, ``False``, or ``Unknown`` (see the :mod:`documentation of the Unknown +truth value `). - The ISGCI database is not all-knowing, and so comparing two classes can - return ``True``, ``False``, or ``Unknown`` (see the :mod:`documentation of - the Unknown truth value `). +An *unknown* answer to ``A <= B`` only means that ISGCI cannot deduce from the +information in its database that ``A`` is a subclass of ``B`` nor that it is +not. For instance, ISGCI does not know at the moment that some chordal graphs +are not trees:: - An *unknown* answer to ``A <= B`` only means that ISGCI cannot deduce from - the information in its database that ``A`` is a subclass of ``B`` nor that - it is not. For instance, ISGCI does not know at the moment that some chordal - graphs are not trees:: + sage: graph_classes.Chordal <= graph_classes.Tree + Unknown - sage: graph_classes.Chordal <= graph_classes.Tree - Unknown +Descriptions +^^^^^^^^^^^^ -Given a graph class, one can obtain its associated information in the -ISGCI database with the :meth:`~GraphClass.description` method:: +Given a graph class, one can obtain its associated information in the ISGCI +database with the :meth:`~GraphClass.description` method:: sage: Chordal.description() Class of graphs : Chordal ------------------------- type : base - ID : gc_32 + id : gc_32 name : chordal Problems : @@ -64,18 +64,26 @@ Cliquewidth : Unbounded Cliquewidth expression : NP-complete Colourability : Linear + Cutwidth : NP-complete Domination : NP-complete + Feedback vertex set : Polynomial + Hamiltonian cycle : NP-complete + Hamiltonian path : NP-complete Independent set : Linear + Maximum bisection : Unknown + Maximum cut : NP-complete + Minimum bisection : Unknown Recognition : Linear Treewidth : Polynomial Weighted clique : Polynomial + Weighted feedback vertex set : Unknown Weighted independent set : Linear It is possible to obtain the complete list of the classes stored in ISGCI by calling the :meth:`~GraphClasses.show_all` method (beware -- long output):: sage: graph_classes.show_all() - ID | name | type | smallgraph + id | name | type | smallgraph ---------------------------------------------------------------------------------------------------------------------- gc_309 | $K_4$--minor--free | base | gc_541 | $N^*$ | base | @@ -96,6 +104,39 @@ sage: GC $P_4$--bipartite graphs +Recognition of graphs +^^^^^^^^^^^^^^^^^^^^^ + +The graph classes represented by the ISGCI database can alternatively be used to +access recognition algorithms. For instance, in order to check that a given +graph is a tree one has the following the options :: + + sage: graphs.PathGraph(5) in graph_classes.Tree + True + +or:: + + sage: graphs.PathGraph(5).is_tree() + True + +Furthermore, all ISGCI graph classes which are defined by the exclusion of a +finite sequence of induced subgraphs benefit from a generic recognition +algorithm. For instance :: + + sage: g = graphs.PetersenGraph() + sage: g in graph_classes.ClawFree + False + sage: g.line_graph() in graph_classes.ClawFree + True + +Or directly from ISGCI :: + + sage: gc = graph_classes.get_class("gc_441") + sage: gc + diamond--free graphs + sage: graphs.PetersenGraph() in gc + True + Predefined classes ------------------ @@ -120,12 +161,15 @@ * - Block - - :meth:`~sage.graphs.generic_graph.GenericGraph.blocks_and_cut_vertices`, + - :meth:`~sage.graphs.generic_graph.GenericGraph.blocks_and_cut_vertices` * - Chordal - :meth:`~sage.graphs.generic_graph.GenericGraph.is_chordal` + * - Claw-Free + - :meth:`~sage.graphs.graph_generators.GraphGenerators.ClawGraph` + * - Comparability - @@ -175,10 +219,9 @@ :meth:`~Graph.is_tree` * - UnitDisk - - :meth:`~sage.graphs.graph_generators.GraphGenerators.IntervalGraph`, + - :meth:`~sage.graphs.graph_generators.GraphGenerators.IntervalGraph` * - UnitInterval - - :meth:`~sage.graphs.generic_graph.GenericGraph.is_interval` Sage's view of ISGCI @@ -191,7 +234,7 @@ Below is what Sage knows of ``gc_249``:: sage: graph_classes.classes()['gc_249'] # random - {'problems': + {'problem': {'Independent set': 'Polynomial', 'Treewidth': 'Unknown', 'Weighted independent set': 'Polynomial', @@ -205,7 +248,7 @@ '3-Colourability': 'NP-complete', 'Recognition': 'Linear'}, 'type': 'base', - 'ID': 'gc_249', + 'id': 'gc_249', 'name': 'line'} **The class inclusion digraph**: Sage remembers the class inclusions through @@ -340,6 +383,7 @@ class is included in another one, we have to check whether there is in the #***************************************************************************** _XML_FILE = "isgci_sage.xml" +_SMALLGRAPHS_FILE = "smallgraphs.txt" class GraphClass(SageObject, CachedRepresentation): r""" @@ -364,10 +408,18 @@ class GraphClass(SageObject, CachedRepresentation): sage: Chordal >= Trees True """ - def __init__(self, name, gc_id): + def __init__(self, name, gc_id, recognition_function = None): r""" Class constructor + INPUT: + + - ``gc_id`` -- the ISGCI class ID + + - ``recognition_function`` -- a function of one argument `g`, which + return boolan answers to the question : *does ``g`` belong to the + class represented by ``gc_id`` ?* + EXAMPLE:: sage: graph_classes.Chordal # indirect doctest @@ -376,6 +428,9 @@ def __init__(self, name, gc_id): self._name = name self._gc_id = gc_id + if not recognition_function is None: + self._recognition_function = recognition_function + def _repr_(self): r""" Returns a short description of the class @@ -459,6 +514,90 @@ def __lt__(self, other): __gt__ = __ne__ = __lt__ + def forbidden_subgraphs(self): + r""" + Returns the list of forbidden induced subgraphs defining the class. + + If the graph class is not defined by a *finite* list of forbidden induced + subgraphs, ``None`` is returned instead. + + EXAMPLES:: + + sage: graph_classes.Perfect.forbidden_subgraphs() + sage: gc = graph_classes.get_class('gc_62') + sage: gc + claw--free graphs + sage: gc.forbidden_subgraphs() + [Graph on 4 vertices] + sage: gc.forbidden_subgraphs()[0].is_isomorphic(graphs.ClawGraph()) + True + """ + classes = GraphClasses().classes() + gc = classes[self._gc_id] + + if gc.get("type",None) != "forbidden": + return None + + excluded = gc.get("smallgraph", None) + + if not excluded: + return None + + if not isinstance(excluded,list): + excluded = [excluded] + + smallgraphs = GraphClasses().smallgraphs() + + if not all(g in smallgraphs for g in excluded): + return None + + return [smallgraphs[g] for g in excluded] + + def __contains__(self, g): + r""" + Tests if ``g`` belongs to the graph class represented by ``self``. + + EXAMPLES:: + + sage: graphs.CompleteBipartiteGraph(3,3) in graph_classes.Bipartite + True + sage: graphs.CompleteGraph(4) in graph_classes.Chordal + True + sage: graphs.CompleteGraph(4) in graph_classes.Comparability + True + sage: graphs.CompleteGraph(4) in graph_classes.Interval + True + sage: graphs.CompleteGraph(4) in graph_classes.Line + True + sage: graphs.CompleteGraph(4) in graph_classes.Perfect + True + sage: graphs.CompleteGraph(4) in graph_classes.Planar + True + sage: graphs.CompleteGraph(4) in graph_classes.Split + True + sage: graphs.PathGraph(4) in graph_classes.Tree + True + """ + from sage.graphs.graph import Graph + + if not isinstance(g, Graph): + return False + + if hasattr(self, "_recognition_function"): + return self._recognition_function(g) + + excluded = self.forbidden_subgraphs() + + if excluded is None: + raise NotImplementedError("No recognition agorithm is available"+ + "for this class.") + + for gg in excluded: + if g.subgraph_search(gg, induced = True): + return False + + return True + def description(self): r""" Prints the information of ISGCI about the current class. @@ -469,7 +608,7 @@ def description(self): Class of graphs : Chordal ------------------------- type : base - ID : gc_32 + id : gc_32 name : chordal Problems : @@ -480,31 +619,39 @@ def description(self): Cliquewidth : Unbounded Cliquewidth expression : NP-complete Colourability : Linear + Cutwidth : NP-complete Domination : NP-complete + Feedback vertex set : Polynomial + Hamiltonian cycle : NP-complete + Hamiltonian path : NP-complete Independent set : Linear + Maximum bisection : Unknown + Maximum cut : NP-complete + Minimum bisection : Unknown Recognition : Linear Treewidth : Polynomial Weighted clique : Polynomial + Weighted feedback vertex set : Unknown Weighted independent set : Linear """ - classes = GraphClasses().classes() - cls = classes[self._gc_id] + print "Class of graphs : "+self._name print "-"*(len(self._name)+18) for key, value in cls.iteritems(): - if value != "" and key != "problems": + if value != "" and key != "problem": print "{0:30} : ".format(key), print value print "\nProblems :" print "-"*11 - for key, value in sorted(cls["problems"].iteritems()): - if value != "": - print "{0:30} : ".format(key), - print value + + for pbname,data in sorted(cls["problem"].items()): + if "complexity" in data: + print "{0:30} : ".format(pbname), + print data["complexity"] from sage.misc.cachefunc import cached_method @@ -540,7 +687,7 @@ def get_class(self, id): if id in classes: c = classes[id] - if "name" in c and c["name"] != "": + if c.get("name",""): name = c["name"] else: name = "class "+str(id) @@ -563,15 +710,14 @@ def classes(self): sage: type(t) sage: sorted(t["gc_151"].keys()) - ['ID', 'name', 'problems', 'type'] + ['id', 'name', 'problem', 'type'] sage: t["gc_151"]['name'] 'cograph' - sage: t["gc_151"]['problems']['Clique'] - 'Linear' + sage: t["gc_151"]['problem']['Clique'] + {'complexity': 'Linear'} """ - classes, inclusions = self._get_ISGCI() - self.inclusions.set_cache(inclusions) - return classes + self._get_ISGCI() + return self.classes() @cached_method def inclusions(self): @@ -593,9 +739,28 @@ def inclusions(self): sage: t[0] {'super': 'gc_2', 'sub': 'gc_1'} """ - self.classes() + self._get_ISGCI() return self.inclusions() + @cached_method + def smallgraphs(self): + r""" + Returns a dictionary associating a graph to a graph description string. + + Upon the first call, this loads the database from the local + XML files. Subsequent calls are cached. + + EXAMPLES:: + + sage: t = graph_classes.inclusions() + sage: type(t) + + sage: t[0] + {'super': 'gc_2', 'sub': 'gc_1'} + """ + self._get_ISGCI() + return self.smallgraphs() + @cached_method def inclusion_digraph(self): r""" @@ -646,81 +811,56 @@ def _download_db(self): try: z.extract(_XML_FILE, os.path.join(SAGE_SHARE,'graphs')) + z.extract(_SMALLGRAPHS_FILE, os.path.join(SAGE_SHARE,'graphs')) except IOError: z.extract(_XML_FILE, SAGE_TMP) + z.extract(_SMALLGRAPHS_FILE, os.path.join(SAGE_SHARE,'graphs')) - - def _parse_db(self, xml_file): + def _parse_db(self, directory): r""" - Parses the ISGCI database and returns its content as Python objects. + Parses the ISGCI database and stores its content in ``self``. INPUT: - - ``xml_file`` -- the name of an XML file containing the data from ISGCI + - ``directory`` -- the name of the directory containing the latest + version of the database. EXAMPLE:: sage: import os sage: from sage.misc.misc import SAGE_SHARE - sage: map(type, graph_classes._parse_db(os.path.join(SAGE_SHARE,'graphs','isgci_sage.xml'))) - [, ] + sage: graph_classes._parse_db(os.path.join(SAGE_SHARE,'graphs')) """ - import xml.dom.minidom - from xml.dom.minidom import Node - - # This method is there to parse the XML file containing the ISGCI - # database. It is admittedly not very pretty, but it builds the class we - # want from the XML file and that's more or less all we ask it to do :-p - - doc = xml.dom.minidom.parse(xml_file) + import xml.etree.cElementTree as ET + import os.path + from sage.graphs.graph import Graph - classes = {} + xml_file = os.path.join(SAGE_SHARE,'graphs',_XML_FILE) + tree = ET.ElementTree(file=xml_file) + root = tree.getroot() + DB = _XML_to_dict(root) giveme = lambda x,y : str(x.getAttribute(y)) - for node in doc.getElementsByTagName("GraphClass"): - ID = str(node.getAttribute("id")) - Dl = {} - smallgraph = [] - Dl["ID"] = giveme(node, "id") - problems = {} - for node2 in node.childNodes: - name = str(node2.nodeName) - if name == "name": - Dl[name] = str(node2.childNodes[0].nodeValue) - - elif name == "smallgraph": - smallgraph.append(str(node2.childNodes[0].nodeValue)) - - elif name == "problem": - problems[giveme(node2, "name")] = giveme(node2, "complexity") - - Dl["problems"] = problems - - if smallgraph: - Dl["smallgraph"] = smallgraph - - if giveme(node, "type"): - Dl["type"] = giveme(node, "type") + classes = {c['id']:c for c in DB['GraphClasses']["GraphClass"]} + for c in classes.itervalues(): + c["problem"] = { pb.pop("name"):pb for pb in c["problem"]} + inclusions = DB['Inclusions']['incl'] - classes[giveme(node, "id")] = Dl + # Parses the list of ISGCI small graphs + smallgraph_file = open(os.path.join(SAGE_SHARE,'graphs',_SMALLGRAPHS_FILE),'r') + smallgraphs = {} - inclusions = [] - for node in doc.getElementsByTagName("incl"): - Dl = {} - for name in ["proper", "confidence", "super", "sub"]: - if giveme(node, name): - Dl[name] = giveme(node, name) + for l in smallgraph_file.readlines(): + key, string = l.split("\t") + smallgraphs[key] = Graph(string) - for node2 in node.childNodes: - name = str(node2.nodeName) - if name == "ref": - Dl[name] = str(node2.childNodes[0].nodeValue) + smallgraph_file.close() - inclusions.append(Dl) - - return classes, inclusions + self.inclusions.set_cache(inclusions) + self.classes.set_cache(classes) + self.smallgraphs.set_cache(smallgraphs) def update_db(self): r""" @@ -769,7 +909,7 @@ def _get_ISGCI(self): EXAMPLE:: - sage: classes, inclusions = graph_classes._get_ISGCI() # long time (4s on sage.math, 2012) + sage: graph_classes._get_ISGCI() # long time (4s on sage.math, 2012) """ import os.path @@ -783,15 +923,15 @@ def _get_ISGCI(self): if (os.path.getmtime(os.path.join(SAGE_DB,_XML_FILE)) > os.path.getmtime(os.path.join(SAGE_SHARE,'graphs',_XML_FILE))): - filename = os.path.join(SAGE_DB,_XML_FILE) + directory = os.path.join(SAGE_DB,_XML_FILE) else: - filename = os.path.join(SAGE_SHARE,'graphs',_XML_FILE) + directory = os.path.join(SAGE_SHARE,'graphs',_XML_FILE) except IOError as e: - filename = os.path.join(SAGE_SHARE,'graphs',_XML_FILE) + directory = os.path.join(SAGE_SHARE,'graphs',_XML_FILE) - return self._parse_db(filename) + self._parse_db(directory) def show_all(self): r""" @@ -800,7 +940,7 @@ def show_all(self): EXAMPLE:: sage: graph_classes.show_all() - ID | name | type | smallgraph + id | name | type | smallgraph ---------------------------------------------------------------------------------------------------------------------- gc_309 | $K_4$--minor--free | base | gc_541 | $N^*$ | base | @@ -817,7 +957,7 @@ def show_all(self): # We want to print the different fields, and this dictionary stores the # maximal number of characters of each field. MAX = { - "ID" : 0, + "id" : 0, "type" : 0, "smallgraph": 0, "name": 0 @@ -825,7 +965,7 @@ def show_all(self): # We sort the classes alphabetically, though we would like to display the # meaningful classes at the top of the list - classes_list.sort(key = lambda x:x.get("name","zzzzz")+"{0:4}".format(int(x["ID"].split('_')[1]))) + classes_list.sort(key = lambda x:x.get("name","zzzzz")+"{0:4}".format(int(x["id"].split('_')[1]))) # Maximum width of a field MAX_LEN = 40 @@ -839,34 +979,72 @@ def show_all(self): MAX[key] = min(length, MAX_LEN) # Head of the table - print ("{0:"+str(MAX["ID"])+"} | {1:"+str(MAX["name"])+"} | {2:"+str(MAX["type"])+"} | {3:"+str(MAX["smallgraph"])+"}").format("ID", "name", "type", "smallgraph") + print ("{0:"+str(MAX["id"])+"} | {1:"+str(MAX["name"])+"} | {2:"+str(MAX["type"])+"} | {3:"+str(MAX["smallgraph"])+"}").format("id", "name", "type", "smallgraph") print "-"*(sum(MAX.values())+9) # Entries for entry in classes_list: - ID = entry.get("ID","") + ID = entry.get("id","") name = entry.get("name","") type = entry.get("type","") smallgraph = entry.get("smallgraph","") - print ("{0:"+str(MAX["ID"])+"} | {1:"+str(MAX["name"])+"} | {2:"+str(MAX["type"])+"} | ").format(ID, name[:MAX_LEN], type[:MAX_LEN])+str(smallgraph)[:MAX_LEN] + print ("{0:"+str(MAX["id"])+"} | {1:"+str(MAX["name"])+"} | {2:"+str(MAX["type"])+"} | ").format(ID, name[:MAX_LEN], type[:MAX_LEN])+str(smallgraph)[:MAX_LEN] + +def _XML_to_dict(root): + r""" + Returns the XML data as a dictionary + + INPUT: + + - ``root`` -- an ``xml.etree.cElementTree.ElementTree`` object. + + OUTPUT: + + A dictionary representing the XML data. + + EXAMPLE:: + + sage: graph_classes.Perfect.description() # indirect doctest + Class of graphs : Perfect + ------------------------- + type : base + id : gc_56 + name : perfect + ... + """ + ans = root.attrib.copy() + for child in root: + if child.tag in ans: + if not isinstance(ans[child.tag],list): + ans[child.tag] = [ans[child.tag]] + ans[child.tag].append(_XML_to_dict(child)) + else: + ans[child.tag] = _XML_to_dict(child) + + # If the dictionary is empty, perhaps the only content is a text, and we + # return this instead. Useful sometimes in the ISGCI db, for graph names. + if not ans: + return root.text + return ans graph_classes = GraphClasses() # Any object added to this list should also appear in the class' documentation, at the top of the file. graph_classes.BinaryTrees = GraphClass("BinaryTrees", "gc_847") -graph_classes.Bipartite = GraphClass("Bipartite", "gc_69") +graph_classes.Bipartite = GraphClass("Bipartite", "gc_69", recognition_function = lambda x:x.is_bipartite()) graph_classes.Block = GraphClass("Block", "gc_93") -graph_classes.Chordal = GraphClass("Chordal", "gc_32") -graph_classes.Comparability = GraphClass("Comparability", "gc_72") +graph_classes.Chordal = GraphClass("Chordal", "gc_32", recognition_function = lambda x:x.is_chordal()) +graph_classes.ClawFree = GraphClass("Claw-free", "gc_62") +graph_classes.Comparability = GraphClass("Comparability", "gc_72", recognition_function = lambda x: __import__('sage').graphs.comparability.is_comparability) graph_classes.Gallai = GraphClass("Gallai", "gc_73") graph_classes.Grid = GraphClass("Grid", "gc_464") -graph_classes.Interval = GraphClass("Interval", "gc_234") -graph_classes.Line = GraphClass("Line", "gc_249") +graph_classes.Interval = GraphClass("Interval", "gc_234", recognition_function = lambda x:x.is_interval()) +graph_classes.Line = GraphClass("Line", "gc_249", recognition_function = lambda x:x.is_line_graph()) graph_classes.Modular = GraphClass("Modular", "gc_50") graph_classes.Outerplanar = GraphClass("Outerplanar", "gc_110") -graph_classes.Perfect = GraphClass("Perfect", "gc_56") -graph_classes.Planar = GraphClass("Planar", "gc_43") -graph_classes.Split = GraphClass("Split", "gc_39") -graph_classes.Tree = GraphClass("Tree", "gc_342") +graph_classes.Perfect = GraphClass("Perfect", "gc_56", recognition_function = lambda x:x.is_perfect()) +graph_classes.Planar = GraphClass("Planar", "gc_43", recognition_function = lambda x:x.is_planar()) +graph_classes.Split = GraphClass("Split", "gc_39", recognition_function = lambda x:x.is_split()) +graph_classes.Tree = GraphClass("Tree", "gc_342", recognition_function = lambda x:x.is_tree()) graph_classes.UnitDisk = GraphClass("UnitDisk", "gc_389") graph_classes.UnitInterval = GraphClass("UnitInterval", "gc_299") From c84246e912c9380936c9e122934ed94ab160453f Mon Sep 17 00:00:00 2001 From: Emmanuel Jeanvoine Date: Mon, 14 Feb 2011 08:51:59 +0000 Subject: [PATCH 004/217] #10779: Improve coverage test for structure/element.pyx --- src/sage/structure/element.pyx | 253 +++++++++++++++++++++------------ 1 file changed, 164 insertions(+), 89 deletions(-) diff --git a/src/sage/structure/element.pyx b/src/sage/structure/element.pyx index b233a1ee85e..3b312375562 100644 --- a/src/sage/structure/element.pyx +++ b/src/sage/structure/element.pyx @@ -224,11 +224,11 @@ def py_scalar_to_element(py): elif PyComplex_Check(py): return CDF(py) else: - raise TypeError, "Not a scalar" + raise TypeError("Not a scalar") def is_Element(x): """ - Return True if x is of type Element. + Return ``True`` if x is of type Element. EXAMPLES:: @@ -319,7 +319,7 @@ cdef class Element(sage_object.SageObject): AttributeError: 'LeftZeroSemigroup_with_category.element_class' object has no attribute 'blah_blah' We test that "private" attributes are not requested from the element class - of the category (trac ticket #10467):: + of the category (:trac:`10467`):: sage: C = EuclideanDomains() sage: P. = QQ[] @@ -447,8 +447,8 @@ cdef class Element(sage_object.SageObject): def _im_gens_(self, codomain, im_gens): """ - Return the image of self in codomain under the map that sends - the images of the generators of the parent of self to the + Return the image of ``self`` in codomain under the map that sends + the images of the generators of the parent of ``self`` to the tuple of elements of im_gens. """ raise NotImplementedError @@ -470,6 +470,13 @@ cdef class Element(sage_object.SageObject): def base_ring(self): """ Returns the base ring of this element's parent (if that makes sense). + + TESTS:: + + sage: QQ.base_ring() + Rational Field + sage: identity_matrix(3).base_ring() + Integer Ring """ return self._parent.base_ring() @@ -637,15 +644,15 @@ cdef class Element(sage_object.SageObject): sage: (2/3).n() 0.666666666666667 - sage: pi.n(digits=10) + sage: pi.n(digits=10) # indirect doctest 3.141592654 - sage: pi.n(prec=20) # 20 bits + sage: pi.n(prec=20) # indirect doctest 3.1416 """ - import sage.misc.functional - return sage.misc.functional.numerical_approx(self, prec=prec, digits=digits) - n=numerical_approx - N=n + from sage.misc.functional import numerical_approx + return numerical_approx(self, prec=prec, digits=digits) + n = numerical_approx + N = n def _mpmath_(self, prec=53, rounding=None): """ @@ -706,7 +713,7 @@ cdef class Element(sage_object.SageObject): cpdef _act_on_(self, x, bint self_on_left): """ - Use this method to implement self acting on x. + Use this method to implement ``self`` acting on x. Return None or raise a CoercionException if no such action is defined here. @@ -715,7 +722,7 @@ cdef class Element(sage_object.SageObject): cpdef _acted_upon_(self, x, bint self_on_left): """ - Use this method to implement self acted on by x. + Use this method to implement ``self`` acted on by x. Return None or raise a CoercionException if no such action is defined here. @@ -724,8 +731,8 @@ cdef class Element(sage_object.SageObject): def __xor__(self, right): - raise RuntimeError, "Use ** for exponentiation, not '^', which means xor\n"+\ - "in Python, and has the wrong precedence." + raise RuntimeError("Use ** for exponentiation, not '^', which means xor\n"+\ + "in Python, and has the wrong precedence.") def __pos__(self): return self @@ -751,7 +758,7 @@ cdef class Element(sage_object.SageObject): def _is_atomic(self): """ - Return True if and only if parenthesis are not required when + Return ``True`` if and only if parenthesis are not required when *printing* out any of `x - s`, `x + s`, `x^s` and `x/s`. EXAMPLES:: @@ -768,7 +775,7 @@ cdef class Element(sage_object.SageObject): def __nonzero__(self): r""" - Return True if self does not equal self.parent()(0). + Return ``True`` if ``self`` does not equal self.parent()(0). Note that this is automatically called when converting to boolean, as in the conditional of an if or while statement. @@ -793,7 +800,7 @@ cdef class Element(sage_object.SageObject): def is_zero(self): """ - Return True if self equals self.parent()(0). The default + Return ``True`` if ``self`` equals self.parent()(0). The default implementation is to fall back to 'not self.__nonzero__'. .. warning:: @@ -937,7 +944,7 @@ cdef class Element(sage_object.SageObject): cdef int _cmp_c_impl(left, Element right) except -2: ### For derived Cython code, you *MUST* ALSO COPY the __richcmp__ above ### into your class!!! For Python code just use __cmp__. - raise NotImplementedError, "BUG: sort algorithm for elements of '%s' not implemented"%right.parent() + raise NotImplementedError("BUG: sort algorithm for elements of '%s' not implemented"%right.parent()) cdef inline bint _rich_to_bool(int op, int r): if op == Py_LT: #< @@ -956,7 +963,7 @@ cdef inline bint _rich_to_bool(int op, int r): def is_ModuleElement(x): """ - Return True if x is of type ModuleElement. + Return ``True`` if x is of type ModuleElement. This is even faster than using isinstance inline. @@ -980,7 +987,7 @@ cdef class ElementWithCachedMethod(Element): The :class:`~sage.misc.cachefunc.cached_method` decorator provides a convenient way to automatically cache the result of a computation. - Since trac ticket #11115, the cached method decorator applied to a + Since :trac:`11115`, the cached method decorator applied to a method without optional arguments is faster than a hand-written cache in Python, and a cached method without any arguments (except ``self``) is actually faster than a Python method that does nothing more but @@ -1220,7 +1227,7 @@ cdef class ModuleElement(Element): return coercion_model.bin_op(left, right, add) cpdef ModuleElement _add_(left, ModuleElement right): - raise TypeError, arith_error_message(left, right, add) + raise TypeError(arith_error_message(left, right, add)) def __iadd__(ModuleElement self, right): if have_same_parent(self, right): @@ -1299,7 +1306,7 @@ cdef class ModuleElement(Element): if PyInt_CheckExact(left): return (right)._mul_long(PyInt_AS_LONG(left)) if have_same_parent(left, right): - raise TypeError, arith_error_message(left, right, mul) + raise TypeError(arith_error_message(left, right, mul)) # Always do this global coercion_model return coercion_model.bin_op(left, right, mul) @@ -1372,7 +1379,7 @@ cdef class ModuleElement(Element): def is_MonoidElement(x): """ - Return True if x is of type MonoidElement. + Return ``True`` if x is of type MonoidElement. """ return IS_INSTANCE(x, MonoidElement) @@ -1430,7 +1437,7 @@ cdef class MonoidElement(Element): Return the (integral) power of self. """ if dummy is not None: - raise RuntimeError, "__pow__ dummy argument not used" + raise RuntimeError("__pow__ dummy argument not used") return generic_power_c(self,n,None) def __nonzero__(self): @@ -1438,7 +1445,7 @@ cdef class MonoidElement(Element): def is_AdditiveGroupElement(x): """ - Return True if x is of type AdditiveGroupElement. + Return ``True`` if x is of type AdditiveGroupElement. """ return IS_INSTANCE(x, AdditiveGroupElement) @@ -1453,7 +1460,7 @@ cdef class AdditiveGroupElement(ModuleElement): return self.additive_order() def __invert__(self): - raise NotImplementedError, "multiplicative inverse not defined for additive group elements" + raise NotImplementedError("multiplicative inverse not defined for additive group elements") cpdef ModuleElement _rmul_(self, RingElement left): return self._lmul_(left) @@ -1470,7 +1477,7 @@ cdef class AdditiveGroupElement(ModuleElement): def is_MultiplicativeGroupElement(x): """ - Return True if x is of type MultiplicativeGroupElement. + Return ``True`` if x is of type MultiplicativeGroupElement. """ return IS_INSTANCE(x, MultiplicativeGroupElement) @@ -1485,7 +1492,7 @@ cdef class MultiplicativeGroupElement(MonoidElement): return self.multiplicative_order() def _add_(self, x): - raise ArithmeticError, "addition not defined in a multiplicative group" + raise ArithmeticError("addition not defined in a multiplicative group") def __div__(left, right): if have_same_parent(left, right): @@ -1508,7 +1515,7 @@ cdef class MultiplicativeGroupElement(MonoidElement): def is_RingElement(x): """ - Return True if x is of type RingElement. + Return ``True`` if x is of type RingElement. """ return IS_INSTANCE(x, RingElement) @@ -1705,7 +1712,7 @@ cdef class RingElement(ModuleElement): Cython classes should override this function to implement multiplication. See extensive documentation at the top of element.pyx. """ - raise TypeError, arith_error_message(self, right, mul) + raise TypeError(arith_error_message(self, right, mul)) def __imul__(left, right): if have_same_parent(left, right): @@ -1766,7 +1773,7 @@ cdef class RingElement(ModuleElement): sage: 2r^(1/2) sqrt(2) - Exponent overflow should throw an OverflowError (trac #2956):: + Exponent overflow should throw an OverflowError (:trac:`2956`):: sage: K. = AA[] sage: x^(2^64 + 12345) @@ -1774,7 +1781,7 @@ cdef class RingElement(ModuleElement): ... OverflowError: Exponent overflow (2147483648). - Another example from trac #2956; this should overflow on x32 + Another example from :trac:`2956`; this should overflow on x32 and succeed on x64:: sage: K. = ZZ[] @@ -1786,7 +1793,7 @@ cdef class RingElement(ModuleElement): """ if dummy is not None: - raise RuntimeError, "__pow__ dummy argument not used" + raise RuntimeError("__pow__ dummy argument not used") return generic_power_c(self,n,None) ################################## @@ -1821,9 +1828,9 @@ cdef class RingElement(ModuleElement): return self._parent.fraction_field()(self, right) except AttributeError: if not right: - raise ZeroDivisionError, "Cannot divide by zero" + raise ZeroDivisionError("Cannot divide by zero") else: - raise TypeError, arith_error_message(self, right, div) + raise TypeError(arith_error_message(self, right, div)) def __idiv__(self, right): """ @@ -1874,11 +1881,11 @@ cdef class RingElement(ModuleElement): def multiplicative_order(self): r""" - Return the multiplicative order of self, if self is a unit, or raise + Return the multiplicative order of self, if ``self`` is a unit, or raise ``ArithmeticError`` otherwise. """ if not self.is_unit(): - raise ArithmeticError, "self (=%s) must be a unit to have a multiplicative order." + raise ArithmeticError("self (=%s) must be a unit to have a multiplicative order.") raise NotImplementedError def is_unit(self): @@ -1888,8 +1895,22 @@ cdef class RingElement(ModuleElement): def is_nilpotent(self): """ - Return True if self is nilpotent, i.e., some power of self + Return ``True`` if ``self`` is nilpotent, i.e., some power of self is 0. + + TESTS:: + + sage: a=QQ(2) + sage: a.is_nilpotent() + False + sage: a=QQ(0) + sage: a.is_nilpotent() + True + sage: m=matrix(RR,3,[[3,2,3],[9,0,3],[-9,0,-3]]) + sage: m.is_nilpotent() + Traceback (most recent call last): + ... + NotImplementedError """ if self.is_unit(): return False @@ -1921,7 +1942,19 @@ cdef class RingElement(ModuleElement): def is_CommutativeRingElement(x): """ - Return True if x is of type CommutativeRingElement. + Return ``True`` if x is of type CommutativeRingElement. + + TESTS:: + + sage: is_CommutativeRingElement(oo) + doctest:...: DeprecationWarning: + Using is_CommutativeRingElement from the top level is deprecated since it was designed to be used by developers rather than end users. + It most likely does not do what you would expect it to do. If you really need to use it, import it from the module that it is defined in. + See http://trac.sagemath.org/10107 for details. + False + + sage: is_CommutativeRingElement(1) + True """ return IS_INSTANCE(x, CommutativeRingElement) @@ -1933,14 +1966,14 @@ cdef class CommutativeRingElement(RingElement): def inverse_mod(self, I): r""" - Return an inverse of self modulo the ideal `I`, if defined, - i.e., if `I` and self together generate the unit ideal. + Return an inverse of ``self`` modulo the ideal `I`, if defined, + i.e., if `I` and ``self`` together generate the unit ideal. """ raise NotImplementedError def divides(self, x): """ - Return True if self divides x. + Return ``True`` if ``self`` divides x. EXAMPLES:: @@ -1959,7 +1992,7 @@ cdef class CommutativeRingElement(RingElement): sage: (x^2+2).divides(x) False - Ticket \#5347 has been fixed:: + :trac:`5347` has been fixed:: sage: K = GF(7) sage: K(3).divides(1) @@ -1987,7 +2020,7 @@ cdef class CommutativeRingElement(RingElement): If x has different parent than `self`, they are first coerced to a common parent if possible. If this coercion fails, it returns a - TypeError. This fixes \#5759 + TypeError. This fixes :trac:`5759` :: sage: Zmod(2)(0).divides(Zmod(2)(0)) @@ -2042,7 +2075,7 @@ cdef class CommutativeRingElement(RingElement): def mod(self, I): r""" - Return a representative for self modulo the ideal I (or the ideal + Return a representative for ``self`` modulo the ideal I (or the ideal generated by the elements of I if I is not an ideal.) EXAMPLE: Integers @@ -2120,7 +2153,7 @@ cdef class CommutativeRingElement(RingElement): def is_square(self, root=False): """ Returns whether or not ring element is a square. If the optional - argument root is True, then also returns the square root (or None, + argument root is ``True``, then also returns the square root (or None, if the it is not a square). INPUT: @@ -2169,17 +2202,17 @@ cdef class CommutativeRingElement(RingElement): INPUT: - - ``extend`` - Whether to make a ring extension containing a square root if self is not a square (default: True) + - ``extend`` - Whether to make a ring extension containing a square root if ``self`` is not a square (default: ``True``) - ``all`` - Whether to return a list of all square roots or just a square root (default: False) - - ``name`` - Required when extend=True and self is not a square. This will be the name of the generator extension. + - ``name`` - Required when extend=``True`` and ``self`` is not a square. This will be the name of the generator extension. OUTPUT: - - if all=False it returns a square root. (throws an error if extend=False and self is not a square) + - if all=False it returns a square root. (throws an error if extend=False and ``self`` is not a square) - - if all=True it returns a list of all the square roots (could be empty if extend=False and self is not a square) + - if all=``True`` it returns a list of all the square roots (could be empty if extend=False and ``self`` is not a square) ALGORITHM: @@ -2274,10 +2307,10 @@ cdef class CommutativeRingElement(RingElement): #all square roots of a non-square should be an empty list if all: return [] - raise ValueError, 'trying to take square root of non-square %s with extend = False' % self + raise ValueError('trying to take square root of non-square %s with extend = False' % self) if name == None: - raise TypeError ("Polynomial is not a square. You must specify the name of the square root when using the default extend = True") + raise TypeError("Polynomial is not a square. You must specify the name of the square root when using the default extend = True") from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing PY = PolynomialRing(P,'y') y = PY.gen() @@ -2474,10 +2507,10 @@ cdef class Vector(ModuleElement): return coercion_model.bin_op(left, right, mul) cpdef Element _dot_product_(Vector left, Vector right): - raise TypeError, arith_error_message(left, right, mul) + raise TypeError(arith_error_message(left, right, mul)) cpdef Vector _pairwise_product_(Vector left, Vector right): - raise TypeError, "unsupported operation for '%s' and '%s'"%(parent_c(left), parent_c(right)) + raise TypeError("unsupported operation for '%s' and '%s'"%(parent_c(left), parent_c(right))) def __div__(self, right): if PY_IS_NUMERIC(right): @@ -2491,10 +2524,10 @@ cdef class Vector(ModuleElement): return W.coordinates(self)[0] / W.coordinates(right)[0] except ArithmeticError: if right.is_zero(): - raise ZeroDivisionError, "division by zero vector" + raise ZeroDivisionError("division by zero vector") else: - raise ArithmeticError, "vector is not in free module" - raise TypeError, arith_error_message(self, right, div) + raise ArithmeticError("vector is not in free module") + raise TypeError(arith_error_message(self, right, div)) def _magma_init_(self, magma): """ @@ -2766,7 +2799,7 @@ def is_Matrix(x): def is_IntegralDomainElement(x): """ - Return True if x is of type IntegralDomainElement. + Return ``True`` if x is of type IntegralDomainElement. """ return IS_INSTANCE(x, IntegralDomainElement) @@ -2777,7 +2810,7 @@ cdef class IntegralDomainElement(CommutativeRingElement): def is_DedekindDomainElement(x): """ - Return True if x is of type DedekindDomainElement. + Return ``True`` if x is of type DedekindDomainElement. """ return IS_INSTANCE(x, DedekindDomainElement) @@ -2786,14 +2819,14 @@ cdef class DedekindDomainElement(IntegralDomainElement): def is_PrincipalIdealDomainElement(x): """ - Return True if x is of type PrincipalIdealDomainElement. + Return ``True`` if x is of type PrincipalIdealDomainElement. """ return IS_INSTANCE(x, PrincipalIdealDomainElement) cdef class PrincipalIdealDomainElement(DedekindDomainElement): def lcm(self, right): """ - Returns the least common multiple of self and right. + Returns the least common multiple of ``self`` and right. """ if not PY_TYPE_CHECK(right, Element) or not ((right)._parent is self._parent): return coercion_model.bin_op(self, right, lcm) @@ -2801,7 +2834,7 @@ cdef class PrincipalIdealDomainElement(DedekindDomainElement): def gcd(self, right): """ - Returns the gcd of self and right, or 0 if both are 0. + Returns the gcd of ``self`` and right, or 0 if both are 0. """ if not PY_TYPE_CHECK(right, Element) or not ((right)._parent is self._parent): return coercion_model.bin_op(self, right, gcd) @@ -2809,10 +2842,10 @@ cdef class PrincipalIdealDomainElement(DedekindDomainElement): def xgcd(self, right): r""" - Return the extended gcd of self and other, i.e., elements `r, s, t` such that + Return the extended gcd of ``self`` and other, i.e., elements `r, s, t` such that .. math:: - r = s \cdot self + t \cdot other. + r = s \cdot ``self`` + t \cdot other. .. note:: @@ -2832,7 +2865,7 @@ PY_SET_TP_NEW(EuclideanDomainElement, Element) def is_EuclideanDomainElement(x): """ - Return True if x is of type EuclideanDomainElement. + Return ``True`` if x is of type EuclideanDomainElement. """ return IS_INSTANCE(x, EuclideanDomainElement) @@ -2843,7 +2876,7 @@ cdef class EuclideanDomainElement(PrincipalIdealDomainElement): def _gcd(self, other): """ - Return the greatest common divisor of self and other. + Return the greatest common divisor of ``self`` and other. Algorithm 3.2.1 in Cohen, GTM 138. """ @@ -2863,7 +2896,7 @@ cdef class EuclideanDomainElement(PrincipalIdealDomainElement): def __divmod__(self, other): """ - Return the quotient and remainder of self divided by other. + Return the quotient and remainder of ``self`` divided by other. EXAMPLES:: @@ -2883,14 +2916,14 @@ cdef class EuclideanDomainElement(PrincipalIdealDomainElement): def __floordiv__(self,right): """ - Quotient of division of self by other. This is denoted //. + Quotient of division of ``self`` by other. This is denoted //. """ Q, _ = self.quo_rem(right) return Q def __mod__(self, other): """ - Remainder of division of self by other. + Remainder of division of ``self`` by other. EXAMPLES:: @@ -2905,7 +2938,7 @@ cdef class EuclideanDomainElement(PrincipalIdealDomainElement): def is_FieldElement(x): """ - Return True if x is of type FieldElement. + Return ``True`` if x is of type FieldElement. """ return IS_INSTANCE(x, FieldElement) @@ -2916,7 +2949,7 @@ cdef class FieldElement(CommutativeRingElement): def is_unit(self): """ - Return True if self is a unit in its parent ring. + Return ``True`` if ``self`` is a unit in its parent ring. EXAMPLES:: @@ -2941,7 +2974,7 @@ cdef class FieldElement(CommutativeRingElement): def _gcd(self, FieldElement other): """ - Return the greatest common divisor of self and other. + Return the greatest common divisor of ``self`` and other. """ if self.is_zero() and other.is_zero(): return self @@ -2950,7 +2983,7 @@ cdef class FieldElement(CommutativeRingElement): def _lcm(self, FieldElement other): """ - Return the least common multiple of self and other. + Return the least common multiple of ``self`` and other. """ if self.is_zero() and other.is_zero(): return self @@ -2990,7 +3023,7 @@ cdef class FieldElement(CommutativeRingElement): def divides(self, FieldElement other): r""" - Check whether self divides other, for field elements. + Check whether ``self`` divides other, for field elements. Since this is a field, all values divide all other values, except that zero does not divide any non-zero values. @@ -3013,7 +3046,20 @@ cdef class FieldElement(CommutativeRingElement): def is_AlgebraElement(x): """ - Return True if x is of type AlgebraElement. + Return ``True`` if x is of type AlgebraElement. + + TESTS:: + + sage: R. = FreeAlgebra(QQ,2) + sage: is_AlgebraElement(x*y) + doctest:...: DeprecationWarning: + Using is_AlgebraElement from the top level is deprecated since it was designed to be used by developers rather than end users. + It most likely does not do what you would expect it to do. If you really need to use it, import it from the module that it is defined in. + See http://trac.sagemath.org/10107 for details. + True + + sage: is_AlgebraElement(1) + False """ return IS_INSTANCE(x, AlgebraElement) @@ -3022,7 +3068,7 @@ cdef class AlgebraElement(RingElement): def is_CommutativeAlgebraElement(x): """ - Return True if x is of type CommutativeAlgebraElement. + Return ``True`` if x is of type CommutativeAlgebraElement. """ return IS_INSTANCE(x, CommutativeAlgebraElement) @@ -3031,7 +3077,19 @@ cdef class CommutativeAlgebraElement(CommutativeRingElement): def is_InfinityElement(x): """ - Return True if x is of type InfinityElement. + Return ``True`` if x is of type InfinityElement. + + TESTS:: + + sage: is_InfinityElement(1) + doctest:...: DeprecationWarning: + Using is_InfinityElement from the top level is deprecated since it was designed to be used by developers rather than end users. + It most likely does not do what you would expect it to do. If you really need to use it, import it from the module that it is defined in. + See http://trac.sagemath.org/10107 for details. + False + + sage: is_InfinityElement(oo) + True """ return IS_INSTANCE(x, InfinityElement) @@ -3104,12 +3162,12 @@ cdef class CoercionModel: cpdef canonical_coercion(self, x, y): if parent_c(x) is parent_c(y): return x,y - raise TypeError, "no common canonical parent for objects with parents: '%s' and '%s'"%(parent_c(x), parent_c(y)) + raise TypeError("no common canonical parent for objects with parents: '%s' and '%s'"%(parent_c(x), parent_c(y))) cpdef bin_op(self, x, y, op): if parent_c(x) is parent_c(y): return op(x,y) - raise TypeError, arith_error_message(x,y,op) + raise TypeError(arith_error_message(x,y,op)) import coerce cdef CoercionModel coercion_model = coerce.CoercionModel_cache_maps() @@ -3331,19 +3389,36 @@ coerce_binop = NamedBinopMethod ############################################################################### -def lcm(x,y): +def lcm(x, y): + """ + TESTS:: + + sage: lcm(3,-4) + 12 + """ from sage.rings.arith import lcm - return lcm(x,y) + return lcm(x, y) -def gcd(x,y): - from sage.rings.arith import gcd - return gcd(x,y) +def gcd(x, y): + """ + TESTS:: -def xgcd(x,y): - from sage.rings.arith import xgcd - return xgcd(x,y) + sage: gcd(12,15) + 3 + """ + from sage.rings.arith import gcd + return gcd(x, y) +def xgcd(x, y): + """ + TESTS:: + sage: x = polygen(QQ) + sage: xgcd(x^3 - 1, x^2 - 1) + (x - 1, 1, -x) + """ + from sage.rings.arith import xgcd + return xgcd(x, y) ###################### @@ -3395,7 +3470,7 @@ cdef generic_power_c(a, nn, one): from sage.rings.integer import Integer n = int(Integer(nn)) except TypeError: - raise NotImplementedError, "non-integral exponents not supported" + raise NotImplementedError("non-integral exponents not supported") if not n: if one is None: From 93cefff357a7942b4c7ea539f1d97c9b0e32acb7 Mon Sep 17 00:00:00 2001 From: Frederic Chapoton Date: Thu, 6 Sep 2012 07:59:56 +0000 Subject: [PATCH 005/217] trac #12916 : implements the Dedekind-MacNeil completion of posets --- src/sage/combinat/posets/posets.py | 99 ++++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) diff --git a/src/sage/combinat/posets/posets.py b/src/sage/combinat/posets/posets.py index 590cea342d3..ed0dc176711 100644 --- a/src/sage/combinat/posets/posets.py +++ b/src/sage/combinat/posets/posets.py @@ -130,6 +130,7 @@ from sage.misc.superseded import deprecated_function_alias from sage.categories.category import Category from sage.categories.sets_cat import Sets +from sage.sets.set import Set from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets from sage.categories.posets import Posets from sage.categories.finite_posets import FinitePosets @@ -4275,6 +4276,104 @@ def p_partition_enumerator(self, tup, R, check=False): res += QR.Fundamental()(Composition(from_subset=(descents, n))) return res + def set_of_upper_bounds(self, A): + r""" + Return the set of upper bounds of the subset `A` of ``self`` + + An element `x` is called an upper bound of `A` if `a\leq x` + for all `a \in A` + + INPUT: + + - `A` -- a subset of the poset ``self`` + + OUTPUT: + + - a subset of the poset ``self`` + + EXAMPLES:: + + sage: P = posets.PentagonPoset() + sage: P.set_of_upper_bounds([P(0), P(1)]) + {1, 4} + sage: P.set_of_upper_bounds([u for u in P]) + {4} + sage: P.set_of_upper_bounds([P(0)]) + {1, 0, 3, 2, 4} + """ + return Set([x for x in self if all([x >= a for a in A])]) + + def set_of_lower_bounds(self, A): + r""" + Return the set of lower bounds of the subset `A` of ``self`` + + An element `x` is called an lower bound of `A` if `x\leq a` + for all `a \in A` + + INPUT: + + - `A` -- a subset of the poset ``self`` + + OUTPUT: + + - a subset of the poset ``self`` + + EXAMPLES:: + + sage: P = posets.PentagonPoset() + sage: P.set_of_lower_bounds([P(1), P(4)]) + {1, 0} + sage: P.set_of_lower_bounds([u for u in P]) + {0} + sage: P.set_of_lower_bounds([P(4)]) + {1, 0, 3, 2, 4} + """ + return Set([x for x in self if all([x <= a for a in A])]) + + def cuts(self): + """ + Return the set of cuts of the poset ``self`` + + A cut is a subset `A` of ``self`` such that the set of lower + bounds of the set of upper bounds of `A` is exactly `A`. + + EXAMPLES:: + + sage: P = posets.AntichainPoset(3) + sage: P.cuts() + [{}, {1}, {0}, {2}, {1, 0, 2}] + """ + return [A for A in Set(self).subsets() + if self.set_of_lower_bounds(self.set_of_upper_bounds([self(a) for a in A])) == Set([self(a) for a in A])] + + def completion_by_cuts(self): + """ + Return the completion by cuts of ``self`` + + This is also called the Dedekind-MacNeille completion. + + See the :wikipedia:`Wikipedia page `. + + The algorithm is naive. + + OUTPUT: + + - a finite poset + + EXAMPLES:: + + sage: P = posets.PentagonPoset() + sage: P.completion_by_cuts().is_isomorphic(P) + True + sage: P = posets.AntichainPoset(3) + sage: Q = P.completion_by_cuts(); Q + Finite poset containing 5 elements + sage: Q.is_isomorphic(posets.DiamondPoset(5)) + True + """ + from sage.misc.misc import attrcall + return Poset((self.cuts(), attrcall("issubset"))) + FinitePoset._dual_class = FinitePoset ##### Posets ##### From b3eee8a1f30a328eeb14080f07dee9ae26ab0ec5 Mon Sep 17 00:00:00 2001 From: Julian Rueth Date: Sat, 20 Oct 2012 20:49:57 +0000 Subject: [PATCH 006/217] Trac #13442: rings can provide _gcd_univariate_polynomial for polynomial factorization --- src/sage/categories/fields.py | 27 +++++++++ .../rings/polynomial/polynomial_element.pyx | 57 ++++++++++++++++++- 2 files changed, 83 insertions(+), 1 deletion(-) diff --git a/src/sage/categories/fields.py b/src/sage/categories/fields.py index 52398f5089a..9b27ab71cad 100644 --- a/src/sage/categories/fields.py +++ b/src/sage/categories/fields.py @@ -194,6 +194,33 @@ def is_integrally_closed(self): """ return True + def _gcd_univariate_polynomial(self, f, g): + """ + Return the greatest common divisor of ``f`` and ``g``, as a + monic polynomial. + + INPUT: + + - ``f``, ``g`` -- two polynomials defined over ``self`` + + .. NOTE:: + + This is a helper method for + :meth:`sage.rings.polynomial.polynomial_element.Polynomial.gcd`. + + EXAMPLES:: + + sage: R. = QQbar[] + sage: QQbar._gcd_univariate_polynomial(2*x,2*x^2) + x + + """ + ret = EuclideanDomains().ElementMethods().gcd(f,g) + c = ret.leading_coefficient() + if c.is_unit(): + return (1/c)*ret + return ret + def _test_characteristic_fields(self, **options): """ Run generic tests on the method :meth:`.characteristic`. diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index 6e22c1f090d..83c802be183 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -3395,6 +3395,61 @@ cdef class Polynomial(CommutativeAlgebraElement): pari.set_real_precision(n) # restore precision return Factorization(F, unit) + @coerce_binop + def gcd(self, other): + """ + Compute a greatest common divisor of ``self`` and ``other``. + + INPUT: + + - ``other`` -- a polynomial in the same ring as ``self`` + + OUTPUT: + + A greatest common divisor of ``self`` and ``other`` as a polynomial + in the same ring as ``self``. Over a field, the return value will + be a monic polynomial. + + .. NOTE:: + + The actual algorithm for computing greatest common divisors depends + on the base ring underlying the polynomial ring. If the base ring + defines a method ``_gcd_univariate_polynomial``, then this method + will be called (see examples below). + + EXAMPLES:: + + sage: R. = QQ[] + sage: (2*x^2).gcd(2*x) + x + sage: R.zero().gcd(0) + 0 + sage: (2*x).gcd(0) + x + + One can easily add gcd functionality to new rings by providing a + method ``_gcd_univariate_polynomial``:: + + sage: R. = QQ[] + sage: S. = R[] + sage: h1 = y*x + sage: h2 = y^2*x^2 + sage: h1.gcd(h2) + Traceback (most recent call last): + ... + NotImplementedError: Univariate Polynomial Ring in x over Rational Field does not provide a gcd implementation for univariate polynomials + sage: T. = QQ[] + sage: R._gcd_univariate_polynomial = lambda f,g: S(T(f).gcd(g)) + sage: h1.gcd(h2) + x*y + sage: del R._gcd_univariate_polynomial + + """ + if hasattr(self.base_ring(), '_gcd_univariate_polynomial'): + return self.base_ring()._gcd_univariate_polynomial(self, other) + else: + raise NotImplementedError("%s does not provide a gcd implementation for univariate polynomials"%self.base_ring()) + @coerce_binop def lcm(self, other): """ @@ -5999,7 +6054,7 @@ cdef class Polynomial(CommutativeAlgebraElement): sage: (2*x*y).is_squarefree() # R does not provide a gcd implementation Traceback (most recent call last): ... - AttributeError: 'sage.rings.polynomial.polynomial_element.Polynomial_generic_dense' object has no attribute 'gcd' + NotImplementedError: Univariate Polynomial Ring in y over Rational Field does not provide a gcd implementation for univariate polynomials sage: (2*x*y^2).is_squarefree() False From a3bf5a1772a929434ab2ee22370bd9a29fb32b4c Mon Sep 17 00:00:00 2001 From: Jean-Pierre Flori Date: Mon, 3 Mar 2014 02:33:14 -0800 Subject: [PATCH 007/217] USe GMP or MPIR as default MP library. --- build/deps | 62 ++++++++++++++-------------- build/install | 80 ++++++++++++++++++++++++++++++++---- build/pkgs/gmp/spkg-install | 11 ++--- build/pkgs/mpir/spkg-install | 19 ++++++--- 4 files changed, 123 insertions(+), 49 deletions(-) diff --git a/build/deps b/build/deps index 3809e9555d2..8cb688922bf 100644 --- a/build/deps +++ b/build/deps @@ -72,7 +72,6 @@ all-sage: \ $(INST)/$(MPC) \ $(INST)/$(MPFI) \ $(INST)/$(MPFR) \ - $(INST)/$(MPIR) \ $(INST)/$(MPMATH) \ $(INST)/$(NETWORKX) \ $(INST)/$(NTL) \ @@ -109,6 +108,7 @@ all-sage: \ $(INST)/$(NCURSES) \ $(INST)/$(ZLIB) \ $(INST)/$(ZNPOLY) \ + $(INST)/$(SAGE_MP_LIBRARY) \ $(INST)/sage \ $(INST)/csage \ $(EXTCODE) \ @@ -126,7 +126,7 @@ toolchain: $(TOOLCHAIN) # See #14168 and #14232. toolchain-deps: $(MAKE) $(INST)/$(ZLIB) - $(MAKE) $(INST)/$(MPIR) + $(MAKE) $(INST)/$(SAGE_MP_LIBRARY) $(MAKE) $(INST)/$(MPFR) $(MAKE) $(INST)/$(MPC) $(MAKE) $(INST)/$(PPL) @@ -167,6 +167,9 @@ $(INST)/$(PATCH): $(INST)/$(BZIP2) # Building normal packages ############################################################################### +$(INST)/$(SAGE_MP_LIBRARY): $(INST)/$(ICONV) + +$(PIPE) "$(SAGE_SPKG) $(SAGE_MP_LIBRARY) 2>&1" "tee -a $(SAGE_LOGS)/$(SAGE_MP_LIBRARY).log" + $(INST)/$(ATLAS): $(INST)/$(PYTHON) +$(PIPE) "$(SAGE_SPKG) $(ATLAS) 2>&1" "tee -a $(SAGE_LOGS)/$(ATLAS).log" @@ -202,29 +205,26 @@ $(INST)/$(CONWAY): $(SAGERUNTIME) $(INST)/$(GRAPHS): +$(PIPE) "$(SAGE_SPKG) $(GRAPHS) 2>&1" "tee -a $(SAGE_LOGS)/$(GRAPHS).log" -$(INST)/$(GLPK): $(INST)/$(MPIR) $(INST)/$(ZLIB) +$(INST)/$(GLPK): $(INST)/$(SAGE_MP_LIBRARY) $(INST)/$(ZLIB) +$(PIPE) "$(SAGE_SPKG) $(GLPK) 2>&1" "tee -a $(SAGE_LOGS)/$(GLPK).log" $(INST)/$(PYTHON): $(INST)/$(ZLIB) $(INST)/$(BZIP2) \ $(INST)/$(READLINE) $(INST)/$(SQLITE) $(INST)/$(LIBPNG) +$(PIPE) "$(SAGE_SPKG) $(PYTHON) 2>&1" "tee -a $(SAGE_LOGS)/$(PYTHON).log" -$(INST)/$(MPIR): $(INST)/$(ICONV) - +$(PIPE) "$(SAGE_SPKG) $(MPIR) 2>&1" "tee -a $(SAGE_LOGS)/$(MPIR).log" - $(INST)/$(GSL): $(INST)/$(ATLAS) +$(PIPE) "$(SAGE_SPKG) $(GSL) 2>&1" "tee -a $(SAGE_LOGS)/$(GSL).log" $(INST)/$(GF2X): +$(PIPE) "$(SAGE_SPKG) $(GF2X) 2>&1" "tee -a $(SAGE_LOGS)/$(GF2X).log" -$(INST)/$(NTL): $(INST)/$(MPIR) $(INST)/$(GF2X) +$(INST)/$(NTL): $(INST)/$(SAGE_MP_LIBRARY) $(INST)/$(GF2X) +$(PIPE) "$(SAGE_SPKG) $(NTL) 2>&1" "tee -a $(SAGE_LOGS)/$(NTL).log" -$(INST)/$(FPLLL): $(INST)/$(MPIR) $(INST)/$(MPFR) +$(INST)/$(FPLLL): $(INST)/$(SAGE_MP_LIBRARY) $(INST)/$(MPFR) +$(PIPE) "$(SAGE_SPKG) $(FPLLL) 2>&1" "tee -a $(SAGE_LOGS)/$(FPLLL).log" -$(INST)/$(PARI): $(INST)/$(READLINE) $(INST)/$(MPIR) \ +$(INST)/$(PARI): $(INST)/$(READLINE) $(INST)/$(SAGE_MP_LIBRARY) \ $(INST)/$(PARI_GALDATA) $(INST)/$(PARI_SEADATA_SMALL) +$(PIPE) "$(SAGE_SPKG) $(PARI) 2>&1" "tee -a $(SAGE_LOGS)/$(PARI).log" @@ -242,34 +242,34 @@ $(INST)/$(POLYBORI): $(INST)/$(PYTHON) $(INST)/$(IPYTHON) \ $(INST)/$(POLYTOPES_DB): +$(PIPE) "$(SAGE_SPKG) $(POLYTOPES_DB) 2>&1" "tee -a $(SAGE_LOGS)/$(POLYTOPES_DB).log" -$(INST)/$(PPL): $(INST)/$(MPIR) $(INST)/$(GLPK) +$(INST)/$(PPL): $(INST)/$(SAGE_MP_LIBRARY) $(INST)/$(GLPK) +$(PIPE) "$(SAGE_SPKG) $(PPL) 2>&1" "tee -a $(SAGE_LOGS)/$(PPL).log" -$(INST)/$(MPC): $(INST)/$(MPIR) $(INST)/$(MPFR) +$(INST)/$(MPC): $(INST)/$(SAGE_MP_LIBRARY) $(INST)/$(MPFR) +$(PIPE) "$(SAGE_SPKG) $(MPC) 2>&1" "tee -a $(SAGE_LOGS)/$(MPC).log" -$(INST)/$(MPFR): $(INST)/$(MPIR) +$(INST)/$(MPFR): $(INST)/$(SAGE_MP_LIBRARY) +$(PIPE) "$(SAGE_SPKG) $(MPFR) 2>&1" "tee -a $(SAGE_LOGS)/$(MPFR).log" -$(INST)/$(MPFI): $(INST)/$(MPIR) $(INST)/$(MPFR) +$(INST)/$(MPFI): $(INST)/$(SAGE_MP_LIBRARY) $(INST)/$(MPFR) +$(PIPE) "$(SAGE_SPKG) $(MPFI) 2>&1" "tee -a $(SAGE_LOGS)/$(MPFI).log" -$(INST)/$(GIVARO): $(INST)/$(MPIR) +$(INST)/$(GIVARO): $(INST)/$(SAGE_MP_LIBRARY) +$(PIPE) "$(SAGE_SPKG) $(GIVARO) 2>&1" "tee -a $(SAGE_LOGS)/$(GIVARO).log" $(INST)/$(GIT): $(INST)/$(ZLIB) $(INST)/$(PYTHON) +$(PIPE) "$(SAGE_SPKG) $(GIT) 2>&1" "tee -a $(SAGE_LOGS)/$(GIT).log" -$(INST)/$(FFLASFFPACK): $(INST)/$(MPIR) $(INST)/$(GIVARO) \ +$(INST)/$(FFLASFFPACK): $(INST)/$(SAGE_MP_LIBRARY) $(INST)/$(GIVARO) \ $(INST)/$(GSL) $(INST)/$(ATLAS) +$(PIPE) "$(SAGE_SPKG) $(FFLASFFPACK) 2>&1" "tee -a $(SAGE_LOGS)/$(FFLASFFPACK).log" -$(INST)/$(LINBOX): $(INST)/$(MPIR) $(INST)/$(NTL) $(INST)/$(GIVARO) \ +$(INST)/$(LINBOX): $(INST)/$(SAGE_MP_LIBRARY) $(INST)/$(NTL) $(INST)/$(GIVARO) \ $(INST)/$(MPFR) $(INST)/$(FPLLL) $(INST)/$(IML) \ $(INST)/$(M4RI) $(INST)/$(M4RIE) $(INST)/$(FFLASFFPACK) +$(PIPE) "$(SAGE_SPKG) $(LINBOX) 2>&1" "tee -a $(SAGE_LOGS)/$(LINBOX).log" -$(INST)/$(IML): $(INST)/$(MPIR) $(INST)/$(GSL) $(INST)/$(ATLAS) +$(INST)/$(IML): $(INST)/$(SAGE_MP_LIBRARY) $(INST)/$(GSL) $(INST)/$(ATLAS) +$(PIPE) "$(SAGE_SPKG) $(IML) 2>&1" "tee -a $(SAGE_LOGS)/$(IML).log" $(INST)/$(GENUS2REDUCTION): $(INST)/$(PARI) @@ -293,7 +293,7 @@ $(INST)/$(SYMPOW): $(INST)/$(SYMMETRICA): +$(PIPE) "$(SAGE_SPKG) $(SYMMETRICA) 2>&1" "tee -a $(SAGE_LOGS)/$(SYMMETRICA).log" -$(INST)/$(GAP): $(INST)/$(NCURSES) $(INST)/$(READLINE) $(INST)/$(MPIR) +$(INST)/$(GAP): $(INST)/$(NCURSES) $(INST)/$(READLINE) $(INST)/$(SAGE_MP_LIBRARY) +$(PIPE) "$(SAGE_SPKG) $(GAP) 2>&1" "tee -a $(SAGE_LOGS)/$(GAP).log" $(INST)/$(LIBGAP): $(INST)/$(GAP) @@ -330,7 +330,7 @@ $(INST)/$(SAGETEX): $(INST)/$(PYTHON) \ $(INST)/$(SETUPTOOLS): $(INST)/$(PYTHON) +$(PIPE) "$(SAGE_SPKG) $(SETUPTOOLS) 2>&1" "tee -a $(SAGE_LOGS)/$(SETUPTOOLS).log" -$(INST)/$(SINGULAR): $(INST)/$(MPIR) $(INST)/$(NTL) \ +$(INST)/$(SINGULAR): $(INST)/$(SAGE_MP_LIBRARY) $(INST)/$(NTL) \ $(INST)/$(READLINE) $(INST)/$(MPFR) +$(PIPE) "$(SAGE_SPKG) $(SINGULAR) 2>&1" "tee -a $(SAGE_LOGS)/$(SINGULAR).log" @@ -360,22 +360,22 @@ $(INST)/$(MATPLOTLIB): $(INST)/$(PYTHON) $(INST)/$(NUMPY) \ $(INST)/$(GDMODULE) +$(PIPE) "$(SAGE_SPKG) $(MATPLOTLIB) 2>&1" "tee -a $(SAGE_LOGS)/$(MATPLOTLIB).log" -$(INST)/$(CDDLIB): $(INST)/$(MPIR) +$(INST)/$(CDDLIB): $(INST)/$(SAGE_MP_LIBRARY) +$(PIPE) "$(SAGE_SPKG) $(CDDLIB) 2>&1" "tee -a $(SAGE_LOGS)/$(CDDLIB).log" -$(INST)/$(GFAN): $(INST)/$(MPIR) $(INST)/$(CDDLIB) +$(INST)/$(GFAN): $(INST)/$(SAGE_MP_LIBRARY) $(INST)/$(CDDLIB) +$(PIPE) "$(SAGE_SPKG) $(GFAN) 2>&1" "tee -a $(SAGE_LOGS)/$(GFAN).log" $(INST)/$(TACHYON): $(INST)/$(LIBPNG) +$(PIPE) "$(SAGE_SPKG) $(TACHYON) 2>&1" "tee -a $(SAGE_LOGS)/$(TACHYON).log" -$(INST)/$(ECM): $(INST)/$(MPIR) +$(INST)/$(ECM): $(INST)/$(SAGE_MP_LIBRARY) +$(PIPE) "$(SAGE_SPKG) $(ECM) 2>&1" "tee -a $(SAGE_LOGS)/$(ECM).log" -$(INST)/$(RATPOINTS): $(INST)/$(MPIR) +$(INST)/$(RATPOINTS): $(INST)/$(SAGE_MP_LIBRARY) +$(PIPE) "$(SAGE_SPKG) $(RATPOINTS) 2>&1" "tee -a $(SAGE_LOGS)/$(RATPOINTS).log" -$(INST)/$(ECL): $(INST)/$(MPIR) $(INST)/$(READLINE) $(INST)/$(BOEHM_GC) +$(INST)/$(ECL): $(INST)/$(SAGE_MP_LIBRARY) $(INST)/$(READLINE) $(INST)/$(BOEHM_GC) +$(PIPE) "$(SAGE_SPKG) $(ECL) 2>&1" "tee -a $(SAGE_LOGS)/$(ECL).log" $(INST)/$(MAXIMA): $(INST)/$(ECL) @@ -393,10 +393,10 @@ $(INST)/$(SYMPY): $(INST)/$(PYTHON) $(INST)/$(CYTHON): $(INST)/$(PYTHON) +$(PIPE) "$(SAGE_SPKG) $(CYTHON) 2>&1" "tee -a $(SAGE_LOGS)/$(CYTHON).log" -$(INST)/$(FLINTQS): $(INST)/$(MPIR) +$(INST)/$(FLINTQS): $(INST)/$(SAGE_MP_LIBRARY) +$(PIPE) "$(SAGE_SPKG) $(FLINTQS) 2>&1" "tee -a $(SAGE_LOGS)/$(FLINTQS).log" -$(INST)/$(FLINT): $(INST)/$(MPIR) $(INST)/$(MPFR) $(INST)/$(NTL) +$(INST)/$(FLINT): $(INST)/$(SAGE_MP_LIBRARY) $(INST)/$(MPFR) $(INST)/$(NTL) +$(PIPE) "$(SAGE_SPKG) $(FLINT) 2>&1" "tee -a $(SAGE_LOGS)/$(FLINT).log" $(INST)/$(ECLIB): $(INST)/$(PARI) $(INST)/$(NTL) $(INST)/$(FLINT) @@ -410,7 +410,7 @@ $(INST)/$(M4RIE): $(INST)/$(M4RI) $(INST)/$(GIVARO) $(INST)/$(NTL) # zn_poly really does depend on Python, despite this is far from obvious. # The 'configure' script in zn_poly calls Python to make a 'makefile'. -$(INST)/$(ZNPOLY): $(INST)/$(MPIR) $(INST)/$(PYTHON) +$(INST)/$(ZNPOLY): $(INST)/$(SAGE_MP_LIBRARY) $(INST)/$(PYTHON) +$(PIPE) "$(SAGE_SPKG) $(ZNPOLY) 2>&1" "tee -a $(SAGE_LOGS)/$(ZNPOLY).log" $(INST)/$(SAGENB): $(INST)/$(PYTHON) $(INST)/$(SETUPTOOLS) $(INST)/$(PEXPECT) \ @@ -433,7 +433,7 @@ $(INST)/$(PYGMENTS): $(INST)/$(PYTHON) $(INST)/$(SETUPTOOLS) # List all *build-time* dependencies of the Sage library. These are, # on the one hand, programs needed for the build/install process of the # Sage library (e.g. JINJA2), and on the -# other hand all dependencies for Cython files (e.g. PARI, NTL, MPIR). +# other hand all dependencies for Cython files (e.g. PARI, NTL, SAGE_MP_LIBRARY). $(INST)/sage: \ $(INST)/$(ATLAS) \ $(INST)/$(CEPHES) \ @@ -459,7 +459,7 @@ $(INST)/sage: \ $(INST)/$(MPC) \ $(INST)/$(MPFI) \ $(INST)/$(MPFR) \ - $(INST)/$(MPIR) \ + $(INST)/$(SAGE_MP_LIBRARY) \ $(INST)/$(NTL) \ $(INST)/$(NUMPY) \ $(INST)/$(PARI) \ @@ -480,7 +480,7 @@ $(INST)/sage: \ fi $(INST)/csage: $(INST)/$(SCONS) \ - $(INST)/$(MPIR) \ + $(INST)/$(SAGE_MP_LIBRARY) \ $(INST)/$(NTL) \ $(INST)/$(PARI) \ $(INST)/$(PYTHON) @@ -494,7 +494,7 @@ $(INST)/ccache: $(BASE) $(INST)/$(ZLIB) +$(PIPE) "$(SAGE_SPKG) ccache 2>&1" "tee -a $(SAGE_LOGS)/ccache.log" touch $(INST)/ccache -$(INST)/$(GCC): $(INST)/$(MPIR) $(INST)/$(MPFR) $(INST)/$(MPC) \ +$(INST)/$(GCC): $(INST)/$(SAGE_MP_LIBRARY) $(INST)/$(MPFR) $(INST)/$(MPC) \ $(INST)/$(ZLIB) +$(PIPE) "$(SAGE_SPKG) $(GCC) 2>&1" "tee -a $(SAGE_LOGS)/$(GCC).log" diff --git a/build/install b/build/install index 05d0b575b07..e990f5008d6 100755 --- a/build/install +++ b/build/install @@ -43,12 +43,6 @@ EOF exit 2 fi -if [ "$SAGE_UPGRADING" = yes ]; then - # We're doing an upgrade. Let build/Makefile call sage-spkg with - # "-f" to force rebuilding dependent packages, too: - export SAGE_SPKG_OPTS="-f" -fi - ############################################################################### # Create basic directories needed for Sage ############################################################################### @@ -61,6 +55,70 @@ mkdir -p "$SAGE_LOCAL/lib" mkdir -p "$SAGE_SPKG_INST" mkdir -p "$SAGE_SHARE" +############################################################################### +# Determine whether to use MPIR (default standard pkg) or GMP (optional pkg). +############################################################################### + +if [ -z "$SAGE_MP_LIBRARY" ]; then + # Automatic detection of installed MP library. + if [ ! -f "$SAGE_LOCAL/share/mp_config" ]; then + echo "MPIR" > "$SAGE_LOCAL/share/mp_config" + fi + SAGE_MP_LIBRARY=`cat "$SAGE_LOCAL/share/mp_config"` +fi + +SAGE_OLD_MP_LIBRARY=`cat "$SAGE_LOCAL/share/mp_config"` +if [ "$SAGE_MP_LIBRARY" != "$SAGE_OLD_MP_LIBRARY" ]; then + SAGE_CHANGE_MP_LIBRARY=yes +else + SAGE_CHANGE_MP_LIBRARY=no +fi + +case "$SAGE_MP_LIBRARY" in + MPIR|GMP) + echo "Using $SAGE_MP_LIBRARY as default MP library." + echo $SAGE_MP_LIBRARY > "$SAGE_LOCAL/share/mp_config" + ;; + *) + echo "Allowed values for SAGE_MP_LIBRARY are \"MPIR\" and \"GMP\"." + echo "If you did not set this variable, check the content of" + echo "\"$SAGE_LOCAL/share/mp_config\"." + exit 1 + ;; +esac + +if [ "$SAGE_CHANGE_MP_LIBRARY" = "yes" ]; then + if [ -f "$SAGE_LOCAL/bin/gcc" ]; then + echo "Deleting old GCC install as the default MP library changes." + rm -f "$SAGE_LOCAL/bin/gcc" + rm -f "$SAGE_LOCAL/bin/cpp" + rm -f "$SAGE_LOCAL/bin/g++" + rm -f "$SAGE_LOCAL/bin/gfortran" + fi + if [ -z "$SAGE_UPGRADING" ]; then + echo "Setting SAGE_UPGRADING to yes as the default MP library changes." + SAGE_UPGRADING=yes + else + if [ "$SAGE_UPGRADING" != "yes" ]; then + echo "SAGE_UPGRADING is set to something different from yes" + echo "whereas the default MP library changes." + echo "You might end up with a broken Sage installation." + fi + fi +fi + +export SAGE_MP_LIBRARY SAGE_CHANGE_MP_LIBRARY + +############################################################################### +# Determine whether we should automatically rebuild dependencies. +############################################################################### + +if [ "$SAGE_UPGRADING" = yes ]; then + # We're doing an upgrade. Let build/Makefile call sage-spkg with + # "-f" to force rebuilding dependent packages, too: + export SAGE_SPKG_OPTS="-f" +fi + ############################################################################### # Determine whether to install GCC (gcc, g++, gfortran). ############################################################################### @@ -401,7 +459,6 @@ MAXIMA=`newest_version maxima` MPC=`newest_version mpc` MPFI=`newest_version mpfi` MPFR=`newest_version mpfr` -MPIR=`newest_version mpir` MPMATH=`newest_version mpmath` NETWORKX=`newest_version networkx` NTL=`newest_version ntl` @@ -447,6 +504,15 @@ INST=`echo "$SAGE_SPKG_INST" | sed 's/ /\\\\ /g'` EOF +# Sage MP library +sage_mp_library=`echo "$SAGE_MP_LIBRARY" | tr '[A-Z]' '[a-z]'` +cat >&5 << EOF +# Sage MP library +$(SAGE_MP_LIBRARY)=`newest_version $(sage_mp_library)` +SAGE_MP_LIBRARY=$(SAGE_MP_LIBRARY) + +EOF + # $(TOOLCHAIN) variable containing prerequisites for the build echo >&5 -n 'TOOLCHAIN =' if [ "$SAGE_INSTALL_CCACHE" = yes ]; then diff --git a/build/pkgs/gmp/spkg-install b/build/pkgs/gmp/spkg-install index a911077431d..cedead6d99b 100755 --- a/build/pkgs/gmp/spkg-install +++ b/build/pkgs/gmp/spkg-install @@ -311,13 +311,16 @@ rm -f "$SAGE_LOCAL"/include/{gmp,mpir}*.h # Do NOT delete old GMP/GMP shared libraries as Sage's versions of libraries # used by GCC might still refer to them, such that their deletion would break # GCC inside Sage. (We could perhaps remove libgmp* though.) -if false; then +# Unless we are switching the default MP library which breaks GCC anyway. +if [ ! -x "$SAGE_LOCAL/bin/gcc" ] || [ "$SAGE_CHANGE_MP_LIBRARY" = "yes" ]; then echo "Removing old GMP/MPIR libraries..." rm -f "$SAGE_LOCAL"/lib/lib{gmp,mpir}* + rm -f "$SAGE_LOCAL"/bin/cyg{gmp,mpir}* else echo "Not removing old GMP/MPIR shared libraries, as other libraries" echo "and executables might still refer to them:" - ls -l "$SAGE_LOCAL"/lib/lib{gmp,mpir}*.so.* + ls -l "$SAGE_LOCAL"/lib/lib{gmp,mpir}* + ls -l "$SAGE_LOCAL"/bin/cyg{gmp,mpir}* echo "(Libraries with the same version number will get updated though.)" fi @@ -330,9 +333,7 @@ echo "Now installing GMP..." $MAKE install if [ $? -ne 0 ]; then echo >&2 "Error installing GMP." - if [ "$UNAME" != "CYGWIN" ]; then # On Cygwin an error is not fatal. - exit 1 - fi + exit 1 fi echo diff --git a/build/pkgs/mpir/spkg-install b/build/pkgs/mpir/spkg-install index 7172e4a76bd..dd8817866a7 100755 --- a/build/pkgs/mpir/spkg-install +++ b/build/pkgs/mpir/spkg-install @@ -193,7 +193,13 @@ export ABI CFLAGS CXXFLAGS LDFLAGS # Partially redundant, but safe(r). # Now configure MPIR, eventually modifying CFLAGS [further]: ############################################################################### -MPIR_CONFIGURE="--enable-gmpcompat --enable-shared $MPIR_CONFIGURE" +MPIR_CONFIGURE="--enable-shared $MPIR_CONFIGURE" + +# Don't install GMP compat headers and symlinks if MPIR is not the default MP +# library. +if [ `cat "SAGE_LOCAL/share/mp_config"` = "MPIR" ]; then + MPIR_CONFIGURE="--enable-gmpcompat $MPIR_CONFIGURE" +fi # If we're bootstrapping GCC from the GCC spkg, don't build the C++ # interface (cf. #12782), static libraries and disable fat binary. @@ -334,13 +340,16 @@ rm -f "$SAGE_LOCAL"/include/{gmp,mpir}*.h # Do NOT delete old GMP/MPIR shared libraries as Sage's versions of libraries # used by GCC might still refer to them, such that their deletion would break # GCC inside Sage. (We could perhaps remove libmpir* though.) -if false; then +# Unless we are switching the default MP library which breaks GCC anyway. +if [ ! -x "$SAGE_LOCAL/bin/gcc" ] || [ "$SAGE_CHANGE_MP_LIBRARY" = "yes" ]; then echo "Removing old GMP/MPIR libraries..." rm -f "$SAGE_LOCAL"/lib/lib{gmp,mpir}* + rm -f "$SAGE_LOCAL"/bin/cyg{gmp,mpir}* else echo "Not removing old GMP/MPIR shared libraries, as other libraries" echo "and executables might still refer to them:" - ls -l "$SAGE_LOCAL"/lib/lib{gmp,mpir}*.so.* + ls -l "$SAGE_LOCAL"/lib/lib{gmp,mpir}* + ls -l "$SAGE_LOCAL"/bin/cyg{gmp,mpir}* echo "(Libraries with the same version number will get updated though.)" fi @@ -353,9 +362,7 @@ echo "Now installing MPIR..." $MAKE install if [ $? -ne 0 ]; then echo >&2 "Error installing MPIR." - if [ "$UNAME" != "CYGWIN" ]; then # On Cygwin an error is not fatal. - exit 1 - fi + exit 1 fi echo From 64802d4fd81be99903abe87c2c77261cef77808c Mon Sep 17 00:00:00 2001 From: Jean-Pierre Flori Date: Mon, 17 Mar 2014 08:49:42 -0700 Subject: [PATCH 008/217] Introduce variables to switch between MPIR and GMP. One case play with: * SAGE_MP_LIBRARY; * SAGE_CHANGE_MP_LIBRARY; * SAGE_UPGRADING. --- build/install | 36 +++++++++++++++++++++++++++--------- build/pkgs/gmp/spkg-install | 4 +--- build/pkgs/mpir/spkg-install | 6 ++---- 3 files changed, 30 insertions(+), 16 deletions(-) diff --git a/build/install b/build/install index 8b5801d11da..3c0ee98d95e 100755 --- a/build/install +++ b/build/install @@ -68,10 +68,12 @@ if [ -z "$SAGE_MP_LIBRARY" ]; then fi SAGE_OLD_MP_LIBRARY=`cat "$SAGE_LOCAL/share/mp_config"` -if [ "$SAGE_MP_LIBRARY" != "$SAGE_OLD_MP_LIBRARY" ]; then - SAGE_CHANGE_MP_LIBRARY=yes -else - SAGE_CHANGE_MP_LIBRARY=no +if [ -z "$SAGE_CHANGE_MP_LIBRARY" ]; then + if [ "$SAGE_MP_LIBRARY" != "$SAGE_OLD_MP_LIBRARY" ]; then + SAGE_CHANGE_MP_LIBRARY=yes + else + SAGE_CHANGE_MP_LIBRARY=no + fi fi case "$SAGE_MP_LIBRARY" in @@ -88,20 +90,37 @@ case "$SAGE_MP_LIBRARY" in esac if [ "$SAGE_CHANGE_MP_LIBRARY" = "yes" ]; then + echo "SAGE_CHANGE_MP_LIBRARY has been set to yes automatically or by the user." + echo "Therefore, the previous versions of MP/MPFR/MPC/PPL and potentially GCC" + echo "will be deleted and rebuilt." + echo "This may lead to an unusable installation." + echo "If that was not your intention, you have 5 seconds to kill this process." + sleep 5 + + echo "Deleting old MP/MPFR/MPC/PPL install as the default MP library changed." + rm -f "$SAGE_LOCAL"/lib/lib{gmp,mpir}* + rm -f "$SAGE_LOCAL"/lib/libmpfr* + rm -f "$SAGE_LOCAL"/lib/libmpc* + rm -f "$SAGE_LOCAL"/lib/libppl* + if [ -f "$SAGE_LOCAL/bin/gcc" ]; then - echo "Deleting old GCC install as the default MP library changes." + echo "Deleting old GCC install as the default MP library changed." rm -f "$SAGE_LOCAL/bin/gcc" rm -f "$SAGE_LOCAL/bin/cpp" rm -f "$SAGE_LOCAL/bin/g++" rm -f "$SAGE_LOCAL/bin/gfortran" fi + + echo "Triggering rebuild of MP library." + rm -f "$SAGE_SPKG_INST"/{gmp,mpir}* + if [ -z "$SAGE_UPGRADING" ]; then - echo "Setting SAGE_UPGRADING to yes as the default MP library changes." + echo "Setting SAGE_UPGRADING to yes as the default MP library changed." SAGE_UPGRADING=yes else if [ "$SAGE_UPGRADING" != "yes" ]; then echo "SAGE_UPGRADING is set to something different from yes" - echo "whereas the default MP library changes." + echo "whereas the default MP library changed." echo "You might end up with a broken Sage installation." fi fi @@ -476,8 +495,7 @@ EOF sage_mp_library=`echo "$SAGE_MP_LIBRARY" | tr '[A-Z]' '[a-z]'` cat >&5 << EOF # Sage MP library -$(SAGE_MP_LIBRARY)=`newest_version $(sage_mp_library)` -SAGE_MP_LIBRARY=$(SAGE_MP_LIBRARY) +SAGE_MP_LIBRARY=`newest_version $sage_mp_library` EOF diff --git a/build/pkgs/gmp/spkg-install b/build/pkgs/gmp/spkg-install index cedead6d99b..f1762badbc9 100755 --- a/build/pkgs/gmp/spkg-install +++ b/build/pkgs/gmp/spkg-install @@ -312,15 +312,13 @@ rm -f "$SAGE_LOCAL"/include/{gmp,mpir}*.h # used by GCC might still refer to them, such that their deletion would break # GCC inside Sage. (We could perhaps remove libgmp* though.) # Unless we are switching the default MP library which breaks GCC anyway. -if [ ! -x "$SAGE_LOCAL/bin/gcc" ] || [ "$SAGE_CHANGE_MP_LIBRARY" = "yes" ]; then +if [ "$SAGE_CHANGE_MP_LIBRARY" = "yes" ]; then echo "Removing old GMP/MPIR libraries..." rm -f "$SAGE_LOCAL"/lib/lib{gmp,mpir}* - rm -f "$SAGE_LOCAL"/bin/cyg{gmp,mpir}* else echo "Not removing old GMP/MPIR shared libraries, as other libraries" echo "and executables might still refer to them:" ls -l "$SAGE_LOCAL"/lib/lib{gmp,mpir}* - ls -l "$SAGE_LOCAL"/bin/cyg{gmp,mpir}* echo "(Libraries with the same version number will get updated though.)" fi diff --git a/build/pkgs/mpir/spkg-install b/build/pkgs/mpir/spkg-install index dd8817866a7..2c46e0e6b9b 100755 --- a/build/pkgs/mpir/spkg-install +++ b/build/pkgs/mpir/spkg-install @@ -197,7 +197,7 @@ MPIR_CONFIGURE="--enable-shared $MPIR_CONFIGURE" # Don't install GMP compat headers and symlinks if MPIR is not the default MP # library. -if [ `cat "SAGE_LOCAL/share/mp_config"` = "MPIR" ]; then +if [ `cat "$SAGE_LOCAL/share/mp_config"` = "MPIR" ]; then MPIR_CONFIGURE="--enable-gmpcompat $MPIR_CONFIGURE" fi @@ -341,15 +341,13 @@ rm -f "$SAGE_LOCAL"/include/{gmp,mpir}*.h # used by GCC might still refer to them, such that their deletion would break # GCC inside Sage. (We could perhaps remove libmpir* though.) # Unless we are switching the default MP library which breaks GCC anyway. -if [ ! -x "$SAGE_LOCAL/bin/gcc" ] || [ "$SAGE_CHANGE_MP_LIBRARY" = "yes" ]; then +if [ "$SAGE_CHANGE_MP_LIBRARY" = "yes" ]; then echo "Removing old GMP/MPIR libraries..." rm -f "$SAGE_LOCAL"/lib/lib{gmp,mpir}* - rm -f "$SAGE_LOCAL"/bin/cyg{gmp,mpir}* else echo "Not removing old GMP/MPIR shared libraries, as other libraries" echo "and executables might still refer to them:" ls -l "$SAGE_LOCAL"/lib/lib{gmp,mpir}* - ls -l "$SAGE_LOCAL"/bin/cyg{gmp,mpir}* echo "(Libraries with the same version number will get updated though.)" fi From 8f1ad732bd60a20b70626aaeb3f566a31214581e Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Tue, 27 May 2014 15:15:25 +0200 Subject: [PATCH 009/217] trac #12797: The cut returned by edge_cut of undirected weighted graphs is sometimes incorrect --- src/sage/graphs/generic_graph.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/sage/graphs/generic_graph.py b/src/sage/graphs/generic_graph.py index 07d04f02545..f76cecdd077 100644 --- a/src/sage/graphs/generic_graph.py +++ b/src/sage/graphs/generic_graph.py @@ -5217,6 +5217,15 @@ def edge_cut(self, s, t, value_only=True, use_edge_labels=False, vertices=False, sage: g = graphs.PappusGraph() sage: g.edge_cut(1, 2, value_only=True, method = "LP") 3 + + :trac:`12797`:: + + sage: G = Graph([(0, 3, 1), (0, 4, 1), (1, 2, 1), (2, 3, 1), (2, 4, 1)]) + sage: G.edge_cut(0,1,value_only=False,use_edge_labels=True) + [1, [(1, 2, 1)]] + sage: G = DiGraph([(0, 3, 1), (0, 4, 1), (2, 1, 1), (3, 2, 1), (4, 2, 1)]) + sage: G.edge_cut(0,1,value_only=False,use_edge_labels=True) + [1, [(2, 1, 1)]] """ self._scream_if_not_simple(allow_loops=True) if vertices: @@ -5231,9 +5240,12 @@ def edge_cut(self, s, t, value_only=True, use_edge_labels=False, vertices=False, if value_only: return self.flow(s,t,value_only=value_only,use_edge_labels=use_edge_labels, method=method) + from sage.graphs.digraph import DiGraph + g = DiGraph(self) + flow_value, flow_graph = self.flow(s,t,value_only=value_only,use_edge_labels=use_edge_labels, method=method) - g = self.copy() for u,v,l in flow_graph.edge_iterator(): + g.add_edge(v,u) if (not use_edge_labels or (weight(g.edge_label(u,v)) == weight(l))): g.delete_edge(u,v) From 2e30025298097a2c280629135f3f76d973c0b4a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 27 May 2014 20:44:14 +0200 Subject: [PATCH 010/217] trac #10779 corrected 3 doctests --- src/sage/structure/element.pyx | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/src/sage/structure/element.pyx b/src/sage/structure/element.pyx index ed2ae24089e..1dc1fd1bb77 100644 --- a/src/sage/structure/element.pyx +++ b/src/sage/structure/element.pyx @@ -1934,11 +1934,8 @@ def is_CommutativeRingElement(x): TESTS:: + sage: from sage.rings.commutative_ring_element import is_CommutativeRingElement sage: is_CommutativeRingElement(oo) - doctest:...: DeprecationWarning: - Using is_CommutativeRingElement from the top level is deprecated since it was designed to be used by developers rather than end users. - It most likely does not do what you would expect it to do. If you really need to use it, import it from the module that it is defined in. - See http://trac.sagemath.org/10107 for details. False sage: is_CommutativeRingElement(1) @@ -2978,12 +2975,9 @@ def is_AlgebraElement(x): TESTS:: + sage: from sage.structure.element import is_AlgebraElement sage: R. = FreeAlgebra(QQ,2) sage: is_AlgebraElement(x*y) - doctest:...: DeprecationWarning: - Using is_AlgebraElement from the top level is deprecated since it was designed to be used by developers rather than end users. - It most likely does not do what you would expect it to do. If you really need to use it, import it from the module that it is defined in. - See http://trac.sagemath.org/10107 for details. True sage: is_AlgebraElement(1) @@ -3009,11 +3003,8 @@ def is_InfinityElement(x): TESTS:: + sage: from sage.structure.element import is_InfinityElement sage: is_InfinityElement(1) - doctest:...: DeprecationWarning: - Using is_InfinityElement from the top level is deprecated since it was designed to be used by developers rather than end users. - It most likely does not do what you would expect it to do. If you really need to use it, import it from the module that it is defined in. - See http://trac.sagemath.org/10107 for details. False sage: is_InfinityElement(oo) From 2947606572e5421301eb408d26066def13b267d4 Mon Sep 17 00:00:00 2001 From: Julian Rueth Date: Wed, 25 Jun 2014 19:03:00 +0200 Subject: [PATCH 011/217] Improved docstring of polynomial gcd. --- src/sage/rings/polynomial/polynomial_element.pyx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index d36bea278e5..35023fa5d98 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -3692,17 +3692,17 @@ cdef class Polynomial(CommutativeAlgebraElement): @coerce_binop def gcd(self, other): """ - Compute a greatest common divisor of ``self`` and ``other``. + Compute a greatest common divisor of this polynomial and ``other``. INPUT: - - ``other`` -- a polynomial in the same ring as ``self`` + - ``other`` -- a polynomial in the same ring as this polynomial OUTPUT: - A greatest common divisor of ``self`` and ``other`` as a polynomial - in the same ring as ``self``. Over a field, the return value will - be a monic polynomial. + A greatest common divisor as a polynomial in the same ring as this + polynomial. Over a field, the return value will be a monic + polynomial. .. NOTE:: From f40215a7eb8b673d7abae3b2f4dab73eadf22ce2 Mon Sep 17 00:00:00 2001 From: Mario Pernici Date: Tue, 1 Jul 2014 19:24:45 +0200 Subject: [PATCH 012/217] added function `permanental_minor_vector`; used in `rook_vector` --- src/sage/matrix/matrix2.pyx | 8 +-- src/sage/matrix/matrix_misc.py | 128 +++++++++++++++++++++++++++++++++ 2 files changed, 132 insertions(+), 4 deletions(-) diff --git a/src/sage/matrix/matrix2.pyx b/src/sage/matrix/matrix2.pyx index a76cb42622f..ba50c01e1dc 100644 --- a/src/sage/matrix/matrix2.pyx +++ b/src/sage/matrix/matrix2.pyx @@ -49,6 +49,7 @@ import sage.modules.free_module import matrix_space import berlekamp_massey from sage.modules.free_module_element import is_FreeModuleElement +from sage.matrix.matrix_misc import permanental_minor_vector cdef class Matrix(matrix1.Matrix): def _backslash_(self, B): @@ -946,6 +947,7 @@ cdef class Matrix(matrix1.Matrix): AUTHORS: - Jaap Spies (2006-02-24) + - Mario Pernici (2014-07-01) """ m = self._nrows n = self._ncols @@ -960,10 +962,8 @@ cdef class Matrix(matrix1.Matrix): if not (x == 0 or x == 1): raise ValueError, "must have zero or one, but we have (=%s)"%x - tmp = [] - for k in range(m+1): - tmp.append(self.permanental_minor(k)) - return tmp + p = permanental_minor_vector(self) + return p def minors(self,k): r""" diff --git a/src/sage/matrix/matrix_misc.py b/src/sage/matrix/matrix_misc.py index 7a5069cb7af..9870c7b4082 100644 --- a/src/sage/matrix/matrix_misc.py +++ b/src/sage/matrix/matrix_misc.py @@ -19,6 +19,7 @@ #***************************************************************************** from sage.categories.fields import Fields +from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing _Fields = Fields() def row_iterator(A): @@ -199,3 +200,130 @@ def weak_popov_form(M,ascend=True): # return reduced matrix and operations matrix return (matrix(r)/den, matrix(N), d) + +def _prm_mul(p1, p2, free_vars_indices, K): + p = {} + mask_free = 0 + one = int(1) + for i in free_vars_indices: + mask_free += one << i + if not p2: + return p + get = p.get + for exp1, v1 in p1.iteritems(): + for exp2, v2 in p2.items(): + if exp1 & exp2: + continue + exp = exp1 | exp2 + v = v1*v2 + if exp & mask_free: + for i in free_vars_indices: + #print 'DB14 exp=%s i=%s' %(exp, i) + if exp & (one << i): + exp = exp.__xor__(one << i) + #print 'DB14b exp=%s' % exp + p[exp] = get(exp, K.zero()) + v + return p + + +def _is_free_var(i, k, m): + """ + i current row + k index of the variable + m matrix as a list of lists + returns true if the variable `k` does not occur from row `i` on + """ + for j in range(i, len(m)): + if m[j][k] != 0: + return False + return True + +def permanental_minor_vector(m, permanent_only=False): + """ + Return the polynomial of the sums of permanental minors of a matrix `m` + in array form. + + The polynomial of the sums of permanental minors is + + .. MATH:: + + \sum_0^{min(nrows, ncols)} p_i(m) x^i + + where `p_i(m) = m.permanental_minor(i)` + + + INPUT: + + - `m` - matrix + + - `permanent_only` if True, only the permanent is computed + + OUTPUT: + + polynomial in array form; the last element of the array is the permanent. + + EXAMPLES:: + + sage: from sage.matrix.matrix_misc import permanental_minor_vector + sage: m = matrix([[1,1],[1,2]]) + sage: permanental_minor_vector(m) + [1, 5, 3] + sage: permanental_minor_vector(m, permanent_only=1) + 3 + + :: + + sage: M = MatrixSpace(QQ,2,2) + sage: A = M([1/5,2/7,3/2,4/5]) + sage: permanental_minor_vector(A, 1) + 103/175 + + :: + + sage: R. = PolynomialRing(ZZ) + sage: A = MatrixSpace(R,2)([[a,1], [a,a+1]]) + sage: permanental_minor_vector(A, 1) + a^2 + 2*a + + REFERENCES: + + P. Butera and M. Pernici + ``Sums of permanental minors using Grassmann algebra'' + http://arxiv.org/abs/1406.5337 + """ + K = PolynomialRing(m.base_ring(), 'K') + m = list(m) + nrows = len(m) + ncols = len(m[0]) + p = {int(0):K.one()} + t = K.gen() + done_vars = set() + one = int(1) + for i in range(nrows): + if permanent_only: + p1 = {} + else: + p1 = {int(0): K.one()} + a = m[i] + for j in range(len(a)): + if a[j]: + p1[one< Date: Tue, 1 Jul 2014 22:26:49 +0200 Subject: [PATCH 013/217] fixed bug in `permanental_minor_vector` in case of vanishing permanent --- src/sage/matrix/matrix_misc.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/sage/matrix/matrix_misc.py b/src/sage/matrix/matrix_misc.py index 9870c7b4082..774acc85227 100644 --- a/src/sage/matrix/matrix_misc.py +++ b/src/sage/matrix/matrix_misc.py @@ -271,6 +271,14 @@ def permanental_minor_vector(m, permanent_only=False): sage: permanental_minor_vector(m, permanent_only=1) 3 + :: + + sage: from sage.matrix.matrix_misc import permanental_minor_vector + sage: M = MatrixSpace(ZZ,4,4) + sage: A = M([1,0,1,0,1,0,1,0,1,0,10,10,1,0,1,1]) + sage: permanental_minor_vector(A) + [1, 28, 114, 84, 0] + :: sage: M = MatrixSpace(QQ,2,2) @@ -321,7 +329,7 @@ def permanental_minor_vector(m, permanent_only=False): return K.zero() assert len(p) == 1 a = p[0].coeffs() - len_a = min(nrows, ncols) + len_a = min(nrows, ncols) + 1 if len(a) < len_a: a += [0]*(len_a - len(a)) if permanent_only: From 0190e4d1140c1f6c892b16f49e4670071a41a0b7 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Wed, 2 Jul 2014 11:32:43 -0500 Subject: [PATCH 014/217] Some review changes to set.py. --- src/sage/sets/set.py | 43 ++++++++++++++++++++----------------------- 1 file changed, 20 insertions(+), 23 deletions(-) diff --git a/src/sage/sets/set.py b/src/sage/sets/set.py index b412ec81f9b..ce18854de0b 100644 --- a/src/sage/sets/set.py +++ b/src/sage/sets/set.py @@ -15,7 +15,8 @@ - Nicolas M. Thiery (2011-03-15) - Added subset and superset methods -- Julian Rueth (2013-04-09) - Collected common code in Set_object_binary, fixed __hash__. +- Julian Rueth (2013-04-09) - Collected common code in + :class:`Set_object_binary`, fixed ``__hash__``. """ @@ -990,10 +991,11 @@ def symmetric_difference(self, other): return Set_object_enumerated(self.set().symmetric_difference(other.set())) class Set_object_binary(Set_object): - """ - An abstract common base class for :class:`Set_object_union`, - :class:`Set_object_intersection`, :class:`Set_object_difference`, and - :class:`Set_object_symmetric_difference`. + r""" + An abstract common base class for sets defined by a binary operators (ex. + :class:`Set_object_union`, :class:`Set_object_intersection`, + :class:`Set_object_difference`, and + :class:`Set_object_symmetric_difference`). INPUT: @@ -1008,23 +1010,22 @@ class Set_object_binary(Set_object): sage: X = Set(QQ^2) sage: Y = Set(ZZ) sage: from sage.sets.set import Set_object_binary - sage: S = Set_object_binary(X,Y,"union","\\cup"); S - Set-theoretic union of Set of elements of Vector space of dimension 2 over Rational Field and Set of elements of Integer Ring - + sage: S = Set_object_binary(X, Y, "union", "\\cup"); S + Set-theoretic union of Set of elements of Vector space of dimension 2 + over Rational Field and Set of elements of Integer Ring """ def __init__(self, X, Y, op, latex_op): - """ + r""" Initialization. TESTS:: - sage: from sage.sets.set import Set_object_binary - sage: X = Set(QQ^2) - sage: Y = Set(ZZ) - sage: S = Set_object_binary(X,Y,"union","\\cup") - sage: type(S) - - + sage: from sage.sets.set import Set_object_binary + sage: X = Set(QQ^2) + sage: Y = Set(ZZ) + sage: S = Set_object_binary(X, Y, "union", "\\cup") + sage: type(S) + """ self._X = X self._Y = Y @@ -1040,9 +1041,8 @@ def _repr_(self): sage: Set(ZZ).union(Set(GF(5))) Set-theoretic union of Set of elements of Integer Ring and {0, 1, 2, 3, 4} - """ - return "Set-theoretic %s of %s and %s"%(self._op, self._X, self._Y) + return "Set-theoretic {} of {} and {}".format(self._op, self._X, self._Y) def _latex_(self): r""" @@ -1052,9 +1052,8 @@ def _latex_(self): sage: latex(Set(ZZ).union(Set(GF(5)))) \Bold{Z} \cup \left\{0, 1, 2, 3, 4\right\} - """ - return '%s %s %s'%(latex(self._X), self._latex_op, latex(self._Y)) + return latex(self._X) + self._latex_op + latex(self._Y) def cardinality(self): """ @@ -1075,7 +1074,6 @@ def __hash__(self): """ The hash value of this set. - EXAMPLES: The hash values of equal sets are in general not equal since it is not @@ -1094,9 +1092,8 @@ def __hash__(self): sage: T = Set(ZZ).union(Set([infinity])) sage: hash(S) == hash(T) True - """ - return hash((self._X,self._Y,self._op)) + return hash((self._X, self._Y, self._op)) class Set_object_union(Set_object_binary): """ From 05ed0370f6a08b0e181f1f520b7df7ec6312a15c Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Wed, 2 Jul 2014 11:35:09 -0500 Subject: [PATCH 015/217] Fix typo. --- src/sage/sets/set.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/sets/set.py b/src/sage/sets/set.py index ce18854de0b..6451e166af8 100644 --- a/src/sage/sets/set.py +++ b/src/sage/sets/set.py @@ -992,7 +992,7 @@ def symmetric_difference(self, other): class Set_object_binary(Set_object): r""" - An abstract common base class for sets defined by a binary operators (ex. + An abstract common base class for sets defined by a binary operation (ex. :class:`Set_object_union`, :class:`Set_object_intersection`, :class:`Set_object_difference`, and :class:`Set_object_symmetric_difference`). From 7b1807808cb638a2f064b78a71ad01f74303bddb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sun, 27 Jul 2014 10:05:10 +0200 Subject: [PATCH 016/217] trac #16399 first naive try --- src/sage/matrix/matrix_integer_dense.pyx | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/src/sage/matrix/matrix_integer_dense.pyx b/src/sage/matrix/matrix_integer_dense.pyx index 2cec8c7f833..e076db65fb0 100644 --- a/src/sage/matrix/matrix_integer_dense.pyx +++ b/src/sage/matrix/matrix_integer_dense.pyx @@ -4388,12 +4388,15 @@ cdef class Matrix_integer_dense(matrix_dense.Matrix_dense): # dense or sparse return res - ##################################################################################### + ################################################################# # operations with matrices - ##################################################################################### + ################################################################# def stack(self, bottom, subdivide=False): r""" - Return the matrix self on top of bottom: [ self ] [ bottom ] + Return the matrix ``self`` on top of ``bottom``: + + [ self ] + [ bottom ] EXAMPLES:: @@ -4451,8 +4454,18 @@ cdef class Matrix_integer_dense(matrix_dense.Matrix_dense): # dense or sparse bottom = bottom.row() if self._ncols != bottom.ncols(): raise TypeError("number of columns must be the same") - if not (self._base_ring is bottom.base_ring()): - bottom = bottom.change_ring(self._base_ring) + top_ring = self._base_ring + bottom_ring = bottom.base_ring() + if not (top_ring is bottom_ring): + if top_ring_has_coerce_map_from(bottom_ring): + bottom = bottom.change_ring(top_ring) + elif bottom_ring_has_coerce_map_from(top_ring): + new_top = self.change_ring(bottom_ring) + return new_top.stack(bottom, subdivide=subdivide): + else: + # what to do ? + # how to find a common parent ? + raise TypeError('damn') cdef Matrix_integer_dense other = bottom.dense_matrix() cdef Matrix_integer_dense M M = self.new_matrix(nrows = self._nrows + other._nrows, ncols = self.ncols()) From 098d574aa98441e4067178a4b8913c88ebc0f6b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sun, 27 Jul 2014 10:20:02 +0200 Subject: [PATCH 017/217] trac #16399 now for sparse matrices --- src/sage/matrix/matrix_sparse.pyx | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/sage/matrix/matrix_sparse.pyx b/src/sage/matrix/matrix_sparse.pyx index d30923e8caa..40d04337017 100644 --- a/src/sage/matrix/matrix_sparse.pyx +++ b/src/sage/matrix/matrix_sparse.pyx @@ -905,8 +905,17 @@ cdef class Matrix_sparse(matrix.Matrix): if self._ncols != other._ncols: raise TypeError, "number of columns must be the same" - if not (self._base_ring is other.base_ring()): - other = other.change_ring(self._base_ring) + top_ring = self._base_ring + bottom_ring = other.base_ring() + if not (top_ring is bottom_ring): + if top_ring_has_coerce_map_from(bottom_ring): + other = other.change_ring(top_ring) + elif bottom_ring_has_coerce_map_from(top_ring): + self = self.change_ring(bottom_ring) # allowed ? + else: + # what to do ? + # how to find a common parent ? + raise TypeError('damn') cdef Matrix_sparse Z Z = self.new_matrix(nrows = self._nrows + other.nrows()) From bee1968a7bfc534ac1ce216562b8f66df770483e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Wed, 30 Jul 2014 16:10:35 +0200 Subject: [PATCH 018/217] trac #16399 trying to use coercion model --- src/sage/matrix/matrix_sparse.pyx | 32 +++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/src/sage/matrix/matrix_sparse.pyx b/src/sage/matrix/matrix_sparse.pyx index 40d04337017..d4b69da9c7e 100644 --- a/src/sage/matrix/matrix_sparse.pyx +++ b/src/sage/matrix/matrix_sparse.pyx @@ -4,7 +4,7 @@ Base class for sparse matrices cimport matrix cimport matrix0 -from sage.structure.element cimport Element, RingElement, ModuleElement, Vector +from sage.structure.element cimport (Element, RingElement, ModuleElement, Vector, CoercionModel) from sage.rings.ring import is_Ring from sage.misc.misc import verbose @@ -894,16 +894,33 @@ cdef class Matrix_sparse(matrix.Matrix): [-----------] [ 0 1 2 3] [ 4 5 6 7] + + TESTS:: + + One can stack matrices over different rings (:trac:`16399`). :: + + sage: M = Matrix(ZZ, 2, 3, range(6), sparse=True) + sage: N = Matrix(QQ, 1, 3, [10,11,12], sparse=True) + sage: M.stack(N) + [0 1 2 3 4] + [5 6 7 8 9] + [0 1 2 3 4] + sage: N.stack(M) + ? + sage: M2 = Matrix(ZZ['x'], 2, 3, range(6), sparse=True) + sage: N.stack(M2) + ? """ if hasattr(bottom, '_vector_'): bottom = bottom.row() if not isinstance(bottom, matrix.Matrix): - raise TypeError, "other must be a matrix" + raise TypeError("other must be a matrix") cdef Matrix_sparse other = bottom.sparse_matrix() + cdef CoercionModel coercion_model if self._ncols != other._ncols: - raise TypeError, "number of columns must be the same" + raise TypeError("number of columns must be the same") top_ring = self._base_ring bottom_ring = other.base_ring() @@ -913,9 +930,12 @@ cdef class Matrix_sparse(matrix.Matrix): elif bottom_ring_has_coerce_map_from(top_ring): self = self.change_ring(bottom_ring) # allowed ? else: - # what to do ? - # how to find a common parent ? - raise TypeError('damn') + from sage.structure.element import get_coercion_model + coercion_model = get_coercion_model() + com_ring = coercion_model.common_parent(top_ring, bottom_ring) + self = self.change_ring(com_ring) # allowed ? + other = other.change_ring(com_ring) + cdef Matrix_sparse Z Z = self.new_matrix(nrows = self._nrows + other.nrows()) From 9986c476c78510e1b98d46890a5b9506f3b62613 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Wed, 30 Jul 2014 16:18:12 +0200 Subject: [PATCH 019/217] trac #16399 fixing some obvious errors. --- src/sage/matrix/matrix_integer_dense.pyx | 31 +++++++++++++++++++----- src/sage/matrix/matrix_sparse.pyx | 4 +-- 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/src/sage/matrix/matrix_integer_dense.pyx b/src/sage/matrix/matrix_integer_dense.pyx index e076db65fb0..84998580f84 100644 --- a/src/sage/matrix/matrix_integer_dense.pyx +++ b/src/sage/matrix/matrix_integer_dense.pyx @@ -93,7 +93,7 @@ from sage.rings.integer_ring import ZZ, IntegerRing_class from sage.rings.integer_ring cimport IntegerRing_class from sage.rings.finite_rings.integer_mod_ring import IntegerModRing from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing -from sage.structure.element cimport ModuleElement, RingElement, Element, Vector +from sage.structure.element cimport (ModuleElement, RingElement, Element, Vector, CoercionModel) from sage.structure.element import is_Vector from sage.structure.sequence import Sequence @@ -4449,23 +4449,42 @@ cdef class Matrix_integer_dense(matrix_dense.Matrix_dense): # dense or sparse [ 0 0 12] sage: P.is_sparse() False + + One can stack matrices over different rings (:trac:`16399`). :: + + sage: M = Matrix(ZZ, 2, 3, range(6)) + sage: N = Matrix(QQ, 1, 3, [10,11,12]) + sage: M.stack(N) + [0 1 2 3 4] + [5 6 7 8 9] + [0 1 2 3 4] + sage: N.stack(M) + ? + sage: M2 = Matrix(ZZ['x'], 2, 3, range(6)) + sage: N.stack(M2) + ? """ if hasattr(bottom, '_vector_'): bottom = bottom.row() if self._ncols != bottom.ncols(): raise TypeError("number of columns must be the same") + cdef CoercionModel coercion_model + top_ring = self._base_ring bottom_ring = bottom.base_ring() if not (top_ring is bottom_ring): - if top_ring_has_coerce_map_from(bottom_ring): + if top_ring.has_coerce_map_from(bottom_ring): bottom = bottom.change_ring(top_ring) - elif bottom_ring_has_coerce_map_from(top_ring): + elif bottom_ring.has_coerce_map_from(top_ring): new_top = self.change_ring(bottom_ring) return new_top.stack(bottom, subdivide=subdivide): else: - # what to do ? - # how to find a common parent ? - raise TypeError('damn') + from sage.structure.element import get_coercion_model + coercion_model = get_coercion_model() + com_ring = coercion_model.common_parent(top_ring, bottom_ring) + self = self.change_ring(com_ring) # allowed ? + bottom = other.change_ring(com_ring) + cdef Matrix_integer_dense other = bottom.dense_matrix() cdef Matrix_integer_dense M M = self.new_matrix(nrows = self._nrows + other._nrows, ncols = self.ncols()) diff --git a/src/sage/matrix/matrix_sparse.pyx b/src/sage/matrix/matrix_sparse.pyx index d4b69da9c7e..ed5ee0747a6 100644 --- a/src/sage/matrix/matrix_sparse.pyx +++ b/src/sage/matrix/matrix_sparse.pyx @@ -925,9 +925,9 @@ cdef class Matrix_sparse(matrix.Matrix): top_ring = self._base_ring bottom_ring = other.base_ring() if not (top_ring is bottom_ring): - if top_ring_has_coerce_map_from(bottom_ring): + if top_ring.has_coerce_map_from(bottom_ring): other = other.change_ring(top_ring) - elif bottom_ring_has_coerce_map_from(top_ring): + elif bottom_ring.has_coerce_map_from(top_ring): self = self.change_ring(bottom_ring) # allowed ? else: from sage.structure.element import get_coercion_model From 95319371cc13f9512521043bfe53cfe806df244e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Wed, 20 Aug 2014 11:31:34 +0200 Subject: [PATCH 020/217] trac #10779 correct failing doctest + more doc changes --- src/sage/structure/element.pyx | 142 ++++++++++++++++----------------- 1 file changed, 71 insertions(+), 71 deletions(-) diff --git a/src/sage/structure/element.pyx b/src/sage/structure/element.pyx index 0fbece59d06..c66a922b608 100644 --- a/src/sage/structure/element.pyx +++ b/src/sage/structure/element.pyx @@ -109,12 +109,12 @@ and classes are similar. There are four relevant functions. This is the function you should override to implement addition in a python subclass of RingElement. - .. warning:: + .. WARNING:: - if you override this in a *Cython* class, it won't get called. - You should override _add_ instead. It is especially important to - keep this in mind whenever you move a class down from Python to - Cython. + if you override this in a *Cython* class, it won't get called. + You should override _add_ instead. It is especially important to + keep this in mind whenever you move a class down from Python to + Cython. The two arguments to this function are guaranteed to have the SAME PARENT. Its return value MUST have the SAME PARENT as its @@ -282,11 +282,11 @@ cdef class Element(sage_object.SageObject): and ``cat.element_class``, in that order, for attribute lookup. - NOTE: + .. NOTE:: - Attributes beginning with two underscores but not ending with - an unnderscore are considered private and are thus exempted - from the lookup in ``cat.element_class``. + Attributes beginning with two underscores but not ending + with an underscore are considered private and are thus + exempted from the lookup in ``cat.element_class``. EXAMPLES: @@ -369,7 +369,7 @@ cdef class Element(sage_object.SageObject): def __getstate__(self): """ - Returns a tuple describing the state of your object. + Return a tuple describing the state of your object. This should return all information that will be required to unpickle the object. The functionality for unpickling is implemented in @@ -406,7 +406,7 @@ cdef class Element(sage_object.SageObject): def __copy__(self): """ - Returns a copy of ``self``. + Return a copy of ``self``. OUTPUT: @@ -474,7 +474,7 @@ cdef class Element(sage_object.SageObject): def base_ring(self): """ - Returns the base ring of this element's parent (if that makes sense). + Return the base ring of this element's parent (if that makes sense). TESTS:: @@ -504,8 +504,8 @@ cdef class Element(sage_object.SageObject): sage: from sage.categories.examples.sets_cat import PrimeNumbers sage: class CCls(PrimeNumbers): - ... def an_element(self): - ... return 18 + ....: def an_element(self): + ....: return 18 sage: CC = CCls() sage: CC._test_an_element() Traceback (most recent call last): @@ -541,8 +541,8 @@ cdef class Element(sage_object.SageObject): Let us now write a broken class method:: sage: class CCls(Element): - ... def __eq__(self, other): - ... return True + ....: def __eq__(self, other): + ....: return True sage: CCls(Parent())._test_eq() Traceback (most recent call last): ... @@ -551,8 +551,8 @@ cdef class Element(sage_object.SageObject): Let us now break inequality:: sage: class CCls(Element): - ... def __ne__(self, other): - ... return True + ....: def __ne__(self, other): + ....: return True sage: CCls(Parent())._test_eq() Traceback (most recent call last): ... @@ -573,7 +573,7 @@ cdef class Element(sage_object.SageObject): def parent(self, x=None): """ - Returns parent of this element; or, if the optional argument x is + Return the parent of this element; or, if the optional argument x is supplied, the result of coercing x into the parent of this element. """ if x is None: @@ -672,9 +672,9 @@ cdef class Element(sage_object.SageObject): Evaluates numerically and returns an mpmath number. Used as fallback for conversion by mpmath.mpmathify(). - .. note:: + .. NOTE:: - Currently, the rounding mode is ignored. + Currently, the rounding mode is ignored. EXAMPLES:: @@ -813,13 +813,15 @@ cdef class Element(sage_object.SageObject): def is_zero(self): """ - Return ``True`` if ``self`` equals self.parent()(0). The default - implementation is to fall back to 'not self.__nonzero__'. + Return ``True`` if ``self`` equals self.parent()(0). - .. warning:: + The default implementation is to fall back to 'not + self.__nonzero__'. - Do not re-implement this method in your subclass but - implement __nonzero__ instead. + .. WARNING:: + + Do not re-implement this method in your subclass but + implement __nonzero__ instead. """ return not self @@ -1151,10 +1153,11 @@ cdef class ElementWithCachedMethod(Element): This getattr method ensures that cached methods and lazy attributes can be inherited from the element class of a category. - NOTE: + .. NOTE:: - The use of cached methods is demonstrated in the main doc string of - this class. Here, we demonstrate lazy attributes. + The use of cached methods is demonstrated in the main doc + string of this class. Here, we demonstrate lazy + attributes. EXAMPLE:: @@ -1868,14 +1871,14 @@ cdef class RingElement(ModuleElement): def additive_order(self): """ - Return the additive order of self. + Return the additive order of ``self``. """ raise NotImplementedError def multiplicative_order(self): r""" - Return the multiplicative order of self, if ``self`` is a unit, or raise - ``ArithmeticError`` otherwise. + Return the multiplicative order of ``self``, if ``self`` is a unit, + or raise ``ArithmeticError`` otherwise. """ if not self.is_unit(): raise ArithmeticError("self (=%s) must be a unit to have a multiplicative order.") @@ -1883,22 +1886,22 @@ cdef class RingElement(ModuleElement): def is_nilpotent(self): """ - Return ``True`` if ``self`` is nilpotent, i.e., some power of self + Return ``True`` if ``self`` is nilpotent, i.e., some power of ``self`` is 0. TESTS:: - sage: a=QQ(2) + sage: a = QQ(2) sage: a.is_nilpotent() False - sage: a=QQ(0) + sage: a = QQ(0) sage: a.is_nilpotent() True - sage: m=matrix(RR,3,[[3,2,3],[9,0,3],[-9,0,-3]]) + sage: m = matrix(QQ,3,[[3,2,3],[9,0,3],[-9,0,-3]]) sage: m.is_nilpotent() Traceback (most recent call last): ... - NotImplementedError + AttributeError: ... object has no attribute 'is_nilpotent' """ if self.is_unit(): return False @@ -2090,15 +2093,15 @@ cdef class CommutativeRingElement(RingElement): sage: f.mod(x + 1) -1 - When little is implemented about a given ring, then mod may - return simply return `f`. For example, reduction is not - implemented for `\ZZ[x]` yet. (TODO!) + Reduction for `\ZZ[x]`:: sage: R. = PolynomialRing(ZZ) sage: f = x^3 + x + 1 sage: f.mod(x + 1) -1 + When little is implemented about a given ring, then mod may + return simply return `f`. EXAMPLE: Multivariate polynomials We reduce a polynomial in two variables modulo a polynomial @@ -2135,25 +2138,22 @@ cdef class CommutativeRingElement(RingElement): def is_square(self, root=False): """ - Returns whether or not ring element is a square. If the optional - argument root is ``True``, then also returns the square root (or None, - if the it is not a square). - - INPUT: + Return whether or not the ring element ``self`` is a square. + If the optional argument root is ``True``, then also return + the square root (or ``None``, if it is not a square). - - ``root`` - whether or not to also return a square - root (default: False) + INPUT: + - ``root`` - whether or not to also return a square + root (default: ``False``) OUTPUT: + - ``bool`` -- whether or not a square - - ``bool`` - whether or not a square - - - ``object`` - (optional) an actual square root if - found, and None otherwise. - + - ``object`` -- (optional) an actual square root if + found, and ``None`` otherwise. EXAMPLES:: @@ -2169,17 +2169,17 @@ cdef class CommutativeRingElement(RingElement): sage: h.is_square(root=True) (True, 2*x^2 + 8*x + 6) - .. NOTE: + .. NOTE:: - This is the is_square implementation for general commutative ring - elements. It's implementation is to raise a NotImplementedError. - The function definition is here to show what functionality is expected and - provide a general framework. + This is the is_square implementation for general + commutative ring elements. It's implementation is to raise + a NotImplementedError. The function definition is here to + show what functionality is expected and provide a general + framework. """ - raise NotImplementedError("is_square() not implemented for elements of %s" %self.parent()) + raise NotImplementedError("is_square() not implemented for elements of %s" % self.parent()) - - def sqrt(self, extend = True, all = False, name=None ): + def sqrt(self, extend=True, all=False, name=None): """ It computes the square root. @@ -2329,10 +2329,10 @@ cdef class Vector(ModuleElement): - Gonzalo Tornaria (2007-06-21) - write test cases and fix them - .. note:: + .. NOTE:: - scalar * vector is implemented (and tested) in class RingElement - matrix * vector is implemented (and tested) in class Matrix + scalar * vector is implemented (and tested) in class RingElement + matrix * vector is implemented (and tested) in class Matrix TESTS: @@ -2576,10 +2576,10 @@ cdef class Matrix(ModuleElement): - Gonzalo Tornaria (2007-06-25) - write test cases and fix them - .. note:: + .. NOTE:: - scalar * matrix is implemented (and tested) in class RingElement - vector * matrix is implemented (and tested) in class Vector + scalar * matrix is implemented (and tested) in class RingElement + vector * matrix is implemented (and tested) in class Vector TESTS: @@ -2817,7 +2817,7 @@ def is_PrincipalIdealDomainElement(x): cdef class PrincipalIdealDomainElement(DedekindDomainElement): def lcm(self, right): """ - Returns the least common multiple of ``self`` and right. + Return the least common multiple of ``self`` and right. """ if not PY_TYPE_CHECK(right, Element) or not ((right)._parent is self._parent): return coercion_model.bin_op(self, right, lcm) @@ -2900,7 +2900,7 @@ cdef class FieldElement(CommutativeRingElement): return self / other def is_unit(self): - """ + r""" Return ``True`` if ``self`` is a unit in its parent ring. EXAMPLES:: @@ -2908,7 +2908,7 @@ cdef class FieldElement(CommutativeRingElement): sage: a = 2/3; a.is_unit() True - On the other hand, 2 is not a unit, since its parent is ZZ. + On the other hand, 2 is not a unit, since its parent is `\ZZ`. :: @@ -3288,7 +3288,7 @@ cdef class NamedBinopMethod: def _sage_src_(self): """ - Returns the source of the wrapped object for introspection. + Return the source of the wrapped object for introspection. EXAMPLES:: @@ -3301,7 +3301,7 @@ cdef class NamedBinopMethod: def _sage_argspec_(self): """ - Returns the argspec of the wrapped object for introspection. + Return the argspec of the wrapped object for introspection. EXAMPLES:: From 431acb6362656c099dfa262fb5ee7d06cb310e64 Mon Sep 17 00:00:00 2001 From: kcrisman Date: Mon, 15 Sep 2014 12:31:49 -0400 Subject: [PATCH 021/217] Trivial fix for typesetting line_graph.py Missing colon. --- src/sage/graphs/line_graph.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/graphs/line_graph.py b/src/sage/graphs/line_graph.py index c9de934d4bf..3b0af720827 100644 --- a/src/sage/graphs/line_graph.py +++ b/src/sage/graphs/line_graph.py @@ -190,7 +190,7 @@ def is_line_graph(g, certificate = False): True The Petersen Graph not being claw-free, it is not a line - graph: + graph:: sage: graphs.PetersenGraph().is_line_graph() False From 27b4e34ac45bf1d847bf947e2183841dcb8e71a1 Mon Sep 17 00:00:00 2001 From: kcrisman Date: Mon, 15 Sep 2014 14:22:11 -0400 Subject: [PATCH 022/217] Minor typesetting issue in graph.py Code should have two backticks, not one. --- src/sage/graphs/graph.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/graphs/graph.py b/src/sage/graphs/graph.py index a946bcb5557..51585edea5d 100644 --- a/src/sage/graphs/graph.py +++ b/src/sage/graphs/graph.py @@ -6340,11 +6340,11 @@ def is_prime(self): r""" Tests whether the current graph is prime. A graph is prime if all its modules are trivial (i.e. empty, all of the graph or - singletons)-- see `self.modular_decomposition?`. + singletons)-- see ``self.modular_decomposition?``. EXAMPLE: - The Petersen Graph and the Bull Graph are both prime :: + The Petersen Graph and the Bull Graph are both prime:: sage: graphs.PetersenGraph().is_prime() True From 429b5e3e2b04c0801a21162b6588aa2099bc01c1 Mon Sep 17 00:00:00 2001 From: Joao de Faria Date: Wed, 15 Oct 2014 21:33:48 -0400 Subject: [PATCH 023/217] Fixed logic to check for number fields --- src/sage/schemes/projective/projective_morphism.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/sage/schemes/projective/projective_morphism.py b/src/sage/schemes/projective/projective_morphism.py index 5506481b7d2..105804d2157 100644 --- a/src/sage/schemes/projective/projective_morphism.py +++ b/src/sage/schemes/projective/projective_morphism.py @@ -39,6 +39,7 @@ # http://www.gnu.org/licenses/ #***************************************************************************** +from sage.categories.number_fields import NumberFields from sage.categories.homset import Hom from sage.functions.all import sqrt from sage.libs.pari.gen import PariError @@ -1657,8 +1658,9 @@ def height_difference_bound(self, prec=None): sage: f.height_difference_bound() 10.7632079329219 """ - if self.domain().base_ring() != ZZ and self.domain().base_ring() != QQ: - raise NotImplementedError("Must be over ZZ or QQ") + BR = self.domain().base_ring() + if not BR in NumberFields: + raise NotImplemenetedError("Must be a number field") if not self.is_endomorphism(): raise NotImplementedError("Must be an endomorphism of projective space") if prec is None: @@ -1672,8 +1674,11 @@ def height_difference_bound(self, prec=None): U = self.global_height(prec) + R(binomial(N + d, d)).log() #compute lower bound - from explicit polynomials of Nullstellensatz CR = self.domain().coordinate_ring() - CR = CR.change_ring(QQ) #.lift() only works over fields - I = CR.ideal(self.defining_polynomials()) + try: + Def_polys = BR.defining_polynomial() + except AttributeError: + Def_polys = [] + I = CR.ideal(self.defining_polynomials(), Def_polys) MCP = [] for k in range(N + 1): CoeffPolys = (CR.gen(k) ** D).lift(I) From d4bdfcab040c79a63125695de8835e875ca9c236 Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Thu, 16 Oct 2014 20:17:29 +0200 Subject: [PATCH 024/217] Calling the .n() method on reals shouldn't increase precision --- src/sage/functions/exp_integral.py | 12 ++-- src/sage/interfaces/tides.py | 14 ++--- src/sage/misc/functional.py | 75 +++++++++++++++++++----- src/sage/modules/free_module_element.pyx | 9 --- src/sage/rings/complex_number.pyx | 3 +- src/sage/rings/real_mpfr.pyx | 43 +++++++++++++- 6 files changed, 117 insertions(+), 39 deletions(-) diff --git a/src/sage/functions/exp_integral.py b/src/sage/functions/exp_integral.py index 521bd36a515..c5d6e76e692 100644 --- a/src/sage/functions/exp_integral.py +++ b/src/sage/functions/exp_integral.py @@ -996,8 +996,8 @@ def _evalf_(self, z, parent=None, algorithm=None): sage: N(cos_integral(1e23)) < 1e-20 True - sage: N(cos_integral(1e-10), digits=30) - -22.4486352650389235918737540487 + sage: N(cos_integral(10^-10), digits=30) + -22.4486352650389239795759024568 sage: cos_integral(ComplexField(100)(I)) 0.83786694098020824089467857943 + 1.5707963267948966192313216916*I @@ -1152,8 +1152,8 @@ def _evalf_(self, z, parent=None, algorithm=None): """ EXAMPLES:: - sage: N(sinh_integral(1e-10), digits=30) - 1.00000000000000003643219731550e-10 + sage: N(sinh_integral(10^-10), digits=30) + 1.00000000000000000000055555556e-10 sage: sinh_integral(ComplexField(100)(I)) 0.94608307036718301494135331382*I @@ -1289,8 +1289,8 @@ def _evalf_(self, z, parent=None, algorithm=None): """ EXAMPLES:: - sage: N(cosh_integral(1e-10), digits=30) - -22.4486352650389235918737540487 + sage: N(cosh_integral(10^-10), digits=30) + -22.4486352650389239795709024568 sage: cosh_integral(ComplexField(100)(I)) 0.33740392290096813466264620389 + 1.5707963267948966192313216916*I diff --git a/src/sage/interfaces/tides.py b/src/sage/interfaces/tides.py index 87451c8f031..6b97ca3e8e9 100644 --- a/src/sage/interfaces/tides.py +++ b/src/sage/interfaces/tides.py @@ -680,7 +680,7 @@ def genfiles_mpfr(integrator, driver, f, ics, initial, final, delta, sage: l[16] ' int nfun = 0;\n' sage: l[26] - '\tmpfr_set_str(v[2], "0.00000000000000000000000000000000000000000000000000", 10, TIDES_RND);\n' + '\tmpfr_set_str(v[2], "0", 10, TIDES_RND);\n' sage: l[30] '\tmpfr_init2(tolabs, TIDES_PREC); \n' sage: l[34] @@ -868,24 +868,24 @@ def genfiles_mpfr(integrator, driver, f, ics, initial, final, delta, outfile.write('\tfor(i=0; i 53 and CDF.has_coerce_map_from(B): + # If we can coerce to CDF, assume input precision was 53 bits + inprec = 53 + else: + # Otherwise, assume precision wasn't the issue + inprec = prec + + if prec > inprec: + raise TypeError("cannot approximate to a precision of %s bits, use at most %s bits" % (prec, inprec)) + + # The issue is not precision, try conversion instead + try: + return RR(x) + except (TypeError, ValueError): + pass + return CC(x) + n = numerical_approx N = numerical_approx diff --git a/src/sage/modules/free_module_element.pyx b/src/sage/modules/free_module_element.pyx index 842263266cb..bf6f4f4541e 100644 --- a/src/sage/modules/free_module_element.pyx +++ b/src/sage/modules/free_module_element.pyx @@ -1067,20 +1067,11 @@ cdef class FreeModuleElement(element_Vector): # abstract base class (1.00000000000000, 2.00000000000000, 3.00000000000000) sage: _.parent() Vector space of dimension 3 over Real Field with 53 bits of precision - sage: v.n(prec=75) - (1.000000000000000000000, 2.000000000000000000000, 3.000000000000000000000) - sage: _.parent() - Vector space of dimension 3 over Real Field with 75 bits of precision - sage: v = vector(CDF, [1,2,3]) sage: v.n() (1.00000000000000, 2.00000000000000, 3.00000000000000) sage: _.parent() Vector space of dimension 3 over Complex Field with 53 bits of precision - sage: v.n(prec=75) - (1.000000000000000000000, 2.000000000000000000000, 3.000000000000000000000) - sage: _.parent() - Vector space of dimension 3 over Complex Field with 75 bits of precision sage: v = vector(Integers(8), [1,2,3]) sage: v.n() diff --git a/src/sage/rings/complex_number.pyx b/src/sage/rings/complex_number.pyx index 378b2dc0a3b..597bed321a9 100644 --- a/src/sage/rings/complex_number.pyx +++ b/src/sage/rings/complex_number.pyx @@ -590,7 +590,8 @@ cdef class ComplexNumber(sage.structure.element.FieldElement): mpc(real='1.0', imag='2.0') """ if prec is not None: - return self.n(prec=prec)._mpmath_() + from complex_field import ComplexField + return ComplexField(prec)(self)._mpmath_() from sage.libs.mpmath.all import make_mpc re = mpfr_to_mpfval(self.__re) im = mpfr_to_mpfval(self.__im) diff --git a/src/sage/rings/real_mpfr.pyx b/src/sage/rings/real_mpfr.pyx index 14c6f809e5a..374f235ec25 100644 --- a/src/sage/rings/real_mpfr.pyx +++ b/src/sage/rings/real_mpfr.pyx @@ -3156,7 +3156,7 @@ cdef class RealNumber(sage.structure.element.RingElement): mpf('-1.5') """ if prec is not None: - return self.n(prec=prec)._mpmath_() + return RealField(prec)(self)._mpmath_() from sage.libs.mpmath.all import make_mpf return make_mpf(mpfr_to_mpfval(self.value)) @@ -5414,6 +5414,47 @@ cdef class RealLiteral(RealNumber): else: return RealLiteral(self._parent, '-'+self.literal, self.base) + def _numerical_approx(self, prec=53, algorithm=None): + """ + Convert ``self`` to a ``RealField`` with ``prec`` bits of + precision. + + INPUT: + + - ``prec`` -- (default: 53) a precision in bits + + - ``algorithm`` -- ignored + + OUTPUT: + + A ``RealNumber`` with ``prec`` bits of precision. + + EXAMPLES:: + + sage: (1.3)._numerical_approx() + 1.30000000000000 + sage: n(1.3, 120) + 1.3000000000000000000000000000000000 + + Compare with:: + + sage: RealField(120)(RR(13/10)) + 1.3000000000000000444089209850062616 + sage: n(RR(13/10), 120) + Traceback (most recent call last): + ... + TypeError: cannot approximate to a precision of 120 bits, use at most 53 bits + + The result is a non-literal:: + + sage: type(1.3) + + sage: type(n(1.3)) + + """ + return RealField(prec)(self.literal) + + RR = RealField() RR_min_prec = RealField(MPFR_PREC_MIN) From 4a0f1277de964fc01b351b9f85e83998adcf80fb Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Mon, 20 Oct 2014 21:15:09 +0200 Subject: [PATCH 025/217] Remove gmp_globals.c, gmp_globals.h and gmp.pxi --- src/c_lib/SConstruct | 4 +- src/c_lib/include/gmp_globals.h | 24 --------- src/c_lib/src/gmp_globals.c | 50 ------------------- src/sage/all.py | 1 - src/sage/combinat/expnums.pyx | 1 - src/sage/ext/gmp.pxi | 47 ----------------- src/sage/ext/multi_modular.pyx | 1 - src/sage/matrix/matrix_cyclo_dense.pyx | 2 - src/sage/quadratic_forms/count_local_2.pyx | 1 - src/sage/rings/factorint.pyx | 1 - src/sage/rings/fast_arith.pyx | 1 - src/sage/rings/fraction_field_FpT.pyx | 1 - src/sage/rings/integer.pyx | 10 +--- src/sage/rings/integer_ring.pyx | 1 - .../rings/padics/padic_generic_element.pyx | 1 - src/sage/rings/padics/padic_printing.pyx | 1 - src/sage/rings/padics/pow_computer.pyx | 1 - src/sage/rings/padics/pow_computer_ext.pyx | 1 - .../polynomial_integer_dense_flint.pyx | 16 +++--- src/sage/rings/polynomial/real_roots.pyx | 1 - .../universal_cyclotomic_field_c.pyx | 1 - 21 files changed, 9 insertions(+), 158 deletions(-) delete mode 100644 src/c_lib/include/gmp_globals.h delete mode 100644 src/c_lib/src/gmp_globals.c delete mode 100644 src/sage/ext/gmp.pxi diff --git a/src/c_lib/SConstruct b/src/c_lib/SConstruct index 18092c8d9aa..29eef02fd81 100644 --- a/src/c_lib/SConstruct +++ b/src/c_lib/SConstruct @@ -129,10 +129,10 @@ env['PYV']=platform.python_version().rsplit('.', 1)[0] includes = ['$SAGE_LOCAL/include/', '$SAGE_LOCAL/include/python$PYV/', '$SAGE_LOCAL/include/NTL/', 'include'] cFiles = Split( "convert.c interrupt.c memory.c mpn_pylong.c mpz_pylong.c") + \ - Split( "mpz_longlong.c stdsage.c gmp_globals.c" ) + Split( "mpz_longlong.c stdsage.c" ) cppFiles = Split( "ZZ_pylong.cpp ntl_wrap.cpp" ) srcFiles = cFiles + cppFiles -incFiles = Split( "ccobject.h convert.h gmp_globals.h" ) + \ +incFiles = Split( "ccobject.h convert.h" ) + \ Split( "interrupt.h memory.h mpn_pylong.h mpz_longlong.h" ) + \ Split( "mpz_pylong.h ntl_wrap.h parisage.h stdsage.h ZZ_pylong.h" ) diff --git a/src/c_lib/include/gmp_globals.h b/src/c_lib/include/gmp_globals.h deleted file mode 100644 index c89583ba1d0..00000000000 --- a/src/c_lib/include/gmp_globals.h +++ /dev/null @@ -1,24 +0,0 @@ -#include - -#ifdef __cplusplus -#define EXTERN extern "C" -#else -#define EXTERN extern -#endif - -// these vars are all used in rational reconstruction; they're cached so we don't -// have to recreate them with every call. -EXTERN mpz_t u, v, q, u0, u1, u2, v0, v1, v2, t0, t1, t2, x, y, ssqr, m2; -EXTERN mpq_t tmp; - -EXTERN mpz_t a1, a2, mod1, sage_mod2, g, s, t, xx; - -EXTERN mpz_t crtrr_a, crtrr_mod; - -EXTERN mpz_t rand_val, rand_n, rand_n1; - -EXTERN gmp_randstate_t rand_state; - -EXTERN void init_mpz_globals(); -EXTERN void clear_mpz_globals(); - diff --git a/src/c_lib/src/gmp_globals.c b/src/c_lib/src/gmp_globals.c deleted file mode 100644 index d1283230c45..00000000000 --- a/src/c_lib/src/gmp_globals.c +++ /dev/null @@ -1,50 +0,0 @@ -#include "gmp_globals.h" - -mpz_t u, v, q, u0, u1, u2, v0, v1, v2, t0, t1, t2, x, y, ssqr, m2; -//changed sqr to ssqr due to a collision with ntl -mpq_t tmp; - -mpz_t a1, a2, mod1, sage_mod2, g, s, t, xx; - -mpz_t crtrr_a, crtrr_mod; - -mpz_t rand_val, rand_n, rand_n1; - -gmp_randstate_t rand_state; - -void init_mpz_globals() { - mpz_init(u); mpz_init(v); mpz_init(q); - mpz_init(u0); mpz_init(u1); mpz_init(u2); - mpz_init(v0); mpz_init(v1); mpz_init(v2); - mpz_init(t0); mpz_init(t1); mpz_init(t2); - mpz_init(x); mpz_init(y); - mpz_init(ssqr); mpz_init(m2); - mpq_init(tmp); - - mpz_init(a1); mpz_init(a2); mpz_init(mod1); mpz_init(sage_mod2); - mpz_init(g); mpz_init(s); mpz_init(t); mpz_init(xx); - - mpz_init(crtrr_a); mpz_init(crtrr_mod); - - mpz_init(rand_val); mpz_init(rand_n); mpz_init(rand_n1); - - gmp_randinit_default(rand_state); -} - -void clear_mpz_globals() { - mpz_clear(u); mpz_clear(v); mpz_clear(q); - mpz_clear(u0); mpz_clear(u1); mpz_clear(u2); - mpz_clear(v0); mpz_clear(v1); mpz_clear(v2); - mpz_clear(t0); mpz_clear(t1); mpz_clear(t2); - mpz_clear(x); mpz_clear(y); - mpz_clear(ssqr); mpz_clear(m2); - mpq_clear(tmp); - - mpz_clear(a1); mpz_clear(a2); mpz_clear(mod1); mpz_clear(sage_mod2); - mpz_clear(g); mpz_clear(s); mpz_clear(t); mpz_clear(xx); - - mpz_clear(crtrr_a); mpz_clear(crtrr_mod); - - mpz_clear(rand_val); mpz_clear(rand_n); mpz_clear(rand_n1); -} - diff --git a/src/sage/all.py b/src/sage/all.py index 72b79d68955..a2447c8f568 100644 --- a/src/sage/all.py +++ b/src/sage/all.py @@ -264,7 +264,6 @@ def quit_sage(verbose=True): # Free globally allocated mpir integers. import sage.rings.integer sage.rings.integer.free_integer_pool() - sage.rings.integer.clear_mpz_globals() import sage.algebras.quatalg.quaternion_algebra_element sage.algebras.quatalg.quaternion_algebra_element._clear_globals() diff --git a/src/sage/combinat/expnums.pyx b/src/sage/combinat/expnums.pyx index 3a038e0bcb6..5320a694663 100644 --- a/src/sage/combinat/expnums.pyx +++ b/src/sage/combinat/expnums.pyx @@ -9,7 +9,6 @@ AUTHORS: include "sage/ext/interrupt.pxi" include "sage/ext/stdsage.pxi" include "sage/ext/cdefs.pxi" -include "sage/ext/gmp.pxi" from sage.rings.integer cimport Integer diff --git a/src/sage/ext/gmp.pxi b/src/sage/ext/gmp.pxi deleted file mode 100644 index d71f3f609a6..00000000000 --- a/src/sage/ext/gmp.pxi +++ /dev/null @@ -1,47 +0,0 @@ -# gmp.inc -- misc. useful GMP functions that don't depend on -# other things and can be included in other files -# USAGE -# -# Include the following at the top of client .pyx file. -# -# include "gmp.pxi" -# -# to include this in a file. - -include 'sage/ext/interrupt.pxi' - -cimport libc.stdlib - -############ The following is the "one global set of vars" -cdef extern from "gmp_globals.h": - cdef mpz_t u, v, q, u0, u1, u2, v0, v1, v2, t0, t1, t2, x, y, ssqr, m2 - # changed sqr to ssqr due to a collision with ntl - cdef mpq_t tmp - - cdef mpz_t a1, a2, mod1, sage_mod2, g, s, t, xx - - cdef mpz_t crtrr_a, crtrr_mod - - cdef mpz_t rand_val, rand_n, rand_n1 - - cdef gmp_randstate_t rand_state - - void init_mpz_globals_c "init_mpz_globals"() - void clear_mpz_globals_c "clear_mpz_globals"() - -######################################################## - -cdef object mpz_to_str(mpz_t x): - """ - Convert a GMP integer to a Python string. - """ - cdef char *s - sig_on() - s = mpz_get_str(NULL, 10, x) - t = str(s) - # Emulate sage_free() to avoid needing to include stdsage.pxi - sig_block() - libc.stdlib.free(s) - sig_unblock() - sig_off() - return t diff --git a/src/sage/ext/multi_modular.pyx b/src/sage/ext/multi_modular.pyx index bffd7f04051..9ccb34acad1 100644 --- a/src/sage/ext/multi_modular.pyx +++ b/src/sage/ext/multi_modular.pyx @@ -14,7 +14,6 @@ Utility classes for multi-modular algorithms. include "sage/ext/interrupt.pxi" include "sage/ext/stdsage.pxi" -include "sage/ext/gmp.pxi" from sage.rings.integer_ring import ZZ diff --git a/src/sage/matrix/matrix_cyclo_dense.pyx b/src/sage/matrix/matrix_cyclo_dense.pyx index 87ff0d8fbd6..b627dda2ce7 100644 --- a/src/sage/matrix/matrix_cyclo_dense.pyx +++ b/src/sage/matrix/matrix_cyclo_dense.pyx @@ -35,9 +35,7 @@ AUTHORS: ###################################################################### include "sage/ext/interrupt.pxi" -# include "sage/ext/stdsage.pxi" include "sage/ext/cdefs.pxi" -include "sage/ext/gmp.pxi" include "sage/ext/random.pxi" include "sage/libs/ntl/decl.pxi" diff --git a/src/sage/quadratic_forms/count_local_2.pyx b/src/sage/quadratic_forms/count_local_2.pyx index 19e1d5ebe3b..2bd7d4aa520 100644 --- a/src/sage/quadratic_forms/count_local_2.pyx +++ b/src/sage/quadratic_forms/count_local_2.pyx @@ -3,7 +3,6 @@ Optimised Cython code for counting congruence solutions """ include "sage/ext/cdefs.pxi" -include "sage/ext/gmp.pxi" from sage.rings.arith import valuation, kronecker_symbol, is_prime from sage.rings.finite_rings.integer_mod import IntegerMod, Mod diff --git a/src/sage/rings/factorint.pyx b/src/sage/rings/factorint.pyx index b3e6b71f7c4..3deee5473e4 100644 --- a/src/sage/rings/factorint.pyx +++ b/src/sage/rings/factorint.pyx @@ -18,7 +18,6 @@ AUTHORS: #***************************************************************************** include "sage/libs/pari/decl.pxi" -include "sage/ext/gmp.pxi" include "sage/ext/stdsage.pxi" from sage.rings.integer cimport Integer diff --git a/src/sage/rings/fast_arith.pyx b/src/sage/rings/fast_arith.pyx index fdc848e29b0..d9bf7d59b3b 100644 --- a/src/sage/rings/fast_arith.pyx +++ b/src/sage/rings/fast_arith.pyx @@ -41,7 +41,6 @@ Basic arithmetic with c-integers. # The int definitions -include "sage/ext/gmp.pxi" include "sage/ext/stdsage.pxi" include "sage/libs/pari/decl.pxi" diff --git a/src/sage/rings/fraction_field_FpT.pyx b/src/sage/rings/fraction_field_FpT.pyx index 65d63818d16..0289fe782da 100644 --- a/src/sage/rings/fraction_field_FpT.pyx +++ b/src/sage/rings/fraction_field_FpT.pyx @@ -3,7 +3,6 @@ import sys include "sage/ext/cdefs.pxi" -include "sage/ext/gmp.pxi" include "sage/ext/interrupt.pxi" include "sage/ext/stdsage.pxi" diff --git a/src/sage/rings/integer.pyx b/src/sage/rings/integer.pyx index 0ac0ec56113..8d145f97d57 100644 --- a/src/sage/rings/integer.pyx +++ b/src/sage/rings/integer.pyx @@ -140,7 +140,6 @@ import operator import sys -include "sage/ext/gmp.pxi" include "sage/ext/interrupt.pxi" # ctrl-c interrupt block support include "sage/ext/stdsage.pxi" from cpython.list cimport * @@ -193,12 +192,6 @@ cdef object numpy_object_interface = {'typestr': '|O'} cdef mpz_t mpz_tmp mpz_init(mpz_tmp) -def init_mpz_globals(): - init_mpz_globals_c() - -def clear_mpz_globals(): - clear_mpz_globals_c() - cdef int set_mpz(Integer self, mpz_t value): mpz_set(self.value, value) @@ -3029,7 +3022,7 @@ cdef class Integer(sage.structure.element.EuclideanDomainElement): elif PY_TYPE_CHECK_EXACT(other, Integer): if mpz_sgn((other).value) == 0: raise ZeroDivisionError, "Integer division by zero" - if mpz_size((x).value) > 100000: + if mpz_size(self.value) > 100000: sig_on() mpz_fdiv_qr(q.value, r.value, self.value, (other).value) sig_off() @@ -6399,7 +6392,6 @@ cdef set_zero_one_elements(): if initialized: return the_integer_ring._zero_element = Integer(0) the_integer_ring._one_element = Integer(1) - init_mpz_globals() initialized = True set_zero_one_elements() diff --git a/src/sage/rings/integer_ring.pyx b/src/sage/rings/integer_ring.pyx index a12b0d45744..c488438749b 100644 --- a/src/sage/rings/integer_ring.pyx +++ b/src/sage/rings/integer_ring.pyx @@ -52,7 +52,6 @@ other types will also coerce to the integers, when it makes sense. ########################################################################### include "sage/ext/cdefs.pxi" -include "sage/ext/gmp.pxi" include "sage/ext/stdsage.pxi" include "sage/ext/interrupt.pxi" # ctrl-c interrupt block support include "sage/ext/random.pxi" diff --git a/src/sage/rings/padics/padic_generic_element.pyx b/src/sage/rings/padics/padic_generic_element.pyx index c4522bf7cb8..352f37f0e73 100644 --- a/src/sage/rings/padics/padic_generic_element.pyx +++ b/src/sage/rings/padics/padic_generic_element.pyx @@ -27,7 +27,6 @@ AUTHORS: # http://www.gnu.org/licenses/ #***************************************************************************** -include "sage/ext/gmp.pxi" include "sage/ext/interrupt.pxi" include "sage/ext/stdsage.pxi" diff --git a/src/sage/rings/padics/padic_printing.pyx b/src/sage/rings/padics/padic_printing.pyx index 55c808ee4bb..19be0b7ab28 100644 --- a/src/sage/rings/padics/padic_printing.pyx +++ b/src/sage/rings/padics/padic_printing.pyx @@ -23,7 +23,6 @@ AUTHORS: #***************************************************************************** include "sage/ext/stdsage.pxi" -include "sage/ext/gmp.pxi" from cpython.list cimport * diff --git a/src/sage/rings/padics/pow_computer.pyx b/src/sage/rings/padics/pow_computer.pyx index 329c5fb93a3..b75902f420f 100644 --- a/src/sage/rings/padics/pow_computer.pyx +++ b/src/sage/rings/padics/pow_computer.pyx @@ -35,7 +35,6 @@ AUTHORS: import weakref from sage.rings.infinity import infinity -include "sage/ext/gmp.pxi" include "sage/ext/interrupt.pxi" include "sage/ext/stdsage.pxi" diff --git a/src/sage/rings/padics/pow_computer_ext.pyx b/src/sage/rings/padics/pow_computer_ext.pyx index ae4983cd39a..079ea57976b 100644 --- a/src/sage/rings/padics/pow_computer_ext.pyx +++ b/src/sage/rings/padics/pow_computer_ext.pyx @@ -48,7 +48,6 @@ AUTHORS: # http://www.gnu.org/licenses/ #***************************************************************************** -include "sage/ext/gmp.pxi" include "sage/ext/interrupt.pxi" include "sage/ext/stdsage.pxi" from cpython.list cimport * diff --git a/src/sage/rings/polynomial/polynomial_integer_dense_flint.pyx b/src/sage/rings/polynomial/polynomial_integer_dense_flint.pyx index 22244760ecc..4aaa88dc0de 100644 --- a/src/sage/rings/polynomial/polynomial_integer_dense_flint.pyx +++ b/src/sage/rings/polynomial/polynomial_integer_dense_flint.pyx @@ -21,7 +21,6 @@ AUTHORS: include "sage/ext/stdsage.pxi" include "sage/ext/interrupt.pxi" -include "sage/ext/gmp.pxi" include "sage/libs/ntl/decl.pxi" from sage.rings.polynomial.polynomial_element cimport Polynomial @@ -451,19 +450,17 @@ cdef class Polynomial_integer_dense_flint(Polynomial): if name is None: name = self.parent().variable_name() cdef long i - cdef mpz_t coef - mpz_init(coef) + cdef Integer coef = PY_NEW(Integer) all = [] for i from fmpz_poly_degree(self.__poly) >= i >= 0: - fmpz_poly_get_coeff_mpz(coef, self.__poly, i) - sign = mpz_sgn(coef) - if sign: - if sign > 0: + fmpz_poly_get_coeff_mpz(coef.value, self.__poly, i) + if coef: + if coef > 0: sign_str = '+' - coeff_str = mpz_to_str(coef) + coeff_str = str(coef) else: sign_str = '-' - coeff_str = mpz_to_str(coef)[1:] + coeff_str = str(coef)[1:] if i > 0: if coeff_str == '1': coeff_str = '' @@ -480,7 +477,6 @@ cdef class Polynomial_integer_dense_flint(Polynomial): PyList_Append(all, " %s %s%s" % (sign_str, coeff_str, name)) else: PyList_Append(all, " %s %s" % (sign_str, coeff_str)) - mpz_clear(coef) if len(all) == 0: return '0' leading = all[0] diff --git a/src/sage/rings/polynomial/real_roots.pyx b/src/sage/rings/polynomial/real_roots.pyx index edf1c14c1a8..3002f28a4b5 100644 --- a/src/sage/rings/polynomial/real_roots.pyx +++ b/src/sage/rings/polynomial/real_roots.pyx @@ -155,7 +155,6 @@ cimport numpy from math import fabs include "sage/ext/cdefs.pxi" -include "sage/ext/gmp.pxi" from sage.libs.mpfr cimport * diff --git a/src/sage/rings/universal_cyclotomic_field/universal_cyclotomic_field_c.pyx b/src/sage/rings/universal_cyclotomic_field/universal_cyclotomic_field_c.pyx index ebd31e0aca5..ad31cd33e50 100644 --- a/src/sage/rings/universal_cyclotomic_field/universal_cyclotomic_field_c.pyx +++ b/src/sage/rings/universal_cyclotomic_field/universal_cyclotomic_field_c.pyx @@ -17,7 +17,6 @@ AUTHORS: #***************************************************************************** include "sage/ext/stdsage.pxi" include "sage/ext/cdefs.pxi" -include "sage/ext/gmp.pxi" import sys import operator From f65acdedf20554ed8cbd88fde232da4229d96f85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Fri, 31 Oct 2014 20:33:59 +0100 Subject: [PATCH 026/217] trac #16603 first review commit --- src/sage/matrix/matrix2.pyx | 12 +++---- src/sage/matrix/matrix_misc.py | 62 +++++++++++++++++++--------------- 2 files changed, 40 insertions(+), 34 deletions(-) diff --git a/src/sage/matrix/matrix2.pyx b/src/sage/matrix/matrix2.pyx index ff6bca4367a..b8b746e850f 100644 --- a/src/sage/matrix/matrix2.pyx +++ b/src/sage/matrix/matrix2.pyx @@ -900,7 +900,6 @@ cdef class Matrix(matrix1.Matrix): pm = pm + self.matrix_from_rows_and_columns(rows, cols).permanent() return pm - def rook_vector(self, check = False): r""" Return the rook vector of the matrix ``self``. @@ -952,7 +951,8 @@ cdef class Matrix(matrix1.Matrix): m = self._nrows n = self._ncols if not m <= n: - raise ValueError, "must have m <= n, but m (=%s) and n (=%s)"%(m,n) + raise ValueError("must have m <= n, but m (=%s) and n (=%s)" + % (m, n)) if check: # verify that self[i, j] in {0, 1} @@ -960,12 +960,11 @@ cdef class Matrix(matrix1.Matrix): for j in range(n): x = self.get_unsafe(i, j) if not (x == 0 or x == 1): - raise ValueError, "must have zero or one, but we have (=%s)"%x + raise ValueError("must have zero or one, but we have (=%s)" % x) - p = permanental_minor_vector(self) - return p + return permanental_minor_vector(self) - def minors(self,k): + def minors(self, k): r""" Return the list of all `k \times k` minors of self. @@ -14283,4 +14282,3 @@ def _jordan_form_vector_in_difference(V, W): if v not in W_space: return v return None - diff --git a/src/sage/matrix/matrix_misc.py b/src/sage/matrix/matrix_misc.py index 774acc85227..14b13231ef9 100644 --- a/src/sage/matrix/matrix_misc.py +++ b/src/sage/matrix/matrix_misc.py @@ -202,6 +202,11 @@ def weak_popov_form(M,ascend=True): return (matrix(r)/den, matrix(N), d) def _prm_mul(p1, p2, free_vars_indices, K): + """ + MISSING DOCUMENTATION HERE + + EXAMPLES:: + """ p = {} mask_free = 0 one = int(1) @@ -215,31 +220,37 @@ def _prm_mul(p1, p2, free_vars_indices, K): if exp1 & exp2: continue exp = exp1 | exp2 - v = v1*v2 + v = v1 * v2 if exp & mask_free: for i in free_vars_indices: - #print 'DB14 exp=%s i=%s' %(exp, i) if exp & (one << i): exp = exp.__xor__(one << i) - #print 'DB14b exp=%s' % exp p[exp] = get(exp, K.zero()) + v return p def _is_free_var(i, k, m): """ - i current row - k index of the variable - m matrix as a list of lists - returns true if the variable `k` does not occur from row `i` on + Return ``True`` if the variable `k` does not occur from row `i` on. + + INPUT: + + - i -- current row + - k -- index of the variable + - m -- matrix as a list of lists + + EXAMPLES:: + + sage: from sage.matrix.matrix_misc import _is_free_var + sage: m = [[1,2,3], [2,0,4], [2,0,4]] + sage: _is_free_var(1,1,m) + True """ - for j in range(i, len(m)): - if m[j][k] != 0: - return False - return True + return all(m[j][k] == 0 for j in range(i, len(m))) + def permanental_minor_vector(m, permanent_only=False): - """ + r""" Return the polynomial of the sums of permanental minors of a matrix `m` in array form. @@ -247,16 +258,15 @@ def permanental_minor_vector(m, permanent_only=False): .. MATH:: - \sum_0^{min(nrows, ncols)} p_i(m) x^i - - where `p_i(m) = m.permanental_minor(i)` + \sum_0^{min(nrows, ncols)} p_i(m) x^i + where `p_i(m)` is ``m.permanental_minor(i)``. INPUT: - - `m` - matrix + - `m` -- matrix - - `permanent_only` if True, only the permanent is computed + - `permanent_only` -- boolean, if ``True``, only the permanent is computed OUTPUT: @@ -273,20 +283,19 @@ def permanental_minor_vector(m, permanent_only=False): :: - sage: from sage.matrix.matrix_misc import permanental_minor_vector sage: M = MatrixSpace(ZZ,4,4) sage: A = M([1,0,1,0,1,0,1,0,1,0,10,10,1,0,1,1]) sage: permanental_minor_vector(A) [1, 28, 114, 84, 0] - :: + An example over `\QQ`:: sage: M = MatrixSpace(QQ,2,2) sage: A = M([1/5,2/7,3/2,4/5]) sage: permanental_minor_vector(A, 1) 103/175 - :: + An example with polynomial coefficients:: sage: R. = PolynomialRing(ZZ) sage: A = MatrixSpace(R,2)([[a,1], [a,a+1]]) @@ -295,15 +304,14 @@ def permanental_minor_vector(m, permanent_only=False): REFERENCES: - P. Butera and M. Pernici - ``Sums of permanental minors using Grassmann algebra'' - http://arxiv.org/abs/1406.5337 + .. [ButPer] P. Butera and M. Pernici "Sums of permanental minors + using Grassmann algebra", :arxiv:`1406.5337` """ K = PolynomialRing(m.base_ring(), 'K') m = list(m) nrows = len(m) ncols = len(m[0]) - p = {int(0):K.one()} + p = {int(0): K.one()} t = K.gen() done_vars = set() one = int(1) @@ -315,12 +323,12 @@ def permanental_minor_vector(m, permanent_only=False): a = m[i] for j in range(len(a)): if a[j]: - p1[one< Date: Wed, 5 Nov 2014 18:32:53 +0100 Subject: [PATCH 027/217] Added missing documentation in ``_prm_mul``; changed name of variable from 'K' to 't' in ``permanental_minor_vector``. --- src/sage/matrix/matrix_misc.py | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/src/sage/matrix/matrix_misc.py b/src/sage/matrix/matrix_misc.py index 14b13231ef9..90c70efa6d7 100644 --- a/src/sage/matrix/matrix_misc.py +++ b/src/sage/matrix/matrix_misc.py @@ -203,9 +203,29 @@ def weak_popov_form(M,ascend=True): def _prm_mul(p1, p2, free_vars_indices, K): """ - MISSING DOCUMENTATION HERE + Return the product of `p1` and `p2`, putting free variables to 1. + + In the following example, + ``p1 = 1 + t*e_0 + t*e_1`` + ``p2 = 1 + t*e_0 + t*e_2; 'e_0` free variable; + using the fact that `e_i` are nilpotent and setting + `e_0 = 1` after performing the product one gets + ``p1 * p2 = (1 + 2*t) + (t + t^2)*e_1 + (t + t^2)*e_2 + t^2*e_1*e_2`` + + The polynomials are represented in dictionary form; to a + variable ``eta_i`` it is associated the key ``1 << i``; + so in the following example 'e_1' corresponds to the key '2' + and 'e_1*e_2' to the key '6'. EXAMPLES:: + + sage: from sage.matrix.matrix_misc import _prm_mul + sage: K = PolynomialRing(ZZ, 't') + sage: t = K.gen() + sage: p1 = {0: 1, 1: t, 4: t} + sage: p2 = {0: 1, 1: t, 2: t} + sage: _prm_mul(p1, p2, [0], K) + {0: 2*t + 1, 2: t^2 + t, 4: t^2 + t, 6: t^2} """ p = {} mask_free = 0 @@ -307,7 +327,7 @@ def permanental_minor_vector(m, permanent_only=False): .. [ButPer] P. Butera and M. Pernici "Sums of permanental minors using Grassmann algebra", :arxiv:`1406.5337` """ - K = PolynomialRing(m.base_ring(), 'K') + K = PolynomialRing(m.base_ring(), 't') m = list(m) nrows = len(m) ncols = len(m[0]) From a59fe8caa1f0c7a272cbc1493fb65aac298e9608 Mon Sep 17 00:00:00 2001 From: Ralf Stephan Date: Mon, 10 Nov 2014 10:45:14 +0100 Subject: [PATCH 028/217] 17311: pass calls to Polynomial --- .../polynomial/polynomial_real_mpfr_dense.pyx | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/sage/rings/polynomial/polynomial_real_mpfr_dense.pyx b/src/sage/rings/polynomial/polynomial_real_mpfr_dense.pyx index 7df469769f2..061b6aa4701 100644 --- a/src/sage/rings/polynomial/polynomial_real_mpfr_dense.pyx +++ b/src/sage/rings/polynomial/polynomial_real_mpfr_dense.pyx @@ -641,7 +641,7 @@ cdef class PolynomialRealDense(Polynomial): else: return a * ~a[a.degree()] << min(aval, bval) - def __call__(self, xx): + def __call__(self, *args, **kwds): """ EXAMPLES:: @@ -669,16 +669,29 @@ cdef class PolynomialRealDense(Polynomial): sage: f = PolynomialRealDense(RR['x']) sage: f(12) 0.000000000000000 + + TESTS:: + + sage: R. = RR[] # :trac:`17311` + sage: (x^2+1)(x=5) + 26.0000000000000 """ + if len(args) == 1: + xx = args[0] + else: + return Polynomial.__call__(self, *args, **kwds) + if not PY_TYPE_CHECK(xx, RealNumber): if self._base_ring.has_coerce_map_from(parent(xx)): xx = self._base_ring(xx) else: return Polynomial.__call__(self, xx) + cdef Py_ssize_t i cdef mp_rnd_t rnd = self._base_ring.rnd cdef RealNumber x = xx cdef RealNumber res + if (x._parent).__prec < self._base_ring.__prec: res = RealNumber(x._parent) else: @@ -704,7 +717,7 @@ cdef class PolynomialRealDense(Polynomial): mpfr_mul(res.value, res.value, x.value, rnd) mpfr_add(res.value, res.value, self._coeffs[i], rnd) return res - + def change_ring(self, R): """ EXAMPLES:: From a524288bad0774624c4823c37203341041117d65 Mon Sep 17 00:00:00 2001 From: Joao de Faria Date: Mon, 10 Nov 2014 16:06:53 -0500 Subject: [PATCH 029/217] Adapted code to not have to check for numerator --- .../schemes/projective/projective_morphism.py | 28 ++++++++++--------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/src/sage/schemes/projective/projective_morphism.py b/src/sage/schemes/projective/projective_morphism.py index 105804d2157..9c122c700cf 100644 --- a/src/sage/schemes/projective/projective_morphism.py +++ b/src/sage/schemes/projective/projective_morphism.py @@ -1659,8 +1659,8 @@ def height_difference_bound(self, prec=None): 10.7632079329219 """ BR = self.domain().base_ring() - if not BR in NumberFields: - raise NotImplemenetedError("Must be a number field") + if not BR in NumberFields(): + raise NotImplementedError("Must be a number field") if not self.is_endomorphism(): raise NotImplementedError("Must be an endomorphism of projective space") if prec is None: @@ -1674,30 +1674,32 @@ def height_difference_bound(self, prec=None): U = self.global_height(prec) + R(binomial(N + d, d)).log() #compute lower bound - from explicit polynomials of Nullstellensatz CR = self.domain().coordinate_ring() - try: - Def_polys = BR.defining_polynomial() - except AttributeError: - Def_polys = [] - I = CR.ideal(self.defining_polynomials(), Def_polys) + CR = CR.change_ring(BR) #lift only works over fields + I = CR.ideal(self.defining_polynomials()) MCP = [] for k in range(N + 1): CoeffPolys = (CR.gen(k) ** D).lift(I) + print CoeffPolys Res = 1 - h = 1 for j in range(len(CoeffPolys)): if CoeffPolys[j] != 0: + #make this a list comprehension for i in range(len(CoeffPolys[j].coefficients())): Res = lcm(Res, abs(CoeffPolys[j].coefficients()[i].denominator())) - h = max(h, abs(CoeffPolys[j].coefficients()[i].numerator())) - MCP.append([Res, Res * h]) #since we need to clear denominators - maxh = 1 + h = max([c.global_height() for g in CoeffPolys for c in (Res*g).coefficients()]) + MCP.append([Res, h]) #since we need to clear denominators + print MCP + maxh = 0 gcdRes = 0 for k in range(len(MCP)): gcdRes = gcd(gcdRes, MCP[k][0]) maxh = max(maxh, MCP[k][1]) - L = abs(R(gcdRes / ((N + 1) * binomial(N + D - d, D - d) * maxh)).log()) - + print maxh + print R(gcdRes).log() + print R((N + 1) * binomial(N + D - d, D - d)).log() + L = abs( R(gcdRes).log() - R((N + 1) * binomial(N + D - d, D - d)).log() - maxh) C = max(U, L) #height difference dh(P) - L <= h(f(P)) <= dh(P) +U + print U,L return(C / (d - 1)) def multiplier(self, P, n, check=True): From f64e556c31802d06134265b225cc3f6cb715bf28 Mon Sep 17 00:00:00 2001 From: Joao de Faria Date: Wed, 12 Nov 2014 13:14:14 -0500 Subject: [PATCH 030/217] Deleted lines used for testing --- src/sage/schemes/projective/projective_morphism.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/sage/schemes/projective/projective_morphism.py b/src/sage/schemes/projective/projective_morphism.py index 9c122c700cf..984e6a0aec4 100644 --- a/src/sage/schemes/projective/projective_morphism.py +++ b/src/sage/schemes/projective/projective_morphism.py @@ -1688,18 +1688,13 @@ def height_difference_bound(self, prec=None): Res = lcm(Res, abs(CoeffPolys[j].coefficients()[i].denominator())) h = max([c.global_height() for g in CoeffPolys for c in (Res*g).coefficients()]) MCP.append([Res, h]) #since we need to clear denominators - print MCP maxh = 0 gcdRes = 0 for k in range(len(MCP)): gcdRes = gcd(gcdRes, MCP[k][0]) maxh = max(maxh, MCP[k][1]) - print maxh - print R(gcdRes).log() - print R((N + 1) * binomial(N + D - d, D - d)).log() L = abs( R(gcdRes).log() - R((N + 1) * binomial(N + D - d, D - d)).log() - maxh) C = max(U, L) #height difference dh(P) - L <= h(f(P)) <= dh(P) +U - print U,L return(C / (d - 1)) def multiplier(self, P, n, check=True): From 0b0f0785e23437afa504bd537fb69df974a7c8c5 Mon Sep 17 00:00:00 2001 From: Jean-Pierre Flori Date: Thu, 20 Nov 2014 03:19:33 -0800 Subject: [PATCH 031/217] Let ATLAS use Cygwin thread functions on Cygwin64. --- build/pkgs/atlas/patches/cygwin_threads.patch | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 build/pkgs/atlas/patches/cygwin_threads.patch diff --git a/build/pkgs/atlas/patches/cygwin_threads.patch b/build/pkgs/atlas/patches/cygwin_threads.patch new file mode 100644 index 00000000000..5f56db51160 --- /dev/null +++ b/build/pkgs/atlas/patches/cygwin_threads.patch @@ -0,0 +1,30 @@ +Let ATLAS use Cygwin thread functions on Cygwin64. +diff -druN ATLAS.orig/src/threads/ATL_thread_start.c ATLAS.new/src/threads/ATL_thread_start.c +--- ATLAS.orig/src/threads/ATL_thread_start.c 2014-07-10 09:22:06.000000000 -0700 ++++ ATLAS.new/src/threads/ATL_thread_start.c 2014-11-18 07:17:39.207997205 -0800 +@@ -14,14 +14,14 @@ + */ + { + #ifdef ATL_WINTHREADS +- #ifdef ATL_WIN32THREADS ++ #if defined(ATL_WIN32THREADS) || defined(__CYGWIN__) + DWORD thrID; + #else + unsigned thrID; + #endif + + #ifdef ATL_NOAFFINITY +- #ifdef ATL_WIN32THREADS ++ #if defined(ATL_WIN32THREADS) || defined(__CYGWIN__) + thr->thrH = CreateThread(NULL, 0, rout, arg, 0, &thrID); + #else + thr->thrH = (HANDLE)_beginthreadex(NULL, 0, rout, arg, 0, &thrID); +@@ -29,7 +29,7 @@ + ATL_assert(thr->thrH); + #else + thr->rank = proc; +- #ifdef ATL_WIN32THREADS ++ #if defined(ATL_WIN32THREADS) || defined(__CYGWIN__) + thr->thrH = CreateThread(NULL, 0, rout, arg, CREATE_SUSPENDED, &thrID); + #else + thr->thrH = (HANDLE)_beginthreadex(NULL, 0, rout, arg, From 3bea4e5a07c418528a77cc52b649aaa6c2c10782 Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Sun, 23 Nov 2014 15:10:56 -0600 Subject: [PATCH 032/217] trac #17385: replacement uniq -> set --- src/sage/graphs/digraph.py | 11 +++++------ src/sage/graphs/graph.py | 11 +++++------ 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/src/sage/graphs/digraph.py b/src/sage/graphs/digraph.py index fc0c9a9450e..ee0778a9dfc 100644 --- a/src/sage/graphs/digraph.py +++ b/src/sage/graphs/digraph.py @@ -571,7 +571,6 @@ def __init__(self, data=None, pos=None, loops=None, format=None, msg = '' GenericGraph.__init__(self) from sage.structure.element import is_Matrix - from sage.misc.misc import uniq if sparse == False: if data_structure != "sparse": @@ -738,7 +737,7 @@ def __init__(self, data=None, pos=None, loops=None, format=None, if multiedges is None: multiedges = False format = 'adjacency_matrix' if format == 'adjacency_matrix': - entries = uniq(data.list()) + entries = set(data.list()) for e in entries: try: e = int(e) @@ -773,7 +772,7 @@ def __init__(self, data=None, pos=None, loops=None, format=None, if len(NZ) != 2: msg += "There must be two nonzero entries (-1 & 1) per column." raise ValueError(msg) - L = sorted(uniq(c.list())) + L = sorted(set(c.list())) if L != [-1,0,1]: msg += "Each column represents an edge: -1 goes to 1." raise ValueError(msg) @@ -785,7 +784,7 @@ def __init__(self, data=None, pos=None, loops=None, format=None, if weighted is None: weighted = False if multiedges is None: total = len(positions) - multiedges = ( len(uniq(positions)) < total ) + multiedges = ( len(set(positions)) < total ) num_verts = data.nrows() elif format == 'DiGraph': if loops is None: loops = data.allows_loops() @@ -794,7 +793,7 @@ def __init__(self, data=None, pos=None, loops=None, format=None, if multiedges is None: multiedges = data.allows_multiple_edges() elif not multiedges: e = data.edges(labels=False) - if len(e) != len(uniq(e)): + if len(e) != len(set(e)): raise ValueError("No multiple edges but input digraph"+ " has multiple edges.") if weighted is None: weighted = data.weighted() @@ -848,7 +847,7 @@ def __init__(self, data=None, pos=None, loops=None, format=None, if loops is None: loops = False if weighted is None: weighted = False for u in data: - if len(uniq(data[u])) != len(data[u]): + if len(set(data[u])) != len(data[u]): if multiedges is False: v = (v for v in data[u] if data[u].count(v) > 1).next() raise ValueError("Non-multidigraph got several edges (%s,%s)"%(u,v)) diff --git a/src/sage/graphs/graph.py b/src/sage/graphs/graph.py index ca8d23a75dc..9c3ada32665 100644 --- a/src/sage/graphs/graph.py +++ b/src/sage/graphs/graph.py @@ -1116,7 +1116,6 @@ def __init__(self, data=None, pos=None, loops=None, format=None, GenericGraph.__init__(self) msg = '' from sage.structure.element import is_Matrix - from sage.misc.misc import uniq if sparse == False: if data_structure != "sparse": @@ -1333,7 +1332,7 @@ def __init__(self, data=None, pos=None, loops=None, format=None, if multiedges is None: multiedges = False format = 'adjacency_matrix' if format == 'adjacency_matrix': - entries = uniq(data.list()) + entries = set(data.list()) for e in entries: try: e = int(e) @@ -1377,7 +1376,7 @@ def __init__(self, data=None, pos=None, loops=None, format=None, raise ValueError(msg) else: positions.append(tuple(NZ)) - L = sorted(uniq(c.list())) + L = sorted(set(c.list())) if data.nrows() != (2 if len(NZ) == 2 else 1): desirable = [-1, 0, 1] if len(NZ) == 2 else [0, 1] @@ -1391,7 +1390,7 @@ def __init__(self, data=None, pos=None, loops=None, format=None, if weighted is None: weighted = False if multiedges is None: total = len(positions) - multiedges = ( len(uniq(positions)) < total ) + multiedges = ( len(set(positions)) < total ) num_verts = data.nrows() elif format == 'Graph': if loops is None: loops = data.allows_loops() @@ -1401,7 +1400,7 @@ def __init__(self, data=None, pos=None, loops=None, format=None, elif not multiedges: e = data.edges(labels=False) e = [sorted(f) for f in e] - if len(e) != len(uniq(e)): + if len(e) != len(set(e)): raise ValueError("No multiple edges but input graph"+ " has multiple edges.") if weighted is None: weighted = data.weighted() @@ -1460,7 +1459,7 @@ def __init__(self, data=None, pos=None, loops=None, format=None, if loops is None: loops = False if weighted is None: weighted = False for u in data: - if len(uniq(data[u])) != len(data[u]): + if len(set(data[u])) != len(data[u]): if multiedges is False: v = (v for v in data[u] if data[u].count(v) > 1).next() raise ValueError("Non-multigraph got several edges (%s,%s)"%(u,v)) From 3f8a98bc9caa9f64504cf2d85ce4ed39ea7ce12c Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Sun, 23 Nov 2014 15:38:13 -0600 Subject: [PATCH 033/217] trac #17385: various optimization --- src/sage/graphs/digraph.py | 127 +++++++++++++++++++++---------------- src/sage/graphs/graph.py | 67 +++++++++++-------- 2 files changed, 113 insertions(+), 81 deletions(-) diff --git a/src/sage/graphs/digraph.py b/src/sage/graphs/digraph.py index ee0778a9dfc..dde022017d4 100644 --- a/src/sage/graphs/digraph.py +++ b/src/sage/graphs/digraph.py @@ -101,6 +101,7 @@ """ from sage.rings.integer import Integer +from sage.rings.integer_ring import ZZ from sage.misc.superseded import deprecation import sage.graphs.generic_graph_pyx as generic_graph_pyx from sage.graphs.generic_graph import GenericGraph @@ -567,12 +568,20 @@ def __init__(self, data=None, pos=None, loops=None, format=None, Traceback (most recent call last): ... ValueError: Non-multidigraph got several edges (0,1) + + Check the error when the input has a loop but ``loops`` is set to False + (:trac:`17385`):: + + sage: DiGraph([(0,0)], loops=False) + Traceback (most recent call last): + ... + ValueError: No loops but dict has loop at 0. """ msg = '' GenericGraph.__init__(self) from sage.structure.element import is_Matrix - if sparse == False: + if sparse is False: if data_structure != "sparse": raise ValueError("The 'sparse' argument is an alias for " "'data_structure'. Please do not define both.") @@ -737,33 +746,37 @@ def __init__(self, data=None, pos=None, loops=None, format=None, if multiedges is None: multiedges = False format = 'adjacency_matrix' if format == 'adjacency_matrix': - entries = set(data.list()) - for e in entries: - try: - e = int(e) - assert e >= 0 - except Exception: - if weighted is False: - raise ValueError("Non-weighted digraph's"+ - " adjacency matrix must have only nonnegative"+ - " integer entries") - weighted = True - if multiedges is None: multiedges = False - break + if data.base_ring() != ZZ: + data = data.change_ring(ZZ) + if data.is_sparse(): + entries = set(data[i,j] for i,j in data.nonzero_positions()) + else: + entries = set(data.list()) + + if ((weighted is None or weighted is False) and + any(e < 0 for e in entries)): + if weighted is False: + raise ValueError("Non-weighted digraph's"+ + " adjacency matrix must have only nonnegative"+ + " integer entries") + weighted = True + if multiedges is None: multiedges = False if weighted is None: weighted = False if multiedges is None: - multiedges = ((not weighted) and sorted(entries) != [0,1]) - - for i in xrange(data.nrows()): - if data[i,i] != 0: - if loops is None: loops = True - elif not loops: - raise ValueError("Non-looped digraph's adjacency"+ - " matrix must have zeroes on the diagonal.") - break + multiedges = ((not weighted) and any(e != 0 and e != 1 for e in entries)) + + if ((loops is None or loops is False) and + any(data[i,i] for i in xrange(data.nrows()))): + if loops is False: + raise ValueError("Non-looped digraph's adjacency"+ + " matrix must have zeroes on the diagonal.") + loops = True + if loops is None: + loops = False + num_verts = data.nrows() elif format == 'incidence_matrix': positions = [] @@ -811,48 +824,54 @@ def __init__(self, data=None, pos=None, loops=None, format=None, elif format == 'dict_of_dicts': if not all(isinstance(data[u], dict) for u in data): raise ValueError("Input dict must be a consistent format.") - verts = set(data.keys()) - if loops is None or loops is False: - for u in data: - if u in data[u]: - if loops is None: loops = True - elif loops is False: - raise ValueError("No loops but dict has loops.") - break - if loops is None: loops = False + + if ((loops is None or loops is False) and + any(u in neighb for u,neighb in data.iteritems())): + if loops is False: + u = (u for u,neighb in data.iteritems() if u in neighb).next() + raise ValueError("No loops but dict has loop at {}.".format(u)) + loops = True + if loops is None: + loops = False + if weighted is None: weighted = False - for u in data: - for v in data[u]: - if v not in verts: verts.add(v) - if multiedges is not False and not isinstance(data[u][v], list): - if multiedges is None: multiedges = False - if multiedges: - raise ValueError("Dict of dicts for multidigraph must be in the format {v : {u : list}}") + + if ((multiedges is True or multiedges is None) and + not all(isinstance(neighb[v], list) for neighb in data.itervalues() for v in neighb)): + if multiedges is True: + raise ValueError("Dict of dicts for multidigraph must be in the format {v : {u : list}}") + multiedges = False if multiedges is None and len(data) > 0: multiedges = True + + verts = set().union(data.keys(), *data.values()) num_verts = len(verts) elif format == 'dict_of_lists': # convert to a dict of lists if not already one if not all(isinstance(data[u], list) for u in data): - data = dict([u, list(data[u])] for u in data) + data = {u: list(v) for u,v in data.iteritems()} + + if ((loops is None or loops is False) and + any(u in neighb for u,neighb in data.iteritems())): + if loops is False: + u = (u for u,neighb in data.iteritems() if u in neighb).next() + raise ValueError("No loops but dict has loop at {}.".format(u)) + loops = True + if loops is None: + loops = False - verts = set().union(data.keys(),*data.values()) - if loops is None or loops is False: - for u in data: - if u in data[u]: - if loops is None: loops = True - elif loops is False: - raise ValueError("No loops but dict has loops.") - break - if loops is None: loops = False if weighted is None: weighted = False - for u in data: - if len(set(data[u])) != len(data[u]): + + if ((multiedges is None or multiedges is False) and + any(len(set(neighb)) != len(neighb) for neighb in data.itervalues())): if multiedges is False: - v = (v for v in data[u] if data[u].count(v) > 1).next() + uv = ((u,v) for u,neighb in data.iteritems() for v in neighb if neighb.count(v) > 1).next() raise ValueError("Non-multidigraph got several edges (%s,%s)"%(u,v)) - if multiedges is None: multiedges = True - if multiedges is None: multiedges = False + multiedges = True + if multiedges is None: + multiedges = False + + verts = set().union(data.keys(),*data.values()) num_verts = len(verts) elif format == 'NX': if weighted is None: diff --git a/src/sage/graphs/graph.py b/src/sage/graphs/graph.py index 9c3ada32665..2d2bc47583b 100644 --- a/src/sage/graphs/graph.py +++ b/src/sage/graphs/graph.py @@ -1112,12 +1112,20 @@ def __init__(self, data=None, pos=None, loops=None, format=None, Traceback (most recent call last): ... ValueError: Non-multigraph got several edges (0,1) + + Check the error when the input has a loop but ``loops`` is set to False + (:trac:`17385`):: + + sage: Graph([(0,0)], loops=False) + Traceback (most recent call last): + ... + ValueError: No loops but dict has a loop at 0. """ GenericGraph.__init__(self) msg = '' from sage.structure.element import is_Matrix - if sparse == False: + if sparse is False: if data_structure != "sparse": raise ValueError("The 'sparse' argument is an alias for " "'data_structure'. Please do not define both.") @@ -1419,19 +1427,19 @@ def __init__(self, data=None, pos=None, loops=None, format=None, elif format == 'dict_of_dicts': if not all(isinstance(data[u], dict) for u in data): raise ValueError("Input dict must be a consistent format.") - verts = set(data.keys()) - if loops is None or loops is False: - for u in data: - if u in data[u]: - if loops is None: loops = True - elif loops is False: - raise ValueError("No loops but dict has loops.") - break - if loops is None: loops = False + + if ((loops is None or loops is False) and + any(u in neighb for u,neighb in data.iteritems())): + if loops is False: + u = (u for u,neighb in data.iteritems() if u in neighb).next() + raise ValueError("No loops but dict has loop at {}.".format(u)) + loops = True + if loops is None: + loops = False + if weighted is None: weighted = False for u in data: for v in data[u]: - if v not in verts: verts.add(v) if hash(u) > hash(v): if v in data and u in data[v]: if data[u][v] != data[v][u]: @@ -1443,28 +1451,34 @@ def __init__(self, data=None, pos=None, loops=None, format=None, raise ValueError("Dict of dicts for multigraph must be in the format {v : {u : list}}") if multiedges is None and len(data) > 0: multiedges = True + + verts = set().union(data.keys(), *data.values()) num_verts = len(verts) elif format == 'dict_of_lists': if not all(isinstance(data[u], list) for u in data): raise ValueError("Input dict must be a consistent format.") - verts = set().union(data.keys(),*data.values()) - if loops is None or loops is False: - for u in data: - if u in data[u]: - if loops is None: loops = True - elif loops is False: - raise ValueError("No loops but dict has loops.") - break - if loops is None: loops = False + if ((loops is None or loops is False) and + any(u in neighb for u,neighb in data.iteritems())): + if loops is False: + u = (u for u,neighb in data.iteritems() if u in neighb).next() + raise ValueError("No loops but dict has a loop at {}.".format(u)) + loops = True + if loops is None: + loops = False + if weighted is None: weighted = False - for u in data: - if len(set(data[u])) != len(data[u]): + + if ((multiedges is None or multiedges is False) and + any(len(set(neighb)) != len(neighb) for neighb in data.itervalues())): if multiedges is False: - v = (v for v in data[u] if data[u].count(v) > 1).next() - raise ValueError("Non-multigraph got several edges (%s,%s)"%(u,v)) - if multiedges is None: multiedges = True - if multiedges is None: multiedges = False + uv = ((u,v) for u,neighb in data.iteritems() for v in neighb if neighb.count(v) > 1).next() + raise ValueError("Non-multigraph got several edges (%s,%s)"%uv) + multiedges = True + if multiedges is None: + multiedges = False + + verts = set().union(data.keys(),*data.values()) num_verts = len(verts) elif format == 'NX': if weighted is None: @@ -6861,7 +6875,6 @@ def ihara_zeta_function_inverse(self): of the Ihara zeta function, Involve (http://msp.org/involve/2008/1-2/involve-v1-n2-p08-p.pdf) """ from sage.matrix.constructor import matrix - from sage.rings.integer_ring import ZZ from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing ring = PolynomialRing(ZZ, 't') From 0fa9c20b5cf44989b40726e5c740c82a564a5263 Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Sun, 23 Nov 2014 19:05:17 -0600 Subject: [PATCH 034/217] trac #17385: more cleanup after reviewer's comment --- src/sage/graphs/digraph.py | 56 +++++++++++++++++++------------- src/sage/graphs/graph.py | 66 +++++++++++++++++++++----------------- 2 files changed, 71 insertions(+), 51 deletions(-) diff --git a/src/sage/graphs/digraph.py b/src/sage/graphs/digraph.py index dde022017d4..64f1d8dcfd1 100644 --- a/src/sage/graphs/digraph.py +++ b/src/sage/graphs/digraph.py @@ -352,6 +352,15 @@ class DiGraph(GenericGraph): sage: DiGraph(M) Digraph on 5 vertices + sage: M = Matrix([[0,1,-1],[-1,0,-1/2],[1,1/2,0]]); M + [ 0 1 -1] + [ -1 0 -1/2] + [ 1 1/2 0] + sage: G = DiGraph(M,sparse=True); G + Digraph on 3 vertices + sage: G.weighted() + True + - an incidence matrix:: sage: M = Matrix(6, [-1,0,0,0,1, 1,-1,0,0,0, 0,1,-1,0,0, 0,0,1,-1,0, 0,0,0,1,-1, 0,0,0,0,0]); M @@ -746,16 +755,24 @@ def __init__(self, data=None, pos=None, loops=None, format=None, if multiedges is None: multiedges = False format = 'adjacency_matrix' if format == 'adjacency_matrix': - if data.base_ring() != ZZ: - data = data.change_ring(ZZ) + # note: the adjacency matrix might be weighted and hence not + # necessarily consists of integers + if not weighted and data.base_ring() != ZZ: + try: + data = data.change_ring(ZZ) + except TypeError: + if weighted is False: + raise ValueError("Non-weighted graph's"+ + " adjacency matrix must have only nonnegative"+ + " integer entries") + weighted = True if data.is_sparse(): entries = set(data[i,j] for i,j in data.nonzero_positions()) else: entries = set(data.list()) - if ((weighted is None or weighted is False) and - any(e < 0 for e in entries)): + if not weighted and any(e < 0 for e in entries): if weighted is False: raise ValueError("Non-weighted digraph's"+ " adjacency matrix must have only nonnegative"+ @@ -768,8 +785,7 @@ def __init__(self, data=None, pos=None, loops=None, format=None, if multiedges is None: multiedges = ((not weighted) and any(e != 0 and e != 1 for e in entries)) - if ((loops is None or loops is False) and - any(data[i,i] for i in xrange(data.nrows()))): + if not loops and any(data[i,i] for i in xrange(data.nrows())): if loops is False: raise ValueError("Non-looped digraph's adjacency"+ " matrix must have zeroes on the diagonal.") @@ -825,8 +841,7 @@ def __init__(self, data=None, pos=None, loops=None, format=None, if not all(isinstance(data[u], dict) for u in data): raise ValueError("Input dict must be a consistent format.") - if ((loops is None or loops is False) and - any(u in neighb for u,neighb in data.iteritems())): + if not loops and any(u in neighb for u,neighb in data.iteritems()): if loops is False: u = (u for u,neighb in data.iteritems() if u in neighb).next() raise ValueError("No loops but dict has loop at {}.".format(u)) @@ -836,8 +851,7 @@ def __init__(self, data=None, pos=None, loops=None, format=None, if weighted is None: weighted = False - if ((multiedges is True or multiedges is None) and - not all(isinstance(neighb[v], list) for neighb in data.itervalues() for v in neighb)): + if not multiedges and not all(isinstance(neighb[v], list) for neighb in data.itervalues() for v in neighb): if multiedges is True: raise ValueError("Dict of dicts for multidigraph must be in the format {v : {u : list}}") multiedges = False @@ -851,23 +865,21 @@ def __init__(self, data=None, pos=None, loops=None, format=None, if not all(isinstance(data[u], list) for u in data): data = {u: list(v) for u,v in data.iteritems()} - if ((loops is None or loops is False) and - any(u in neighb for u,neighb in data.iteritems())): - if loops is False: - u = (u for u,neighb in data.iteritems() if u in neighb).next() - raise ValueError("No loops but dict has loop at {}.".format(u)) - loops = True + if not loops and any(u in neighb for u,neighb in data.iteritems()): + if loops is False: + u = (u for u,neighb in data.iteritems() if u in neighb).next() + raise ValueError("No loops but dict has loop at {}.".format(u)) + loops = True if loops is None: loops = False if weighted is None: weighted = False - if ((multiedges is None or multiedges is False) and - any(len(set(neighb)) != len(neighb) for neighb in data.itervalues())): - if multiedges is False: - uv = ((u,v) for u,neighb in data.iteritems() for v in neighb if neighb.count(v) > 1).next() - raise ValueError("Non-multidigraph got several edges (%s,%s)"%(u,v)) - multiedges = True + if not multiedges and any(len(set(neighb)) != len(neighb) for neighb in data.itervalues()): + if multiedges is False: + uv = ((u,v) for u,neighb in data.iteritems() for v in neighb if neighb.count(v) > 1).next() + raise ValueError("Non-multidigraph got several edges (%s,%s)"%(u,v)) + multiedges = True if multiedges is None: multiedges = False diff --git a/src/sage/graphs/graph.py b/src/sage/graphs/graph.py index 2d2bc47583b..32ffcfc9eb0 100644 --- a/src/sage/graphs/graph.py +++ b/src/sage/graphs/graph.py @@ -1340,33 +1340,44 @@ def __init__(self, data=None, pos=None, loops=None, format=None, if multiedges is None: multiedges = False format = 'adjacency_matrix' if format == 'adjacency_matrix': - entries = set(data.list()) - for e in entries: + # note: the adjacency matrix might be weighted and hence not + # necessarily consists of integers + if not weighted and data.base_ring() != ZZ: try: - e = int(e) - assert e >= 0 - except Exception: + data = data.change_ring(ZZ) + except TypeError: if weighted is False: raise ValueError("Non-weighted graph's"+ " adjacency matrix must have only nonnegative"+ " integer entries") weighted = True - if multiedges is None: multiedges = False - break + if data.is_sparse(): + entries = set(data[i,j] for i,j in data.nonzero_positions()) + else: + entries = set(data.list()) + + if not weighted and any(e < 0 for e in entries): + if weighted is False: + raise ValueError("Non-weighted digraph's"+ + " adjacency matrix must have only nonnegative"+ + " integer entries") + weighted = True + if multiedges is None: multiedges = False if weighted is None: weighted = False if multiedges is None: - multiedges = ((not weighted) and sorted(entries) != [0,1]) + multiedges = ((not weighted) and any(e != 0 and e != 1 for e in entries)) + + if not loops and any(data[i,i] for i in xrange(data.nrows())): + if loops is False: + raise ValueError("Non-looped digraph's adjacency"+ + " matrix must have zeroes on the diagonal.") + loops = True + if loops is None: + loops = False - for i in xrange(data.nrows()): - if data[i,i] != 0: - if loops is None: loops = True - elif not loops: - raise ValueError("Non-looped graph's adjacency"+ - " matrix must have zeroes on the diagonal.") - break num_verts = data.nrows() elif format == 'incidence_matrix': positions = [] @@ -1428,8 +1439,7 @@ def __init__(self, data=None, pos=None, loops=None, format=None, if not all(isinstance(data[u], dict) for u in data): raise ValueError("Input dict must be a consistent format.") - if ((loops is None or loops is False) and - any(u in neighb for u,neighb in data.iteritems())): + if not loops and any(u in neighb for u,neighb in data.iteritems()): if loops is False: u = (u for u,neighb in data.iteritems() if u in neighb).next() raise ValueError("No loops but dict has loop at {}.".format(u)) @@ -1458,23 +1468,21 @@ def __init__(self, data=None, pos=None, loops=None, format=None, if not all(isinstance(data[u], list) for u in data): raise ValueError("Input dict must be a consistent format.") - if ((loops is None or loops is False) and - any(u in neighb for u,neighb in data.iteritems())): - if loops is False: - u = (u for u,neighb in data.iteritems() if u in neighb).next() - raise ValueError("No loops but dict has a loop at {}.".format(u)) - loops = True + if not loops and any(u in neighb for u,neighb in data.iteritems()): + if loops is False: + u = (u for u,neighb in data.iteritems() if u in neighb).next() + raise ValueError("No loops but dict has a loop at {}.".format(u)) + loops = True if loops is None: loops = False if weighted is None: weighted = False - if ((multiedges is None or multiedges is False) and - any(len(set(neighb)) != len(neighb) for neighb in data.itervalues())): - if multiedges is False: - uv = ((u,v) for u,neighb in data.iteritems() for v in neighb if neighb.count(v) > 1).next() - raise ValueError("Non-multigraph got several edges (%s,%s)"%uv) - multiedges = True + if not multiedges and any(len(set(neighb)) != len(neighb) for neighb in data.itervalues()): + if multiedges is False: + uv = ((u,v) for u,neighb in data.iteritems() for v in neighb if neighb.count(v) > 1).next() + raise ValueError("Non-multigraph got several edges (%s,%s)"%uv) + multiedges = True if multiedges is None: multiedges = False From 28038c2531aefe461d11c7da4bc02d043dd2f86a Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Mon, 13 Oct 2014 09:51:43 +0200 Subject: [PATCH 035/217] trac #17149: iOA with big holes through product of OA --- .../combinat/designs/orthogonal_arrays.py | 43 ++++++++++++++++--- 1 file changed, 36 insertions(+), 7 deletions(-) diff --git a/src/sage/combinat/designs/orthogonal_arrays.py b/src/sage/combinat/designs/orthogonal_arrays.py index 2f6f20449d4..ef1efd46ae6 100644 --- a/src/sage/combinat/designs/orthogonal_arrays.py +++ b/src/sage/combinat/designs/orthogonal_arrays.py @@ -1086,11 +1086,6 @@ def incomplete_orthogonal_array(k,n,holes_sizes,resolvable=False, existence=Fals - ``holes_sizes`` (list of integers) -- respective sizes of the holes to be found. - .. NOTE:: - - Right now the feature is only available when all holes have size 1, - i.e. `s_i=1`. - - ``resolvable`` (boolean) -- set to ``True`` if you want the design to be resolvable. The classes of the resolvable design are obtained as the first `n` blocks, then the next `n` blocks, etc ... Set to ``False`` by default. @@ -1106,8 +1101,15 @@ def incomplete_orthogonal_array(k,n,holes_sizes,resolvable=False, existence=Fals .. NOTE:: - By convention, the ground set is always `V = \{0, ..., n-1\}` and the - holes are `\{n-1, ..., n-s_1\}^k`, `\{n-s_1-1,...,n-s_1-s_2\}^k`, etc. + By convention, the ground set is always `V = \{0, ..., n-1\}`. + + If all holes have size 1, in the incomplete orthogonal array returned by + this function the holes are `\{n-1, ..., n-s_1\}^k`, + `\{n-s_1-1,...,n-s_1-s_2\}^k`, etc. + + More generally, if ``holes_sizes`` is equal to `u1,...,uk`, the `i`-th + hole is the set of points `\{n-\sum_{j\geq i}u_j,...,n-\sum_{j\geq + i+1}u_j\}^k`. .. SEEALSO:: @@ -1185,6 +1187,15 @@ def incomplete_orthogonal_array(k,n,holes_sizes,resolvable=False, existence=Fals ... NotImplementedError: I was not able to build this OA(6,36)-OA(6,8)-2.OA(6,9) + 10 holes of size 9 through the product construction:: + + sage: iOA = designs.incomplete_orthogonal_array(10,153,[9]*10) # long time + sage: OA9 = designs.orthogonal_arrays.build(10,9) + sage: for i in range(10): + ....: iOA.extend([[153-9*(i+1)+x for x in B] for B in OA9]) + sage: is_orthogonal_array(iOA,10,153) + True + REFERENCES: .. [BvR82] More mutually orthogonal Latin squares, @@ -1280,6 +1291,24 @@ def incomplete_orthogonal_array(k,n,holes_sizes,resolvable=False, existence=Fals OA = OA_from_quasi_difference_matrix(M,G,fill_hole=False) return [B[:k] for B in OA] + # Equal holes [h,h,...] with h>1 through OA product construction + # + # (i.e. OA(k,n1)-x.OA(k,1) and OA(k,n2) ==> OA(k,n1.n2)-x.OA(k,n2) ) + elif (not all_holes_of_size_1 and # h>1 + n%holes_sizes[0] == 0 and # h divides n + all(h==holes_sizes[0] for h in holes_sizes) and # holes of equal size + orthogonal_array(k,holes_sizes[0],existence=True) and # OA(k,h) + incomplete_orthogonal_array(k,n//holes_sizes[0],[1]*x,existence=True)): # OA(k,n/h)-x.OA(k,1) + if existence: + return True + from itertools import izip + h = holes_sizes[0] + iOA1 = incomplete_orthogonal_array(k,n//holes_sizes[0],[1]*x) + iOA2 = orthogonal_array(k,h) + + return [[B1[i]*h+B2[i] for i in range(k)] + for B1 in iOA1 + for B2 in iOA2] else: if existence: return Unknown From 478c6587dee3176250714979a995f39e8ffb49d3 Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Thu, 30 Oct 2014 12:52:21 +0100 Subject: [PATCH 036/217] trac #17149: y->sum_of_holes, x->number_of_holes, holes_sizes->holes --- .../combinat/designs/orthogonal_arrays.py | 66 +++++++++---------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/src/sage/combinat/designs/orthogonal_arrays.py b/src/sage/combinat/designs/orthogonal_arrays.py index ef1efd46ae6..d7aa61c5ffb 100644 --- a/src/sage/combinat/designs/orthogonal_arrays.py +++ b/src/sage/combinat/designs/orthogonal_arrays.py @@ -1064,7 +1064,7 @@ def largest_available_k(n,t=2): k += 1 return k -def incomplete_orthogonal_array(k,n,holes_sizes,resolvable=False, existence=False): +def incomplete_orthogonal_array(k,n,holes,resolvable=False, existence=False): r""" Return an `OA(k,n)-\sum_{1\leq i\leq x} OA(k,s_i)`. @@ -1083,8 +1083,7 @@ def incomplete_orthogonal_array(k,n,holes_sizes,resolvable=False, existence=Fals - ``k,n`` (integers) - - ``holes_sizes`` (list of integers) -- respective sizes of the holes to be - found. + - ``holes`` (list of integers) -- respective sizes of the holes to be found. - ``resolvable`` (boolean) -- set to ``True`` if you want the design to be resolvable. The classes of the resolvable design are obtained as the first @@ -1107,9 +1106,8 @@ def incomplete_orthogonal_array(k,n,holes_sizes,resolvable=False, existence=Fals this function the holes are `\{n-1, ..., n-s_1\}^k`, `\{n-s_1-1,...,n-s_1-s_2\}^k`, etc. - More generally, if ``holes_sizes`` is equal to `u1,...,uk`, the `i`-th - hole is the set of points `\{n-\sum_{j\geq i}u_j,...,n-\sum_{j\geq - i+1}u_j\}^k`. + More generally, if ``holes`` is equal to `u1,...,uk`, the `i`-th hole is + the set of points `\{n-\sum_{j\geq i}u_j,...,n-\sum_{j\geq i+1}u_j\}^k`. .. SEEALSO:: @@ -1204,24 +1202,26 @@ def incomplete_orthogonal_array(k,n,holes_sizes,resolvable=False, existence=Fals vol.39, num.3, pages 263-281 1982 """ - assert all(xx > 0 for xx in holes_sizes) + assert all(xx > 0 for xx in holes) from database import QDM - y = sum(holes_sizes) - x = len(holes_sizes) - all_holes_of_size_1 = (x==y) + OA = None - if y == 0: + sum_of_holes = sum(holes) + number_of_holes = len(holes) + all_holes_of_size_1 = (number_of_holes==sum_of_holes) + + if sum_of_holes == 0: return orthogonal_array(k,n,existence=existence,resolvable=resolvable) - if y > n: + if sum_of_holes > n: if existence: return False raise EmptySetError("The total size of holes must be smaller or equal than the size of the ground set") - if all_holes_of_size_1 and resolvable and y != n: + if all_holes_of_size_1 and resolvable and sum_of_holes != n: if existence: return False - raise EmptySetError("There is no resolvable incomplete OA({},{}) whose holes' sizes sum to {}!=n(={})".format(k,n,y,n)) + raise EmptySetError("There is no resolvable incomplete OA({},{}) whose holes' sizes sum to {}!=n(={})".format(k,n,sum_of_holes,n)) if all_holes_of_size_1 and resolvable: # n holes of size 1 --> equivalent to OA(k+1,n) if existence: @@ -1244,48 +1244,48 @@ def incomplete_orthogonal_array(k,n,holes_sizes,resolvable=False, existence=Fals return OA[:-n] # Easy case - elif all_holes_of_size_1 and x <= 1: + elif all_holes_of_size_1 and number_of_holes <= 1: if existence: return orthogonal_array(k,n,existence=True) OA = orthogonal_array(k,n) - independent_set = OA[:x] + independent_set = OA[:number_of_holes] - elif all_holes_of_size_1 and x <= 3 and n > k-1 and k >= 3 and existence: + elif all_holes_of_size_1 and number_of_holes <= 3 and n > k-1 and k >= 3 and existence: # This is lemma 2.3 from [BvR82]_ with u=1 return orthogonal_array(k,n,existence=True) - elif all_holes_of_size_1 and x >= 2 and k == n+1: + elif all_holes_of_size_1 and number_of_holes >= 2 and k == n+1: if existence: return False - raise EmptySetError("There is no OA(n+1,n) - {}.OA(n+1,1) as all blocks do intersect in a projective plane.".format(x)) + raise EmptySetError("There is no OA(n+1,n) - {}.OA(n+1,1) as all blocks do intersect in a projective plane.".format(number_of_holes)) # If we can build OA(k+1,n) then we can find n disjoint blocks in OA(k,n) elif all_holes_of_size_1 and orthogonal_array(k+1,n,existence=True): if existence: return True OA = orthogonal_array(k+1,n) - independent_set = [B[:-1] for B in OA if B[-1] == 0][:x] + independent_set = [B[:-1] for B in OA if B[-1] == 0][:number_of_holes] OA = [B[:-1] for B in OA] elif all_holes_of_size_1 and orthogonal_array(k,n,existence=True): OA = orthogonal_array(k,n) try: - independent_set = OA_find_disjoint_blocks(OA,k,n,x) + independent_set = OA_find_disjoint_blocks(OA,k,n,number_of_holes) except ValueError: if existence: return Unknown - raise NotImplementedError("I was not able to build this OA({},{})-{}.OA({},1)".format(k,n,x,k)) + raise NotImplementedError("I was not able to build this OA({},{})-{}.OA({},1)".format(k,n,number_of_holes,k)) if existence: return True - independent_set = OA_find_disjoint_blocks(OA,k,n,x) + independent_set = OA_find_disjoint_blocks(OA,k,n,number_of_holes) elif all_holes_of_size_1 and not orthogonal_array(k,n,existence=True): return orthogonal_array(k,n,existence=existence) # From a quasi-difference matrix - elif x==1 and any(uu==y and mu<=1 and lmbda==1 and k<=kk+1 for (nn,lmbda,mu,uu),(kk,_) in QDM.get((n,1),{}).iteritems()): + elif number_of_holes==1 and any(uu==sum_of_holes and mu<=1 and lmbda==1 and k<=kk+1 for (nn,lmbda,mu,uu),(kk,_) in QDM.get((n,1),{}).iteritems()): for (nn,lmbda,mu,uu),(kk,f) in QDM[n,1].iteritems(): - if uu==y and mu<=1 and lmbda==1 and k<=kk+1: + if uu==sum_of_holes and mu<=1 and lmbda==1 and k<=kk+1: break G,M = f() OA = OA_from_quasi_difference_matrix(M,G,fill_hole=False) @@ -1295,15 +1295,15 @@ def incomplete_orthogonal_array(k,n,holes_sizes,resolvable=False, existence=Fals # # (i.e. OA(k,n1)-x.OA(k,1) and OA(k,n2) ==> OA(k,n1.n2)-x.OA(k,n2) ) elif (not all_holes_of_size_1 and # h>1 - n%holes_sizes[0] == 0 and # h divides n - all(h==holes_sizes[0] for h in holes_sizes) and # holes of equal size - orthogonal_array(k,holes_sizes[0],existence=True) and # OA(k,h) - incomplete_orthogonal_array(k,n//holes_sizes[0],[1]*x,existence=True)): # OA(k,n/h)-x.OA(k,1) + n%holes[0] == 0 and # h divides n + all(h==holes[0] for h in holes) and # holes of equal size + orthogonal_array(k,holes[0],existence=True) and # OA(k,h) + incomplete_orthogonal_array(k,n//holes[0],[1]*number_of_holes,existence=True)): # OA(k,n/h)-x.OA(k,1) if existence: return True from itertools import izip - h = holes_sizes[0] - iOA1 = incomplete_orthogonal_array(k,n//holes_sizes[0],[1]*x) + h = holes[0] + iOA1 = incomplete_orthogonal_array(k,n//holes[0],[1]*number_of_holes) iOA2 = orthogonal_array(k,h) return [[B1[i]*h+B2[i] for i in range(k)] @@ -1315,10 +1315,10 @@ def incomplete_orthogonal_array(k,n,holes_sizes,resolvable=False, existence=Fals # format the list of holes from string import join f = lambda x: "" if x == 1 else "{}.".format(x) - holes_string = join(["-{}OA({},{})".format(f(holes_sizes.count(x)),k,x) for x in sorted(set(holes_sizes))],'') + holes_string = join(["-{}OA({},{})".format(f(holes.count(x)),k,x) for x in sorted(set(holes))],'') raise NotImplementedError("I was not able to build this OA({},{}){}".format(k,n,holes_string)) - assert x == len(independent_set) + assert number_of_holes == len(independent_set) for B in independent_set: OA.remove(B) From a9e7c28340317f10f64971b808e19288f533d2b6 Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Thu, 30 Oct 2014 14:58:38 +0100 Subject: [PATCH 037/217] trac #17149: Code cleaning --- .../combinat/designs/orthogonal_arrays.py | 73 +++++++++++-------- 1 file changed, 42 insertions(+), 31 deletions(-) diff --git a/src/sage/combinat/designs/orthogonal_arrays.py b/src/sage/combinat/designs/orthogonal_arrays.py index d7aa61c5ffb..0283a8fd01f 100644 --- a/src/sage/combinat/designs/orthogonal_arrays.py +++ b/src/sage/combinat/designs/orthogonal_arrays.py @@ -1145,8 +1145,7 @@ def incomplete_orthogonal_array(k,n,holes,resolvable=False, existence=False): sage: designs.incomplete_orthogonal_array(4,3,[1,1]) Traceback (most recent call last): ... - EmptySetError: There is no OA(n+1,n) - 2.OA(n+1,1) as all blocks do - intersect in a projective plane. + EmptySetError: There is no OA(n+1,n) - 2.OA(n+1,1) as all blocks intersect in a projective plane. sage: n=10 sage: k=designs.orthogonal_arrays.largest_available_k(n) sage: designs.incomplete_orthogonal_array(k,n,[1,1,1],existence=True) @@ -1176,7 +1175,7 @@ def incomplete_orthogonal_array(k,n,holes,resolvable=False, existence=False): sage: designs.incomplete_orthogonal_array(9,13,[1]*10,resolvable=True) Traceback (most recent call last): ... - EmptySetError: There is no resolvable incomplete OA(9,13) whose holes' sizes sum to 10!=n(=13) + EmptySetError: There is no resolvable incomplete OA(9,13) whose holes' sizes sum to 10 0 for xx in holes) - from database import QDM + from sage.combinat.designs.database import QDM + for h in holes: + if h<0: + raise ValueError("Holes must have size >=0, but {} was in the list").format(h) - OA = None + holes = [h for h in holes if h>0] - sum_of_holes = sum(holes) + if not holes: + return orthogonal_array(k,n,existence=existence,resolvable=resolvable) + + sum_of_holes = sum(holes) number_of_holes = len(holes) - all_holes_of_size_1 = (number_of_holes==sum_of_holes) + max_hole = max(holes) + min_hole = min(holes) - if sum_of_holes == 0: - return orthogonal_array(k,n,existence=existence,resolvable=resolvable) if sum_of_holes > n: if existence: return False raise EmptySetError("The total size of holes must be smaller or equal than the size of the ground set") - if all_holes_of_size_1 and resolvable and sum_of_holes != n: + if (max_hole == 1 and + resolvable and + sum_of_holes != n): if existence: return False - raise EmptySetError("There is no resolvable incomplete OA({},{}) whose holes' sizes sum to {}!=n(={})".format(k,n,sum_of_holes,n)) + raise EmptySetError("There is no resolvable incomplete OA({},{}) whose holes' sizes sum to {} equivalent to OA(k+1,n) + # resolvable OA(k,n)-n.OA(k,1) ==> equivalent to OA(k+1,n) + if max_hole==1 and resolvable: if existence: return orthogonal_array(k+1,n,existence=True) @@ -1244,30 +1250,36 @@ def incomplete_orthogonal_array(k,n,holes,resolvable=False, existence=False): return OA[:-n] # Easy case - elif all_holes_of_size_1 and number_of_holes <= 1: + elif max_hole==1 and number_of_holes <= 1: if existence: return orthogonal_array(k,n,existence=True) OA = orthogonal_array(k,n) independent_set = OA[:number_of_holes] - elif all_holes_of_size_1 and number_of_holes <= 3 and n > k-1 and k >= 3 and existence: - # This is lemma 2.3 from [BvR82]_ with u=1 + # This is lemma 2.3 from [BvR82]_ with u=1 + # + # TODO: handle larger holes too + elif (max_hole==1 and + existence and + number_of_holes <= 3 and + n > k-1 and k >= 3): return orthogonal_array(k,n,existence=True) - elif all_holes_of_size_1 and number_of_holes >= 2 and k == n+1: + elif max_hole==1 and number_of_holes >= 2 and k == n+1: if existence: return False - raise EmptySetError("There is no OA(n+1,n) - {}.OA(n+1,1) as all blocks do intersect in a projective plane.".format(number_of_holes)) + raise EmptySetError(("There is no OA(n+1,n) - {}.OA(n+1,1) as all blocks " + "intersect in a projective plane.").format(number_of_holes)) - # If we can build OA(k+1,n) then we can find n disjoint blocks in OA(k,n) - elif all_holes_of_size_1 and orthogonal_array(k+1,n,existence=True): + # Holes of size 1 from OA(k+1,n) + elif max_hole==1 and orthogonal_array(k+1,n,existence=True): if existence: return True OA = orthogonal_array(k+1,n) independent_set = [B[:-1] for B in OA if B[-1] == 0][:number_of_holes] OA = [B[:-1] for B in OA] - elif all_holes_of_size_1 and orthogonal_array(k,n,existence=True): + elif max_hole==1 and orthogonal_array(k,n,existence=True): OA = orthogonal_array(k,n) try: independent_set = OA_find_disjoint_blocks(OA,k,n,number_of_holes) @@ -1279,7 +1291,7 @@ def incomplete_orthogonal_array(k,n,holes,resolvable=False, existence=False): return True independent_set = OA_find_disjoint_blocks(OA,k,n,number_of_holes) - elif all_holes_of_size_1 and not orthogonal_array(k,n,existence=True): + elif max_hole==1 and not orthogonal_array(k,n,existence=True): return orthogonal_array(k,n,existence=existence) # From a quasi-difference matrix @@ -1294,15 +1306,14 @@ def incomplete_orthogonal_array(k,n,holes,resolvable=False, existence=False): # Equal holes [h,h,...] with h>1 through OA product construction # # (i.e. OA(k,n1)-x.OA(k,1) and OA(k,n2) ==> OA(k,n1.n2)-x.OA(k,n2) ) - elif (not all_holes_of_size_1 and # h>1 - n%holes[0] == 0 and # h divides n - all(h==holes[0] for h in holes) and # holes of equal size - orthogonal_array(k,holes[0],existence=True) and # OA(k,h) - incomplete_orthogonal_array(k,n//holes[0],[1]*number_of_holes,existence=True)): # OA(k,n/h)-x.OA(k,1) + elif (min_hole > 1 and + max_hole == min_hole and + n%min_hole == 0 and # h divides n + orthogonal_array(k,min_hole,existence=True) and # OA(k,h) + incomplete_orthogonal_array(k,n//min_hole,[1]*number_of_holes,existence=True)): # OA(k,n/h)-x.OA(k,1) if existence: return True - from itertools import izip - h = holes[0] + h = min_hole iOA1 = incomplete_orthogonal_array(k,n//holes[0],[1]*number_of_holes) iOA2 = orthogonal_array(k,h) @@ -1886,7 +1897,7 @@ def OA_from_PBD(k,n,PBD, check=True): sage: OA_from_PBD(4,10,pbd) Traceback (most recent call last): ... - EmptySetError: There is no OA(n+1,n) - 3.OA(n+1,1) as all blocks do intersect in a projective plane. + EmptySetError: There is no OA(n+1,n) - 3.OA(n+1,1) as all blocks intersect in a projective plane. Or an `OA(3,6)` (as the PBD has 10 points):: @@ -2119,7 +2130,7 @@ def is_available(k,n,t=2): INPUT: - ``k,n,t`` (integers) -- parameters of the orthogonal array. - +x .. SEEALSO:: :meth:`exists` From c3f7ddfc3695771b5998eb98fdfbb140b6e993d8 Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Mon, 1 Dec 2014 20:27:01 +0530 Subject: [PATCH 038/217] trac #17149: OA(10,1620) --- src/sage/combinat/designs/database.py | 33 ++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/src/sage/combinat/designs/database.py b/src/sage/combinat/designs/database.py index 17c6bdf519c..fc785395002 100644 --- a/src/sage/combinat/designs/database.py +++ b/src/sage/combinat/designs/database.py @@ -1916,6 +1916,36 @@ def OA_25_1262(): return OA_from_PBD(25,1262,PBD,check=False) +def OA_10_1620(): + r""" + Returns an OA(10,1620) + + This is obtained through the generalized Brouwer-van Rees + construction. Indeed, `1620 = 144.11+(36=4.9)` and there exists an + `OA(10,153) - OA(10,9)`. + + .. NOTE:: + + This function should be removed once + :func:`~sage.combinat.designs.orthogonal_arrays_find_recursive.find_brouwer_van_rees_with_one_truncated_column` + can handle all incomplete orthogonal arrays obtained through + :func:`~sage.combinat.designs.orthogonal_arrays.incomplete_orthogonal_array`. + + EXAMPLES:: + + sage: from sage.combinat.designs.designs_pyx import is_orthogonal_array + sage: from sage.combinat.designs.database import OA_10_1620 + sage: OA = OA_10_1620() # not tested -- ~7s + sage: print is_orthogonal_array(OA,10,1620,2) # not tested -- ~7s + True + + The design is available from the general constructor:: + + sage: designs.orthogonal_arrays.is_available(10,1620) + True + """ + return wilson_construction(None,10,11,144,[[(9,4)]]) + # Index of the OA constructions # # Associates to n the pair (k,f) where f() is a function that returns an OA(k,n) @@ -1952,7 +1982,8 @@ def OA_25_1262(): 640 : (11 , OA_11_640), 796 : (10 , OA_10_796), 896 : (15 , OA_15_896), - 1262 : (25 , OA_25_1262), + 1262 : (25, OA_25_1262), + 1620 : (10, OA_10_1620), } # Add this data to the module's doc LIST_OF_OA_CONSTRUCTIONS = join((":func:`OA({},{}) `".format(k,n,k,n) From 2330e61717599571938c23604ffbaae9a3b1e710 Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Mon, 1 Dec 2014 20:41:14 +0530 Subject: [PATCH 039/217] trac #17149: OA(9,1078) --- src/sage/combinat/designs/database.py | 31 +++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/sage/combinat/designs/database.py b/src/sage/combinat/designs/database.py index fc785395002..cd392748f45 100644 --- a/src/sage/combinat/designs/database.py +++ b/src/sage/combinat/designs/database.py @@ -1879,6 +1879,36 @@ def OA_15_896(): return OA_n_times_2_pow_c_from_matrix(15,7,FiniteField(7),zip(*A),Y,check=False) +def OA_9_1078(): + r""" + Returns an OA(9,1078) + + This is obtained through the generalized Brouwer-van Rees + construction. Indeed, `1078 = 89.11 + (99=9.11)` and there exists an + `OA(9,100) - OA(9,11)`. + + .. NOTE:: + + This function should be removed once + :func:`~sage.combinat.designs.orthogonal_arrays_find_recursive.find_brouwer_van_rees_with_one_truncated_column` + can handle all incomplete orthogonal arrays obtained through + :func:`~sage.combinat.designs.orthogonal_arrays.incomplete_orthogonal_array`. + + EXAMPLES:: + + sage: from sage.combinat.designs.designs_pyx import is_orthogonal_array + sage: from sage.combinat.designs.database import OA_9_1078 + sage: OA = OA_9_1078() # not tested -- ~3s + sage: print is_orthogonal_array(OA,9,1078,2) # not tested -- ~3s + True + + The design is available from the general constructor:: + + sage: designs.orthogonal_arrays.is_available(9,1078) + True + """ + return wilson_construction(None,9,11,89,[[(11,9)]]) + def OA_25_1262(): r""" Returns an OA(25,1262) @@ -1982,6 +2012,7 @@ def OA_10_1620(): 640 : (11 , OA_11_640), 796 : (10 , OA_10_796), 896 : (15 , OA_15_896), + 1078 : (9 , OA_9_1078), 1262 : (25, OA_25_1262), 1620 : (10, OA_10_1620), } From da421999df22d2a0c851630eb6c9fd33b448d0c3 Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Mon, 1 Dec 2014 20:45:36 +0530 Subject: [PATCH 040/217] trac #17149: OA(9,1612) --- src/sage/combinat/designs/database.py | 31 +++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/sage/combinat/designs/database.py b/src/sage/combinat/designs/database.py index cd392748f45..c28ea1c1871 100644 --- a/src/sage/combinat/designs/database.py +++ b/src/sage/combinat/designs/database.py @@ -1946,6 +1946,36 @@ def OA_25_1262(): return OA_from_PBD(25,1262,PBD,check=False) +def OA_9_1612(): + r""" + Returns an OA(9,1612) + + This is obtained through the generalized Brouwer-van Rees + construction. Indeed, `1612 = 89.17 + (99=9.11)` and there exists an + `OA(9,100) - OA(9,11)`. + + .. NOTE:: + + This function should be removed once + :func:`~sage.combinat.designs.orthogonal_arrays_find_recursive.find_brouwer_van_rees_with_one_truncated_column` + can handle all incomplete orthogonal arrays obtained through + :func:`~sage.combinat.designs.orthogonal_arrays.incomplete_orthogonal_array`. + + EXAMPLES:: + + sage: from sage.combinat.designs.designs_pyx import is_orthogonal_array + sage: from sage.combinat.designs.database import OA_9_1612 + sage: OA = OA_9_1612() # not tested -- ~6s + sage: print is_orthogonal_array(OA,9,1612,2) # not tested -- ~6s + True + + The design is available from the general constructor:: + + sage: designs.orthogonal_arrays.is_available(9,1612) + True + """ + return wilson_construction(None,9,17,89,[[(11,9)]]) + def OA_10_1620(): r""" Returns an OA(10,1620) @@ -2014,6 +2044,7 @@ def OA_10_1620(): 896 : (15 , OA_15_896), 1078 : (9 , OA_9_1078), 1262 : (25, OA_25_1262), + 1612 : (9 , OA_9_1612), 1620 : (10, OA_10_1620), } # Add this data to the module's doc From 32ae67c2fe24800943b883fe7f591bea784e5789 Mon Sep 17 00:00:00 2001 From: Ralf Stephan Date: Tue, 2 Dec 2014 18:22:55 +0100 Subject: [PATCH 041/217] 17399: do not let maxima handle ex.series coefficients --- src/sage/symbolic/expression.pyx | 36 +++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/src/sage/symbolic/expression.pyx b/src/sage/symbolic/expression.pyx index 021c40c91eb..8605f8f295c 100644 --- a/src/sage/symbolic/expression.pyx +++ b/src/sage/symbolic/expression.pyx @@ -5156,17 +5156,37 @@ cdef class Expression(CommutativeRingElement): sage: p = (17/3*a)*x^(3/2) + x*y + 1/x + x^x sage: p.coefficients(x) [[1, -1], [x^x, 0], [y, 1], [17/3*a, 3/2]] + + Series coefficients are now handled correctly (:trac:`17399`):: + + sage: s=(1/(1-x)).series(x,6) + sage: s.coeffs() + [[1, 0], [1, 1], [1, 2], [1, 3], [1, 4], [1, 5]] + sage: x,y = var("x,y") + sage: s=(1/(1-y-x)).series(x,6) + sage: s.coeffs(y) + [] + sage: s.coeffs() + [[-1/(y - 1), 0], + [(y - 1)^(-2), 1], + [-1/(y - 1)^3, 2], + [(y - 1)^(-4), 3], + [-1/(y - 1)^5, 4], + [(y - 1)^(-6), 5]] """ - f = self._maxima_() - maxima = f.parent() - maxima._eval_line('load(coeflist)') if x is None: x = self.default_variable() - x = self.parent().var(repr(x)) - G = f.coeffs(x) - from sage.calculus.calculus import symbolic_expression_from_maxima_string - S = symbolic_expression_from_maxima_string(repr(G)) - return S[1:] + # x = self.parent().var(str(x)) + if is_a_series(self._gobj): + return [[self.coeff(x, d), d] for d in xrange(self.degree(x))] + else: + f = self._maxima_() + maxima = f.parent() + maxima._eval_line('load(coeflist)') + G = f.coeffs(x) + from sage.calculus.calculus import symbolic_expression_from_maxima_string + S = symbolic_expression_from_maxima_string(repr(G)) + return S[1:] coeffs = coefficients From 98959fb6c4ae60350aeb171f3740483c8c56dc30 Mon Sep 17 00:00:00 2001 From: Joao de Faria Date: Wed, 3 Dec 2014 12:38:12 -0500 Subject: [PATCH 042/217] 17082 - fixed error checks --- src/sage/schemes/projective/projective_morphism.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/sage/schemes/projective/projective_morphism.py b/src/sage/schemes/projective/projective_morphism.py index 984e6a0aec4..3f8ee275ea0 100644 --- a/src/sage/schemes/projective/projective_morphism.py +++ b/src/sage/schemes/projective/projective_morphism.py @@ -1659,8 +1659,8 @@ def height_difference_bound(self, prec=None): 10.7632079329219 """ BR = self.domain().base_ring() - if not BR in NumberFields(): - raise NotImplementedError("Must be a number field") + if not BR in NumberFields() and not BR == ZZ: + raise NotImplementedError("Must be a number field or the integers") if not self.is_endomorphism(): raise NotImplementedError("Must be an endomorphism of projective space") if prec is None: @@ -1674,16 +1674,15 @@ def height_difference_bound(self, prec=None): U = self.global_height(prec) + R(binomial(N + d, d)).log() #compute lower bound - from explicit polynomials of Nullstellensatz CR = self.domain().coordinate_ring() - CR = CR.change_ring(BR) #lift only works over fields + if BR == ZZ: + CR = CR.change_ring(QQ) #lift only works over fields I = CR.ideal(self.defining_polynomials()) MCP = [] for k in range(N + 1): CoeffPolys = (CR.gen(k) ** D).lift(I) - print CoeffPolys Res = 1 for j in range(len(CoeffPolys)): if CoeffPolys[j] != 0: - #make this a list comprehension for i in range(len(CoeffPolys[j].coefficients())): Res = lcm(Res, abs(CoeffPolys[j].coefficients()[i].denominator())) h = max([c.global_height() for g in CoeffPolys for c in (Res*g).coefficients()]) From 09349098e9a50e01761b46402198c40ba3becd3c Mon Sep 17 00:00:00 2001 From: Joao de Faria Date: Wed, 3 Dec 2014 12:45:54 -0500 Subject: [PATCH 043/217] 17082- added num field example --- src/sage/schemes/projective/projective_morphism.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/sage/schemes/projective/projective_morphism.py b/src/sage/schemes/projective/projective_morphism.py index 3f8ee275ea0..07292deeb95 100644 --- a/src/sage/schemes/projective/projective_morphism.py +++ b/src/sage/schemes/projective/projective_morphism.py @@ -1656,8 +1656,18 @@ def height_difference_bound(self, prec=None): 11.0020998412042 sage: f.normalize_coordinates() sage: f.height_difference_bound() - 10.7632079329219 - """ + 10.3089526606443 + + :: + + sage: R.=QQ[] + sage: K. = NumberField(x^3 - 2) + sage: P. = ProjectiveSpace(K,2) + sage: H = End(P) + sage: f = H([1/(c+1)*x^2+c*y^2,210*x*y,10000*z^2]) + sage: f.height_difference_bound() + 11.0020998412042 +""" BR = self.domain().base_ring() if not BR in NumberFields() and not BR == ZZ: raise NotImplementedError("Must be a number field or the integers") From 99820cfcc289efa8fd54468b296051c9cc13891a Mon Sep 17 00:00:00 2001 From: Ralf Stephan Date: Thu, 4 Dec 2014 11:03:32 +0100 Subject: [PATCH 044/217] 17399: roll back previous commit to allow merge of 17428 --- src/sage/symbolic/expression.pyx | 36 +++++++------------------------- 1 file changed, 8 insertions(+), 28 deletions(-) diff --git a/src/sage/symbolic/expression.pyx b/src/sage/symbolic/expression.pyx index 8605f8f295c..021c40c91eb 100644 --- a/src/sage/symbolic/expression.pyx +++ b/src/sage/symbolic/expression.pyx @@ -5156,37 +5156,17 @@ cdef class Expression(CommutativeRingElement): sage: p = (17/3*a)*x^(3/2) + x*y + 1/x + x^x sage: p.coefficients(x) [[1, -1], [x^x, 0], [y, 1], [17/3*a, 3/2]] - - Series coefficients are now handled correctly (:trac:`17399`):: - - sage: s=(1/(1-x)).series(x,6) - sage: s.coeffs() - [[1, 0], [1, 1], [1, 2], [1, 3], [1, 4], [1, 5]] - sage: x,y = var("x,y") - sage: s=(1/(1-y-x)).series(x,6) - sage: s.coeffs(y) - [] - sage: s.coeffs() - [[-1/(y - 1), 0], - [(y - 1)^(-2), 1], - [-1/(y - 1)^3, 2], - [(y - 1)^(-4), 3], - [-1/(y - 1)^5, 4], - [(y - 1)^(-6), 5]] """ + f = self._maxima_() + maxima = f.parent() + maxima._eval_line('load(coeflist)') if x is None: x = self.default_variable() - # x = self.parent().var(str(x)) - if is_a_series(self._gobj): - return [[self.coeff(x, d), d] for d in xrange(self.degree(x))] - else: - f = self._maxima_() - maxima = f.parent() - maxima._eval_line('load(coeflist)') - G = f.coeffs(x) - from sage.calculus.calculus import symbolic_expression_from_maxima_string - S = symbolic_expression_from_maxima_string(repr(G)) - return S[1:] + x = self.parent().var(repr(x)) + G = f.coeffs(x) + from sage.calculus.calculus import symbolic_expression_from_maxima_string + S = symbolic_expression_from_maxima_string(repr(G)) + return S[1:] coeffs = coefficients From 6cd5286d987cf944621fb7666f1756b7785bcd0b Mon Sep 17 00:00:00 2001 From: Ralf Stephan Date: Thu, 4 Dec 2014 15:04:08 +0100 Subject: [PATCH 045/217] 17399: handle series in ex.coefficients() --- src/sage/symbolic/expression.pyx | 42 +++++++++++++++++++++++--------- 1 file changed, 31 insertions(+), 11 deletions(-) diff --git a/src/sage/symbolic/expression.pyx b/src/sage/symbolic/expression.pyx index dccf1b7d119..762a02924cc 100644 --- a/src/sage/symbolic/expression.pyx +++ b/src/sage/symbolic/expression.pyx @@ -5173,6 +5173,8 @@ cdef class Expression(CommutativeRingElement): [[2*a^2 + 1, 0], [-2*sqrt(2)*a + 1, 1], [1, 2]] sage: p.coefficients(x, sparse=False) [2*a^2 + 1, -2*sqrt(2)*a + 1, 1] + + TESTS: The behaviour is undefined with noninteger or negative exponents:: @@ -5190,17 +5192,34 @@ cdef class Expression(CommutativeRingElement): doctest:...: DeprecationWarning: coeffs is deprecated. Please use coefficients instead. See http://trac.sagemath.org/17438 for details. [[1, 1]] + + Series coefficients are now handled correctly (:trac:`17399`):: + + sage: s=(1/(1-x)).series(x,6); s + 1 + 1*x + 1*x^2 + 1*x^3 + 1*x^4 + 1*x^5 + Order(x^6) + sage: s.coefficients() + [[1, 0], [1, 1], [1, 2], [1, 3], [1, 4], [1, 5]] + sage: s.coefficients(x, sparse=False) + [1, 1, 1, 1, 1, 1] + sage: x,y = var("x,y") + sage: s=(1/(1-y*x-x)).series(x,3); s + 1 + (y + 1)*x + ((y + 1)^2)*x^2 + Order(x^3) + sage: s.coefficients(x, sparse=False) + [1, y + 1, (y + 1)^2] """ - f = self._maxima_() - maxima = f.parent() - maxima._eval_line('load(coeflist)') if x is None: x = self.default_variable() - x = self.parent().var(repr(x)) - G = f.coeffs(x) - from sage.calculus.calculus import symbolic_expression_from_maxima_string - S = symbolic_expression_from_maxima_string(repr(G)) - l = S[1:] + if is_a_series(self._gobj): + l = [[self.coefficient(x, d), d] for d in xrange(self.degree(x))] + else: + f = self._maxima_() + maxima = f.parent() + maxima._eval_line('load(coeflist)') + G = f.coeffs(x) + from sage.calculus.calculus import symbolic_expression_from_maxima_string + S = symbolic_expression_from_maxima_string(repr(G)) + l = S[1:] + if sparse is True: return l else: @@ -5237,9 +5256,6 @@ cdef class Expression(CommutativeRingElement): (x, y, a) sage: (x^5).list() [0, 0, 0, 0, 0, 1] - sage: p = x^3 - (x-3)*(x^2+x) + 1 - sage: p.list() - [1, 3, 2] sage: p = x - x^3 + 5/7*x^5 sage: p.list() [0, 1, 0, -1, 0, 5/7] @@ -5247,6 +5263,10 @@ cdef class Expression(CommutativeRingElement): -2*sqrt(2)*a*x + 2*a^2 + x^2 + x + 1 sage: p.list(a) [x^2 + x + 1, -2*sqrt(2)*x, 2] + sage: s=(1/(1-x)).series(x,6); s + 1 + 1*x + 1*x^2 + 1*x^3 + 1*x^4 + 1*x^5 + Order(x^6) + sage: s.list() + [1, 1, 1, 1, 1, 1] """ return self.coefficients(x=x, sparse=False) From 0eebe9015c55f3f5a349a1605e3cafa070f17014 Mon Sep 17 00:00:00 2001 From: Ben Hutz Date: Fri, 5 Dec 2014 10:59:12 -0500 Subject: [PATCH 046/217] 17427: improve hash for projective points --- .../schemes/projective/projective_point.py | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/src/sage/schemes/projective/projective_point.py b/src/sage/schemes/projective/projective_point.py index 4c54016a6ad..6f94616aab5 100644 --- a/src/sage/schemes/projective/projective_point.py +++ b/src/sage/schemes/projective/projective_point.py @@ -35,10 +35,12 @@ # http://www.gnu.org/licenses/ #***************************************************************************** +from sage.categories.integral_domains import IntegralDomains from sage.categories.number_fields import NumberFields _NumberFields = NumberFields() from sage.rings.infinity import infinity from sage.rings.arith import gcd, lcm, is_prime +from sage.rings.fraction_field import FractionField from sage.rings.integer_ring import ZZ from sage.rings.number_field.order import is_NumberFieldOrder from sage.rings.padics.all import Qp @@ -340,6 +342,48 @@ def __ne__(self,right): return True return False + def __hash__(self): + """ + Computes the hash value of ``self``. + + OUTPUT: Integer. + + EXAMPLES:: + + sage: P.=ProjectiveSpace(ZZ,1) + sage: hash(P([1,1])) + 7316841028997809016 # 64-bit + sage: hash(P.point([2,2], False)) + 7316841028997809016 # 64-bit + + :: + + sage: R.=PolynomialRing(QQ) + sage: K.=NumberField(x^2+3) + sage: O=K.maximal_order() + sage: P.=ProjectiveSpace(O,1) + sage: hash(P([1+w,2])) + 4801154424156762579 # 64-bit + sage: hash(P([2,1-w])) + 4801154424156762579 # 64-bit + + :: + + sage: P.=ProjectiveSpace(Zmod(10), 1) + sage: hash(P([2,5])) + 4677413289753502123 # 64-bit + """ + R = self.codomain().base_ring() + #if there is a fraction field normalize the point so that + #equal points have equal hash values + if R in IntegralDomains(): + P = self.change_ring(FractionField(R)) + P.normalize_coordinates() + return hash(str(P)) + #if there is no good way to normalize return + #a constant value + return hash(self.codomain()) + def scale_by(self,t): """ Scale the coordinates of the point ``self`` by `t`. A ``TypeError`` occurs if @@ -1056,6 +1100,24 @@ def __init__(self, X, v, check=True): self._coords = v + def __hash__(self): + """ + Computes the hash value of ``self``. + + OUTPUT: Integer. + + EXAMPLES:: + + sage: P.=ProjectiveSpace(QQ, 1) + sage: hash(P([1/2,1])) + 3714374126286711103 # 64-bit + sage: hash(P.point([1,2], False)) + 3714374126286711103 # 64-bit + """ + P = copy(self) + P.normalize_coordinates() + return hash(str(P)) + def normalize_coordinates(self): r""" Normalizes ``self`` so that the last non-zero coordinate is `1`. From a6fd53b0c8a0e451254cfd5afd25a40f7d1d8a2e Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Sat, 6 Dec 2014 19:24:49 +0100 Subject: [PATCH 047/217] trac #16603: speedup + doc --- src/sage/matrix/matrix_misc.py | 115 +++++++++++++++++---------------- 1 file changed, 59 insertions(+), 56 deletions(-) diff --git a/src/sage/matrix/matrix_misc.py b/src/sage/matrix/matrix_misc.py index 90c70efa6d7..c07f3e5086b 100644 --- a/src/sage/matrix/matrix_misc.py +++ b/src/sage/matrix/matrix_misc.py @@ -20,6 +20,7 @@ from sage.categories.fields import Fields from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing +from sage.rings.integer_ring import ZZ _Fields = Fields() def row_iterator(A): @@ -201,42 +202,33 @@ def weak_popov_form(M,ascend=True): # return reduced matrix and operations matrix return (matrix(r)/den, matrix(N), d) -def _prm_mul(p1, p2, free_vars_indices, K): +def prm_mul(p1, p2, free_vars_indices): """ - Return the product of `p1` and `p2`, putting free variables to 1. + Return the product of ``p1`` and ``p2``, putting free variables in + ``free_vars_indices`` to `1`. - In the following example, - ``p1 = 1 + t*e_0 + t*e_1`` - ``p2 = 1 + t*e_0 + t*e_2; 'e_0` free variable; - using the fact that `e_i` are nilpotent and setting - `e_0 = 1` after performing the product one gets - ``p1 * p2 = (1 + 2*t) + (t + t^2)*e_1 + (t + t^2)*e_2 + t^2*e_1*e_2`` - - The polynomials are represented in dictionary form; to a - variable ``eta_i`` it is associated the key ``1 << i``; - so in the following example 'e_1' corresponds to the key '2' - and 'e_1*e_2' to the key '6'. + This function is mainly use as a subroutine of + :func:`permanental_minor_vector`. EXAMPLES:: - sage: from sage.matrix.matrix_misc import _prm_mul - sage: K = PolynomialRing(ZZ, 't') - sage: t = K.gen() + sage: from sage.matrix.matrix_misc import prm_mul + sage: t = polygen(ZZ, 't') sage: p1 = {0: 1, 1: t, 4: t} sage: p2 = {0: 1, 1: t, 2: t} - sage: _prm_mul(p1, p2, [0], K) + sage: prm_mul(p1, p2, [0]) {0: 2*t + 1, 2: t^2 + t, 4: t^2 + t, 6: t^2} """ p = {} mask_free = 0 - one = int(1) + one = 1 for i in free_vars_indices: mask_free += one << i if not p2: return p get = p.get for exp1, v1 in p1.iteritems(): - for exp2, v2 in p2.items(): + for exp2, v2 in p2.iteritems(): if exp1 & exp2: continue exp = exp1 | exp2 @@ -244,31 +236,13 @@ def _prm_mul(p1, p2, free_vars_indices, K): if exp & mask_free: for i in free_vars_indices: if exp & (one << i): - exp = exp.__xor__(one << i) - p[exp] = get(exp, K.zero()) + v + exp ^= one << i + if exp not in p: + p[exp] = v + else: + p[exp] += v return p - -def _is_free_var(i, k, m): - """ - Return ``True`` if the variable `k` does not occur from row `i` on. - - INPUT: - - - i -- current row - - k -- index of the variable - - m -- matrix as a list of lists - - EXAMPLES:: - - sage: from sage.matrix.matrix_misc import _is_free_var - sage: m = [[1,2,3], [2,0,4], [2,0,4]] - sage: _is_free_var(1,1,m) - True - """ - return all(m[j][k] == 0 for j in range(i, len(m))) - - def permanental_minor_vector(m, permanent_only=False): r""" Return the polynomial of the sums of permanental minors of a matrix `m` @@ -280,17 +254,23 @@ def permanental_minor_vector(m, permanent_only=False): \sum_0^{min(nrows, ncols)} p_i(m) x^i - where `p_i(m)` is ``m.permanental_minor(i)``. + where `p_i(m)` is the `i`-th permanental minor of `m` (that can also be + obtained through the method + :meth:`~sage.matrix.matrix2.Matrix.permanental_minor` via + ``m.permanental_minor(i)``). INPUT: - - `m` -- matrix + - ``m`` -- matrix - - `permanent_only` -- boolean, if ``True``, only the permanent is computed + - ``permanent_only`` -- optional boolean. If ``True``, only the permanent + is computed (might be faster). OUTPUT: - polynomial in array form; the last element of the array is the permanent. + The list of coefficients of the polynomial, i.e. the coefficient of `x^i` is + at the `i`-th position in the list. In particular, the last element of the + list is the permanent. EXAMPLES:: @@ -307,39 +287,63 @@ def permanental_minor_vector(m, permanent_only=False): sage: A = M([1,0,1,0,1,0,1,0,1,0,10,10,1,0,1,1]) sage: permanental_minor_vector(A) [1, 28, 114, 84, 0] + sage: [A.permanental_minor(i) for i in range(5)] + [1, 28, 114, 84, 0] An example over `\QQ`:: sage: M = MatrixSpace(QQ,2,2) sage: A = M([1/5,2/7,3/2,4/5]) - sage: permanental_minor_vector(A, 1) + sage: permanental_minor_vector(A, True) 103/175 An example with polynomial coefficients:: sage: R. = PolynomialRing(ZZ) sage: A = MatrixSpace(R,2)([[a,1], [a,a+1]]) - sage: permanental_minor_vector(A, 1) + sage: permanental_minor_vector(A, True) a^2 + 2*a + ALGORITHM: + + The algorithm uses polynomials over the algebra + `K[\eta_1, \eta_2,\ldots, \eta_k]` where the `\eta_i` are commuting, + nilpotent of order `2` (i.e. `\eta_i^2 = 0`). Let us consider an example of + computation. Let `p_1 = 1 + t \eta_0 + t \eta_1` and + `p_2 = 1 + t \eta_0 + t \eta_2`. Then + + .. MATH:: + + p_1 p_2 = 1 + 2t \eta_0 + + t (\eta_1 + \eta_2) + + t^2 (\eta_0 \eta_1 + \eta_0 \eta_2 + \eta_1 \eta_2) + + The product is implemented as a subroutine in :func:`prm_mul`. The + polynomials are represented in dictionary form: to a variable `\eta_i` + it is associated the key `2^i` (or in Python ``1 << i``). So in the + above example `\eta_1` corresponds to the key `2` while the product + `\eta_1 \eta_2` to the key `6`. + + MORE DOC NEEDED!!! + REFERENCES: .. [ButPer] P. Butera and M. Pernici "Sums of permanental minors using Grassmann algebra", :arxiv:`1406.5337` """ K = PolynomialRing(m.base_ring(), 't') - m = list(m) - nrows = len(m) - ncols = len(m[0]) + nrows = m.nrows() + ncols = m.ncols() + m = m.rows() p = {int(0): K.one()} t = K.gen() done_vars = set() - one = int(1) + one = 1 for i in range(nrows): if permanent_only: p1 = {} else: - p1 = {int(0): K.one()} + p1 = {0: K.one()} a = m[i] for j in range(len(a)): if a[j]: @@ -348,11 +352,10 @@ def permanental_minor_vector(m, permanent_only=False): for j in range(ncols): if j in done_vars: continue - r = _is_free_var(i + 1, j, m) - if r: + if all(m[k][j] == 0 for k in range(i+1, len(m))): free_vars_indices.append(j) done_vars.add(j) - p = _prm_mul(p, p1, free_vars_indices, K) + p = prm_mul(p, p1, free_vars_indices) if not p: return K.zero() assert len(p) == 1 From f611f58d45d8a0989a47a744dfd537b8e54b2412 Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Sat, 6 Dec 2014 21:58:17 +0100 Subject: [PATCH 048/217] trac #16603: add matrix_misc.py to the doc --- src/doc/en/reference/matrices/index.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/doc/en/reference/matrices/index.rst b/src/doc/en/reference/matrices/index.rst index 75c0bd93b6b..4e614ea67db 100644 --- a/src/doc/en/reference/matrices/index.rst +++ b/src/doc/en/reference/matrices/index.rst @@ -52,6 +52,8 @@ objects like operation tables (e.g. the multiplication table of a group). sage/matrix/docs + sage/matrix/matrix_misc + sage/matrix/matrix sage/matrix/matrix0 From 4d6c87e5d2ba91cb16156f0dfb266180e0af9e2f Mon Sep 17 00:00:00 2001 From: Ben Hutz Date: Mon, 8 Dec 2014 13:54:10 -0500 Subject: [PATCH 049/217] 17427: adjusted formatting and added comments --- .../schemes/projective/projective_point.py | 48 +++++++++---------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/src/sage/schemes/projective/projective_point.py b/src/sage/schemes/projective/projective_point.py index 6f94616aab5..e373c7d8da4 100644 --- a/src/sage/schemes/projective/projective_point.py +++ b/src/sage/schemes/projective/projective_point.py @@ -344,13 +344,16 @@ def __ne__(self,right): def __hash__(self): """ - Computes the hash value of ``self``. + Computes the hash value of ``self``. If the base ring has a fraction + field, normalize the point in the fraction field and then hash so + that equal points have equal hash values. If the base ring is not + an integral domain, return the hash of the parent. OUTPUT: Integer. EXAMPLES:: - sage: P.=ProjectiveSpace(ZZ,1) + sage: P. = ProjectiveSpace(ZZ, 1) sage: hash(P([1,1])) 7316841028997809016 # 64-bit sage: hash(P.point([2,2], False)) @@ -358,18 +361,18 @@ def __hash__(self): :: - sage: R.=PolynomialRing(QQ) - sage: K.=NumberField(x^2+3) - sage: O=K.maximal_order() - sage: P.=ProjectiveSpace(O,1) - sage: hash(P([1+w,2])) + sage: R. = PolynomialRing(QQ) + sage: K. = NumberField(x^2 + 3) + sage: O = K.maximal_order() + sage: P. = ProjectiveSpace(O, 1) + sage: hash(P([1+w, 2])) 4801154424156762579 # 64-bit - sage: hash(P([2,1-w])) + sage: hash(P([2, 1-w])) 4801154424156762579 # 64-bit :: - sage: P.=ProjectiveSpace(Zmod(10), 1) + sage: P. = ProjectiveSpace(Zmod(10), 1) sage: hash(P([2,5])) 4677413289753502123 # 64-bit """ @@ -1108,10 +1111,10 @@ def __hash__(self): EXAMPLES:: - sage: P.=ProjectiveSpace(QQ, 1) - sage: hash(P([1/2,1])) + sage: P. = ProjectiveSpace(QQ, 1) + sage: hash(P([1/2, 1])) 3714374126286711103 # 64-bit - sage: hash(P.point([1,2], False)) + sage: hash(P.point([1, 2], False)) 3714374126286711103 # 64-bit """ P = copy(self) @@ -1186,38 +1189,35 @@ def __hash__(self): r""" Returns the integer hash of ``self`` - - OUTPUT: - - - integer + OUTPUT: Integer. EXAMPLES:: - sage: P.=ProjectiveSpace(GF(5),2) + sage: P. = ProjectiveSpace(GF(5), 2) sage: hash(P(2,1,2)) 41 :: - sage: P.=ProjectiveSpace(GF(7),2) - sage: X=P.subscheme(x^2-y^2) - sage: hash(X(1,1,2)) + sage: P. = ProjectiveSpace(GF(7), 2) + sage: X = P.subscheme(x^2 - y^2) + sage: hash(X(1, 1, 2)) 81 :: - sage: P.=ProjectiveSpace(GF(13),1) + sage: P. = ProjectiveSpace(GF(13), 1) sage: hash(P(3,4)) 17 :: - sage: P.=ProjectiveSpace(GF(13^3,'t'),1) + sage: P. = ProjectiveSpace(GF(13^3,'t'), 1) sage: hash(P(3,4)) 2201 """ - p=self.codomain().base_ring().order() - N=self.codomain().ambient_space().dimension_relative() + p = self.codomain().base_ring().order() + N = self.codomain().ambient_space().dimension_relative() return sum(hash(self[i])*p**i for i in range(N+1)) def orbit_structure(self,f): From f511623cce7bde7f022fa212a505526b3b62f0db Mon Sep 17 00:00:00 2001 From: Mario Pernici Date: Tue, 9 Dec 2014 13:58:03 +0100 Subject: [PATCH 050/217] Added documentation to `permanental_minor_vector` --- src/sage/matrix/matrix_misc.py | 67 +++++++++++++++++++++++++++++++--- 1 file changed, 61 insertions(+), 6 deletions(-) diff --git a/src/sage/matrix/matrix_misc.py b/src/sage/matrix/matrix_misc.py index c07f3e5086b..cde1493eed3 100644 --- a/src/sage/matrix/matrix_misc.py +++ b/src/sage/matrix/matrix_misc.py @@ -306,10 +306,25 @@ def permanental_minor_vector(m, permanent_only=False): ALGORITHM: - The algorithm uses polynomials over the algebra - `K[\eta_1, \eta_2,\ldots, \eta_k]` where the `\eta_i` are commuting, - nilpotent of order `2` (i.e. `\eta_i^2 = 0`). Let us consider an example of - computation. Let `p_1 = 1 + t \eta_0 + t \eta_1` and + The permanent of a n x n matrix `A` is the coefficient of the + x_1...x_n monomial in + + .. MATH:: + + \prod_{i=1}^n \sum_{j=1}^n A_{ij} x_j + + Evaluating this product one can neglect `x_i^2`, that is `x_i` + can be considered to be nilpotent of order `2`. + + To formalize this procedure, consider polynomials over the algebra + `K[t, \eta_1, \eta_2,\ldots, \eta_k]` where the `\eta_i` are + commuting, nilpotent of order `2` (i.e. `\eta_i^2 = 0`), + and `t` is commuting. + Introduce an "integration" operation

consisting in the sum + of the coefficients of the non-vanishing monomials of the polynomial p. + + Let us consider an example of computation. + Let `p_1 = 1 + t \eta_0 + t \eta_1` and `p_2 = 1 + t \eta_0 + t \eta_2`. Then .. MATH:: @@ -318,13 +333,53 @@ def permanental_minor_vector(m, permanent_only=False): t (\eta_1 + \eta_2) + t^2 (\eta_0 \eta_1 + \eta_0 \eta_2 + \eta_1 \eta_2) + = 1 + 4t + 3t^2 + + A useful property is the following: let `p_1(\eta_0,..,\eta_k)` + be a polynomial in distributed form, and let `p_2(\eta_j,..,\eta_k)` + be a product of polynomials, with `j \ge 1`; one has + + .. MATH:: + = + + where `\eta_0,..,\eta_{j-1}` are replaced by `1` in `p_1`. + + In this formalism + + .. MATH:: + + perm(A) = < \prod_{i=1}^n \sum_{j=1}^n A_{ij} \eta_j > + + The sum of permanental k-minors of `A` is + + .. MATH:: + + permMinor (A, k) = \sum_{r,s} perm(A_{r,s}) + + where ``A_{r,s}`` is the matrix obtained eliminating the rows `r` + and the columns `s`. + + The generating function of the sums of the permanental minors + of a m x n matrix is + + .. MATH:: + + g(t) = < \prod_{i=1}^m (1 + t \sum_{j=1}^n A_{ij} \eta_j) > + + In fact the `t^k` coefficient of `g(t)` corresponds to choosing + `k` rows of `A`; `\eta_i` is associated to the i-th column; + nilpotency avoids having twice the same column in a product of A's. + The product is implemented as a subroutine in :func:`prm_mul`. The polynomials are represented in dictionary form: to a variable `\eta_i` it is associated the key `2^i` (or in Python ``1 << i``). So in the above example `\eta_1` corresponds to the key `2` while the product `\eta_1 \eta_2` to the key `6`. - MORE DOC NEEDED!!! + The complexity of this algorithm is `O(2^n m^2 n)`. + If `A` is a banded matrix with width `w`, that is `A_{ij}=0` for + `|i - j| > w`, and `w < n/2`, then the complexity of the + algorithm is `O(4^w (w+1) n^2)`. REFERENCES: @@ -335,7 +390,7 @@ def permanental_minor_vector(m, permanent_only=False): nrows = m.nrows() ncols = m.ncols() m = m.rows() - p = {int(0): K.one()} + p = {0: K.one()} t = K.gen() done_vars = set() one = 1 From 97f2062c85b6845115bd0e3a3fb2e7316dc2ade5 Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Tue, 9 Dec 2014 18:57:29 +0100 Subject: [PATCH 051/217] trac #16603: documentation + argument modifs --- src/sage/matrix/matrix_misc.py | 189 ++++++++++++++++++++------------- 1 file changed, 113 insertions(+), 76 deletions(-) diff --git a/src/sage/matrix/matrix_misc.py b/src/sage/matrix/matrix_misc.py index cde1493eed3..c4c33f1db87 100644 --- a/src/sage/matrix/matrix_misc.py +++ b/src/sage/matrix/matrix_misc.py @@ -243,50 +243,51 @@ def prm_mul(p1, p2, free_vars_indices): p[exp] += v return p -def permanental_minor_vector(m, permanent_only=False): +def permanental_minor_polynomial(A, permanent_only=False, var='t'): r""" - Return the polynomial of the sums of permanental minors of a matrix `m` - in array form. + Return the polynomial of the sums of permanental minors of ``A``. The polynomial of the sums of permanental minors is .. MATH:: - \sum_0^{min(nrows, ncols)} p_i(m) x^i + \sum_{i=0}^{min(nrows, ncols)} p_i(A) x^i - where `p_i(m)` is the `i`-th permanental minor of `m` (that can also be + where `p_i(A)` is the `i`-th permanental minor of `A` (that can also be obtained through the method :meth:`~sage.matrix.matrix2.Matrix.permanental_minor` via - ``m.permanental_minor(i)``). + ``A.permanental_minor(i)``). - INPUT: + The algorithm implemented by that function has been developed by P. Butera + and M. Pernici, see [ButPer]. Its complexity is `O(2^n m^2 n)` where `m` and + `n` are the number of rows and columns of `A`. Moreover, if `A` is a banded + matrix with width `w`, that is `A_{ij}=0` for `|i - j| > w` and `w < n/2`, + then the complexity of the algorithm is `O(4^w (w+1) n^2)`. - - ``m`` -- matrix + INPUT: - - ``permanent_only`` -- optional boolean. If ``True``, only the permanent - is computed (might be faster). + - ``A`` -- matrix - OUTPUT: + - ``permanent_only`` -- optional boolean. If ``True``, only the permanent + is computed (might be faster). - The list of coefficients of the polynomial, i.e. the coefficient of `x^i` is - at the `i`-th position in the list. In particular, the last element of the - list is the permanent. + - ``var`` -- a variable name EXAMPLES:: - sage: from sage.matrix.matrix_misc import permanental_minor_vector + sage: from sage.matrix.matrix_misc import permanental_minor_polynomial sage: m = matrix([[1,1],[1,2]]) - sage: permanental_minor_vector(m) - [1, 5, 3] - sage: permanental_minor_vector(m, permanent_only=1) + sage: permanental_minor_polynomial(m) + 3*t^2 + 5*t + 1 + sage: permanental_minor_polynomial(m, permanent_only=True) 3 :: sage: M = MatrixSpace(ZZ,4,4) sage: A = M([1,0,1,0,1,0,1,0,1,0,10,10,1,0,1,1]) - sage: permanental_minor_vector(A) - [1, 28, 114, 84, 0] + sage: permanental_minor_polynomial(A) + 84*t^3 + 114*t^2 + 28*t + 1 sage: [A.permanental_minor(i) for i in range(5)] [1, 28, 114, 84, 0] @@ -294,112 +295,149 @@ def permanental_minor_vector(m, permanent_only=False): sage: M = MatrixSpace(QQ,2,2) sage: A = M([1/5,2/7,3/2,4/5]) - sage: permanental_minor_vector(A, True) + sage: permanental_minor_polynomial(A, True) 103/175 An example with polynomial coefficients:: sage: R. = PolynomialRing(ZZ) sage: A = MatrixSpace(R,2)([[a,1], [a,a+1]]) - sage: permanental_minor_vector(A, True) + sage: permanental_minor_polynomial(A, True) a^2 + 2*a + A usage of the ``var`` argument:: + + sage: m = matrix(ZZ,4,[0,1,2,3,1,2,3,0,2,3,0,1,3,0,1,2]) + sage: permanental_minor_polynomial(m, var='x') + 164*x^4 + 384*x^3 + 172*x^2 + 24*x + 1 + ALGORITHM: - The permanent of a n x n matrix `A` is the coefficient of the - x_1...x_n monomial in + The permanent `perm(A)` of a `n \times n` matrix `A` is the coefficient of the + `x_1 x_2 \ldots x_n` monomial in .. MATH:: - \prod_{i=1}^n \sum_{j=1}^n A_{ij} x_j + \prod_{i=1}^n \left( \sum_{j=1}^n A_{ij} x_j \right) Evaluating this product one can neglect `x_i^2`, that is `x_i` can be considered to be nilpotent of order `2`. - To formalize this procedure, consider polynomials over the algebra - `K[t, \eta_1, \eta_2,\ldots, \eta_k]` where the `\eta_i` are - commuting, nilpotent of order `2` (i.e. `\eta_i^2 = 0`), - and `t` is commuting. - Introduce an "integration" operation

consisting in the sum - of the coefficients of the non-vanishing monomials of the polynomial p. + To formalize this procedure, consider the algebra + `R = K[\eta_1, \eta_2, \ldots, \eta_n]` where the `\eta_i` are + commuting, nilpotent of order `2` (i.e. `\eta_i^2 = 0`). + Formally it is the quotient ring of the polynomial + ring in `\eta_1, \eta_2, \ldots, \eta_n` quotiented by the ideal + generated by the `\eta_i^2`. + + We will mostly consider the ring `R[t]` of polynomials over `R`. We + denote a generic element of `R[t]` by `p(\eta_1, \ldots, \eta_n)` or + `p(\eta_{i_1}, \ldots, \eta_{i_k})` if we want to emphasize that some + monomials in the `\eta_i` are missing. + + Introduce an "integration" operation `\langle p \rangle` over `R` and + `R[t]` consisting in the sum of the coefficients of the non-vanishing + monomials in `\eta_i` (i.e. the result of setting all variables `\eta_i` + to `1`). Let us emphasize that this is *not* a morphism of algebras as + `\langle \eta_1 \rangle^2 = 1` while `\langle \eta_1^2 \rangle = 0`! Let us consider an example of computation. - Let `p_1 = 1 + t \eta_0 + t \eta_1` and - `p_2 = 1 + t \eta_0 + t \eta_2`. Then + Let `p_1 = 1 + t \eta_1 + t \eta_2` and + `p_2 = 1 + t \eta_1 + t \eta_3`. Then .. MATH:: - p_1 p_2 = 1 + 2t \eta_0 + - t (\eta_1 + \eta_2) + - t^2 (\eta_0 \eta_1 + \eta_0 \eta_2 + \eta_1 \eta_2) + p_1 p_2 = 1 + 2t \eta_1 + + t (\eta_2 + \eta_3) + + t^2 (\eta_1 \eta_2 + \eta_1 \eta_3 + \eta_2 \eta_3) - = 1 + 4t + 3t^2 + and - A useful property is the following: let `p_1(\eta_0,..,\eta_k)` - be a polynomial in distributed form, and let `p_2(\eta_j,..,\eta_k)` - be a product of polynomials, with `j \ge 1`; one has + .. MATH:: + + \langle p_1 p_2 \rangle = 1 + 4t + 3t^2 + + In this formalism, the permanent is just .. MATH:: - = - where `\eta_0,..,\eta_{j-1}` are replaced by `1` in `p_1`. + perm(A) = \langle \prod_{i=1}^n \sum_{j=1}^n A_{ij} \eta_j \rangle - In this formalism + A useful property of `\langle . \rangle` which makes this algorithm + efficient for band matrices is the following: let + `p_1(\eta_1, \ldots, \eta_n)` and `p_2(\eta_j, \ldots, \eta_n)` be + polynomials in `R[t]` where `j \ge 1`. Then one has .. MATH:: - perm(A) = < \prod_{i=1}^n \sum_{j=1}^n A_{ij} \eta_j > + \langle p_1(\eta_1, \ldots, \eta_n) p_2 \rangle = + \langle p_1(1, \ldots, 1, \eta_j, \ldots, \eta_n) p_2 \rangle + + where `\eta_1,..,\eta_{j-1}` are replaced by `1` in `p_1`. Informally, + we can "integrate" these variables *before* performing the product. More + generally, if a monomial `\eta_i` is missing in one of the term of a + product, then it can be integrated in the other term. - The sum of permanental k-minors of `A` is + Now let us consider an `m \times n` matrix with `m \leq n`. The *sum of + permanental `k`-minors of `A`* is .. MATH:: - permMinor (A, k) = \sum_{r,s} perm(A_{r,s}) + perm(A, k) = \sum_{r,c} perm(A_{r,c}) - where ``A_{r,s}`` is the matrix obtained eliminating the rows `r` - and the columns `s`. + where the sum is over the `k`-subsets `r` of rows and `k`-subsets `c` of + columns and `A_{r,c}` is the submatrix obtained from `A` by keeping only + the rows `r` and columns `c`. Of course + `perm(A, \min(m,n)) = perm(A)` and note that `perm(A,1)` is just the sum + of all entries of the matrix. - The generating function of the sums of the permanental minors - of a m x n matrix is + The generating function of these sums of permanental minors is .. MATH:: - g(t) = < \prod_{i=1}^m (1 + t \sum_{j=1}^n A_{ij} \eta_j) > + g(t) = \left\langle + \prod_{i=1}^m \left(1 + t \sum_{j=1}^n A_{ij} \eta_j\right) + \right\rangle In fact the `t^k` coefficient of `g(t)` corresponds to choosing `k` rows of `A`; `\eta_i` is associated to the i-th column; - nilpotency avoids having twice the same column in a product of A's. - - The product is implemented as a subroutine in :func:`prm_mul`. The - polynomials are represented in dictionary form: to a variable `\eta_i` - it is associated the key `2^i` (or in Python ``1 << i``). So in the - above example `\eta_1` corresponds to the key `2` while the product - `\eta_1 \eta_2` to the key `6`. - - The complexity of this algorithm is `O(2^n m^2 n)`. - If `A` is a banded matrix with width `w`, that is `A_{ij}=0` for - `|i - j| > w`, and `w < n/2`, then the complexity of the - algorithm is `O(4^w (w+1) n^2)`. + nilpotency avoids having twice the same column in a product of `A`'s. + + For more details, see the article [ButPer]. + + From a technical point of view, the product in + `K[\eta_1, \ldots, \eta_n][t]` is implemented as a subroutine in + :func:`prm_mul`. The indices of the rows and columns actually start at + `0`, so the variables are `\eta_0, \ldots, \eta_{n-1}`. Polynomials are + represented in dictionary form: to a variable `\eta_i` is associated + the key `2^i` (or in Python ``1 << i``). The keys associated to products + are obtained by considering the development in base `2`: to the monomial + `\eta_{i_1} \ldots \eta_{i_k}` is associated the key + `2^{i_1} + \ldots + 2^{i_k}`. So the product `\eta_1 \eta_2` corresponds to the + key `6 = (110)_2` while `\eta_0 \eta_3` has key `5 = (101)_2`. In + particular all operations on monomials are implemented via bitwise + operations on the keys. REFERENCES: .. [ButPer] P. Butera and M. Pernici "Sums of permanental minors using Grassmann algebra", :arxiv:`1406.5337` """ - K = PolynomialRing(m.base_ring(), 't') - nrows = m.nrows() - ncols = m.ncols() - m = m.rows() + K = PolynomialRing(A.base_ring(), var) + nrows = A.nrows() + ncols = A.ncols() + A = A.rows() p = {0: K.one()} t = K.gen() done_vars = set() one = 1 for i in range(nrows): + # build the polynomial p1 = 1 + t sum A_{ij} eta_j if permanent_only: p1 = {} else: p1 = {0: K.one()} - a = m[i] + a = A[i] # the i-th row of A for j in range(len(a)): if a[j]: p1[one< Date: Tue, 9 Dec 2014 18:57:49 +0100 Subject: [PATCH 052/217] trac #16603: various optimizations --- src/sage/matrix/matrix_misc.py | 55 +++++++++++++++++++--------------- 1 file changed, 31 insertions(+), 24 deletions(-) diff --git a/src/sage/matrix/matrix_misc.py b/src/sage/matrix/matrix_misc.py index c4c33f1db87..9ba42b06ba5 100644 --- a/src/sage/matrix/matrix_misc.py +++ b/src/sage/matrix/matrix_misc.py @@ -202,13 +202,20 @@ def weak_popov_form(M,ascend=True): # return reduced matrix and operations matrix return (matrix(r)/den, matrix(N), d) -def prm_mul(p1, p2, free_vars_indices): +def prm_mul(p1, p2, mask_free): """ Return the product of ``p1`` and ``p2``, putting free variables in - ``free_vars_indices`` to `1`. + ``mask_free`` to `1`. This function is mainly use as a subroutine of - :func:`permanental_minor_vector`. + :func:`permanental_minor_polynomial`. + + INPUT: + + - ``p1,p2`` -- polynomials as dictionaries + + - ``mask_free`` -- an integer mask that give the list of free variables + (the `i`-th variable is free if the `i`-th bit of ``mask_free`` is `1`) EXAMPLES:: @@ -216,27 +223,22 @@ def prm_mul(p1, p2, free_vars_indices): sage: t = polygen(ZZ, 't') sage: p1 = {0: 1, 1: t, 4: t} sage: p2 = {0: 1, 1: t, 2: t} - sage: prm_mul(p1, p2, [0]) + sage: prm_mul(p1, p2, 1) {0: 2*t + 1, 2: t^2 + t, 4: t^2 + t, 6: t^2} """ p = {} - mask_free = 0 - one = 1 - for i in free_vars_indices: - mask_free += one << i if not p2: return p get = p.get for exp1, v1 in p1.iteritems(): + if v1.is_zero(): + continue for exp2, v2 in p2.iteritems(): if exp1 & exp2: continue - exp = exp1 | exp2 v = v1 * v2 - if exp & mask_free: - for i in free_vars_indices: - if exp & (one << i): - exp ^= one << i + exp = exp1 | exp2 + exp = exp ^ (exp & mask_free) if exp not in p: p[exp] = v else: @@ -429,8 +431,7 @@ def permanental_minor_polynomial(A, permanent_only=False, var='t'): A = A.rows() p = {0: K.one()} t = K.gen() - done_vars = set() - one = 1 + vars_to_do = range(ncols) for i in range(nrows): # build the polynomial p1 = 1 + t sum A_{ij} eta_j if permanent_only: @@ -440,15 +441,21 @@ def permanental_minor_polynomial(A, permanent_only=False, var='t'): a = A[i] # the i-th row of A for j in range(len(a)): if a[j]: - p1[one< Date: Tue, 9 Dec 2014 18:58:00 +0100 Subject: [PATCH 053/217] trac #16603: modify methods of matrices --- src/sage/matrix/matrix2.pyx | 113 +++++++++++++++++++++++++++++++----- 1 file changed, 98 insertions(+), 15 deletions(-) diff --git a/src/sage/matrix/matrix2.pyx b/src/sage/matrix/matrix2.pyx index b8b746e850f..1f48361dd28 100644 --- a/src/sage/matrix/matrix2.pyx +++ b/src/sage/matrix/matrix2.pyx @@ -49,7 +49,7 @@ import sage.modules.free_module import matrix_space import berlekamp_massey from sage.modules.free_module_element import is_FreeModuleElement -from sage.matrix.matrix_misc import permanental_minor_vector +from sage.matrix.matrix_misc import permanental_minor_polynomial cdef class Matrix(matrix1.Matrix): def _backslash_(self, B): @@ -680,10 +680,15 @@ cdef class Matrix(matrix1.Matrix): self, right = canonical_coercion(self, right) return self._elementwise_product(right) - def permanent(self): + def permanent(self, algorithm="Ryser"): r""" Calculate and return the permanent of the `m \times n` - matrix ``self`` using Ryser's algorithm. + matrix ``self``. + + By default it uses Ryser's algorithm, but setting ``algorithm`` to + "ButeraPernici" you can use the algorithm of Butera and Pernici (which + is well suited for band matrices, i.e. matrices whose entries are + concentrated near the diagonal). Let `A = (a_{i,j})` be an `m \times n` matrix over any commutative ring, with `m \le n`. The permanent of @@ -708,16 +713,25 @@ cdef class Matrix(matrix1.Matrix): - ``A`` -- matrix of size `m \times n` with `m \leq n` + - ``algorithm`` -- either "Ryser" (default) or "ButeraPernici". The + Butera-Pernici algorithm takes advantage of presence of zeros and is + very well suited for sparse matrices. + OUTPUT: permanent of the matrix `A` ALGORITHM: - Modification of theorem 7.1.1. from Brualdi and Ryser: - Combinatorial Matrix Theory. Instead of deleting columns from - `A`, we choose columns from `A` and calculate the - product of the row sums of the selected submatrix. + The Ryser algorithm is implemented in the method + :meth:`_permanent_ryser`. It is a modification of theorem 7.1.1. from + Brualdi and Ryser: Combinatorial Matrix Theory. Instead of deleting + columns from `A`, we choose columns from `A` and calculate the product + of the row sums of the selected submatrix. + + The Butera-Pernici algorithm is implemented in the function + :func:`~sage.matrix.matrix_misc.permanental_minor_polynomial`. It takes + advantage of cancellations that may occur in the computations. EXAMPLES:: @@ -740,6 +754,7 @@ cdef class Matrix(matrix1.Matrix): sage: A.permanent() 36.0000000000000 + See Sloane's sequence OEIS A079908(3) = 36, "The Dancing School Problems" @@ -757,6 +772,16 @@ cdef class Matrix(matrix1.Matrix): sage: A.permanent() 32 + + A huge determinant that can not be reasonably computed with the Ryser + algorithm (a `100 \times 100` band matrix with width `5`):: + + sage: n, w = 100, 5 + sage: m = matrix([[(i+j)%5 + 1 if abs(i-j) <= 5 else 0 + ....: for i in range(100)] for j in range(100)]) + sage: m.permanent(algorithm="ButeraPernici") + 40201088396031257747704937070726537334335585753726771681430778348900577273826792657139261321475297717474885781433 + See Minc: Permanents, Example 2.1, p. 5. :: @@ -786,6 +811,27 @@ cdef class Matrix(matrix1.Matrix): - Jaap Spies (2006-02-21): added definition of permanent """ + if algorithm == "Ryser": + return self._permanent_ryser() + + elif algorithm == "ButeraPernici": + return permanental_minor_polynomial(self, True) + + else: + raise ValueError("algorithm must be one of \"Ryser\" or \"ButeraPernici\".") + + def _permanent_ryser(self): + r""" + Return the permanent computed using Ryser algorithm. + + See :meth:`permanent` for the documentation. + + EXAMPLES:: + + sage: m = matrix([[1,1],[1,1]]) + sage: m._permanent_ryser() + 2 + """ cdef Py_ssize_t m, n, r cdef int sn @@ -809,8 +855,7 @@ cdef class Matrix(matrix1.Matrix): perm = perm + sn * _binomial(n-r, m-r) * s return perm - - def permanental_minor(self, Py_ssize_t k): + def permanental_minor(self, Py_ssize_t k, algorithm="Ryser"): r""" Return the permanental `k`-minor of an `m \times n` matrix. @@ -831,11 +876,10 @@ cdef class Matrix(matrix1.Matrix): INPUT: - - ``self`` -- matrix of size `m \times n` with `m \leq n` - - OUTPUT: + - ``k`` -- the size of the minor - The permanental `k`-minor of the matrix ``self``. + - ``algorithm`` -- either "Reiser" (default) or "ButeraPernici". The + Butera-Pernici algorithm is well suited for band matrices. EXAMPLES:: @@ -883,6 +927,32 @@ cdef class Matrix(matrix1.Matrix): - Jaap Spies (2006-02-19) """ + if algorithm == "Ryser": + return self._permanental_minor_ryser(k) + + elif algorithm == "ButeraPernici": + p = permanental_minor_polynomial(self) + return p[k] + + else: + raise ValueError("algorithm must be one of \"Ryser\" or \"ButeraPernici\".") + + def _permanental_minor_ryser(self, Py_ssize_t k): + r""" + Compute the `k`-th permanental minor using Ryser algorithm. + + See :meth:`permanental_minor` for the documentation. + + EXAMPLES:: + + sage: m = matrix([[1,2,1],[3,4,3],[5,6,5]]) + sage: m._permanental_minor_ryser(1) + 30 + sage: m._permanental_minor_ryser(2) + 174 + sage: m._permanental_minor_ryser(3) + 136 + """ m = self._nrows n = self._ncols if not m <= n: @@ -900,7 +970,7 @@ cdef class Matrix(matrix1.Matrix): pm = pm + self.matrix_from_rows_and_columns(rows, cols).permanent() return pm - def rook_vector(self, check = False): + def rook_vector(self, algorithm="Ryser", check=False): r""" Return the rook vector of the matrix ``self``. @@ -924,6 +994,10 @@ cdef class Matrix(matrix1.Matrix): - ``check`` -- Boolean (default: ``False``) determining whether to check that ``self`` is a (0,1)-matrix. + - ``algorithm`` - either "Ryser" or "ButeraPernici" (default). The + Butera-Pernici algorithm is very well suited for band matrices but + Ryser one might be faster on simple and small instances. + OUTPUT: The rook vector of the matrix ``self``. @@ -948,6 +1022,7 @@ cdef class Matrix(matrix1.Matrix): - Jaap Spies (2006-02-24) - Mario Pernici (2014-07-01) """ + #TODO: do we need to forbid m <= n?? m = self._nrows n = self._ncols if not m <= n: @@ -962,7 +1037,15 @@ cdef class Matrix(matrix1.Matrix): if not (x == 0 or x == 1): raise ValueError("must have zero or one, but we have (=%s)" % x) - return permanental_minor_vector(self) + if algorithm == "Ryser": + return [self.permanental_minor(k,algorithm="Ryser") for k in range(m+1)] + + elif algorithm == "ButeraPernici": + p = permanental_minor_polynomial(self) + return [p[k] for k in range(m+1)] + + else: + raise ValueError("algorithm must be one of \"Ryser\" or \"ButeraPernici\".") def minors(self, k): r""" From e216b924358b162514c8d6f344a21d0ae36f0251 Mon Sep 17 00:00:00 2001 From: Martin von Gagern Date: Fri, 27 Jun 2014 15:35:21 +0200 Subject: [PATCH 054/217] Make number of frames available after rendering using generator. The APNG pipeline requires the number of frames up front, and other converters might benefit as well. Once the frames are rendered (and don't get re-rendered), the number of frames is obviously fixed. As an alternative, we could turn the generator into a list when rendering, thus avoiding the extra attribute. But the downside of this is that the list might be around for considerable time, eating memory. The example animation is changed since the previous example would generate warnings during rendering, due to negative values in the default range of x. --- src/sage/plot/animate.py | 38 ++++++++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/src/sage/plot/animate.py b/src/sage/plot/animate.py index c4078f8d2e2..fe0aa497af6 100644 --- a/src/sage/plot/animate.py +++ b/src/sage/plot/animate.py @@ -184,10 +184,15 @@ class Animation(SageObject): ....: for k in srange(0,2*pi,0.3)]) sage: a.show() # optional -- ImageMagick - Do not convert input iterator to a list:: + Do not convert input iterator to a list, but ensure that + the frame count is known after rendering the frames:: - sage: a = animate((x^p for p in sxrange(1,2,.1))); a + sage: a = animate((plot(x^p, (x,0,2)) for p in sxrange(1,2,.1))); a Animation with unknown number of frames + sage: a.png() # long time + '.../' + sage: len(a) # long time + 10 sage: a._frames Date: Thu, 11 Dec 2014 20:10:05 +0100 Subject: [PATCH 055/217] trac #17385: better error message --- src/sage/graphs/digraph.py | 8 ++++---- src/sage/graphs/graph.py | 9 +++++---- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/sage/graphs/digraph.py b/src/sage/graphs/digraph.py index 64f1d8dcfd1..86631c2e20a 100644 --- a/src/sage/graphs/digraph.py +++ b/src/sage/graphs/digraph.py @@ -584,7 +584,7 @@ def __init__(self, data=None, pos=None, loops=None, format=None, sage: DiGraph([(0,0)], loops=False) Traceback (most recent call last): ... - ValueError: No loops but dict has loop at 0. + ValueError: The digraph was built with loops=False but input data has a loop at 0. """ msg = '' GenericGraph.__init__(self) @@ -818,7 +818,7 @@ def __init__(self, data=None, pos=None, loops=None, format=None, elif format == 'DiGraph': if loops is None: loops = data.allows_loops() elif not loops and data.has_loops(): - raise ValueError("No loops but input digraph has loops.") + raise ValueError("The digraph was built with loops=False but input data has a loop") if multiedges is None: multiedges = data.allows_multiple_edges() elif not multiedges: e = data.edges(labels=False) @@ -844,7 +844,7 @@ def __init__(self, data=None, pos=None, loops=None, format=None, if not loops and any(u in neighb for u,neighb in data.iteritems()): if loops is False: u = (u for u,neighb in data.iteritems() if u in neighb).next() - raise ValueError("No loops but dict has loop at {}.".format(u)) + raise ValueError("The digraph was built with loops=False but input data has a loop at {}.".format(u)) loops = True if loops is None: loops = False @@ -868,7 +868,7 @@ def __init__(self, data=None, pos=None, loops=None, format=None, if not loops and any(u in neighb for u,neighb in data.iteritems()): if loops is False: u = (u for u,neighb in data.iteritems() if u in neighb).next() - raise ValueError("No loops but dict has loop at {}.".format(u)) + raise ValueError("The digraph was built with loops=False but input data has a loop at {}.".format(u)) loops = True if loops is None: loops = False diff --git a/src/sage/graphs/graph.py b/src/sage/graphs/graph.py index 32ffcfc9eb0..a85bcec525d 100644 --- a/src/sage/graphs/graph.py +++ b/src/sage/graphs/graph.py @@ -1119,7 +1119,7 @@ def __init__(self, data=None, pos=None, loops=None, format=None, sage: Graph([(0,0)], loops=False) Traceback (most recent call last): ... - ValueError: No loops but dict has a loop at 0. + ValueError: The graph was built with loops=False but input data has a loop at 0. """ GenericGraph.__init__(self) msg = '' @@ -1414,7 +1414,7 @@ def __init__(self, data=None, pos=None, loops=None, format=None, elif format == 'Graph': if loops is None: loops = data.allows_loops() elif not loops and data.has_loops(): - raise ValueError("No loops but input graph has loops.") + raise ValueError("The graph was built with loops=False but input data has a loop") if multiedges is None: multiedges = data.allows_multiple_edges() elif not multiedges: e = data.edges(labels=False) @@ -1442,7 +1442,7 @@ def __init__(self, data=None, pos=None, loops=None, format=None, if not loops and any(u in neighb for u,neighb in data.iteritems()): if loops is False: u = (u for u,neighb in data.iteritems() if u in neighb).next() - raise ValueError("No loops but dict has loop at {}.".format(u)) + raise ValueError("The graph was built with loops=False but input data has a loop at {}.".format(u)) loops = True if loops is None: loops = False @@ -1471,7 +1471,8 @@ def __init__(self, data=None, pos=None, loops=None, format=None, if not loops and any(u in neighb for u,neighb in data.iteritems()): if loops is False: u = (u for u,neighb in data.iteritems() if u in neighb).next() - raise ValueError("No loops but dict has a loop at {}.".format(u)) + raise ValueError("The graph was built with loops=False but input data has a loop at {}.".format(u)) + loops = True if loops is None: loops = False From c76177e20cc6bb21c38c1c8123a2f5dde6fa4507 Mon Sep 17 00:00:00 2001 From: Mario Pernici Date: Fri, 12 Dec 2014 11:21:48 +0100 Subject: [PATCH 056/217] Added `prec` parameter to `permanental_minor_polynomial`; small changes in the documentation --- src/sage/matrix/matrix2.pyx | 2 +- src/sage/matrix/matrix_misc.py | 49 ++++++++++++++++++++++++---------- 2 files changed, 36 insertions(+), 15 deletions(-) diff --git a/src/sage/matrix/matrix2.pyx b/src/sage/matrix/matrix2.pyx index 1f48361dd28..bf54461064d 100644 --- a/src/sage/matrix/matrix2.pyx +++ b/src/sage/matrix/matrix2.pyx @@ -931,7 +931,7 @@ cdef class Matrix(matrix1.Matrix): return self._permanental_minor_ryser(k) elif algorithm == "ButeraPernici": - p = permanental_minor_polynomial(self) + p = permanental_minor_polynomial(self, prec=k+1) return p[k] else: diff --git a/src/sage/matrix/matrix_misc.py b/src/sage/matrix/matrix_misc.py index 9ba42b06ba5..676ee79d6d5 100644 --- a/src/sage/matrix/matrix_misc.py +++ b/src/sage/matrix/matrix_misc.py @@ -202,7 +202,7 @@ def weak_popov_form(M,ascend=True): # return reduced matrix and operations matrix return (matrix(r)/den, matrix(N), d) -def prm_mul(p1, p2, mask_free): +def prm_mul(p1, p2, mask_free, prec): """ Return the product of ``p1`` and ``p2``, putting free variables in ``mask_free`` to `1`. @@ -212,24 +212,25 @@ def prm_mul(p1, p2, mask_free): INPUT: - - ``p1,p2`` -- polynomials as dictionaries + - `p1,p2` -- polynomials as dictionaries - - ``mask_free`` -- an integer mask that give the list of free variables + - `mask_free` -- an integer mask that give the list of free variables (the `i`-th variable is free if the `i`-th bit of ``mask_free`` is `1`) + - `prec` -- if `prec` is not None, truncate the product at precision `prec` + EXAMPLES:: sage: from sage.matrix.matrix_misc import prm_mul sage: t = polygen(ZZ, 't') sage: p1 = {0: 1, 1: t, 4: t} sage: p2 = {0: 1, 1: t, 2: t} - sage: prm_mul(p1, p2, 1) + sage: prm_mul(p1, p2, 1, None) {0: 2*t + 1, 2: t^2 + t, 4: t^2 + t, 6: t^2} """ p = {} if not p2: return p - get = p.get for exp1, v1 in p1.iteritems(): if v1.is_zero(): continue @@ -237,6 +238,8 @@ def prm_mul(p1, p2, mask_free): if exp1 & exp2: continue v = v1 * v2 + if prec is not None: + v._unsafe_mutate(prec, 0) exp = exp1 | exp2 exp = exp ^ (exp & mask_free) if exp not in p: @@ -245,10 +248,21 @@ def prm_mul(p1, p2, mask_free): p[exp] += v return p -def permanental_minor_polynomial(A, permanent_only=False, var='t'): +def permanental_minor_polynomial(A, permanent_only=False, var='t', prec=None): r""" Return the polynomial of the sums of permanental minors of ``A``. + INPUT: + + - `A` -- a matrix + + - `permanent_only` -- if True, return only the permanent of `A` + + - `var` -- name of the polynomial variable + + - `prec` -- if prec is not None, truncate the polynomial at precision `prec` + + The polynomial of the sums of permanental minors is .. MATH:: @@ -283,6 +297,8 @@ def permanental_minor_polynomial(A, permanent_only=False, var='t'): 3*t^2 + 5*t + 1 sage: permanental_minor_polynomial(m, permanent_only=True) 3 + sage: permanental_minor_polynomial(m, prec=2) + 5*t + 1 :: @@ -315,8 +331,8 @@ def permanental_minor_polynomial(A, permanent_only=False, var='t'): ALGORITHM: - The permanent `perm(A)` of a `n \times n` matrix `A` is the coefficient of the - `x_1 x_2 \ldots x_n` monomial in + The permanent `perm(A)` of a `n \times n` matrix `A` is the coefficient + of the `x_1 x_2 \ldots x_n` monomial in .. MATH:: @@ -377,8 +393,8 @@ def permanental_minor_polynomial(A, permanent_only=False, var='t'): where `\eta_1,..,\eta_{j-1}` are replaced by `1` in `p_1`. Informally, we can "integrate" these variables *before* performing the product. More - generally, if a monomial `\eta_i` is missing in one of the term of a - product, then it can be integrated in the other term. + generally, if a monomial `\eta_i` is missing in one of the terms of a + product of two terms, then it can be integrated in the other term. Now let us consider an `m \times n` matrix with `m \leq n`. The *sum of permanental `k`-minors of `A`* is @@ -415,9 +431,9 @@ def permanental_minor_polynomial(A, permanent_only=False, var='t'): the key `2^i` (or in Python ``1 << i``). The keys associated to products are obtained by considering the development in base `2`: to the monomial `\eta_{i_1} \ldots \eta_{i_k}` is associated the key - `2^{i_1} + \ldots + 2^{i_k}`. So the product `\eta_1 \eta_2` corresponds to the - key `6 = (110)_2` while `\eta_0 \eta_3` has key `5 = (101)_2`. In - particular all operations on monomials are implemented via bitwise + `2^{i_1} + \ldots + 2^{i_k}`. So the product `\eta_1 \eta_2` corresponds + to the key `6 = (110)_2` while `\eta_0 \eta_3` has key `9 = (1001)_2`. + In particular all operations on monomials are implemented via bitwise operations on the keys. REFERENCES: @@ -425,6 +441,11 @@ def permanental_minor_polynomial(A, permanent_only=False, var='t'): .. [ButPer] P. Butera and M. Pernici "Sums of permanental minors using Grassmann algebra", :arxiv:`1406.5337` """ + if permanent_only: + prec = None + if prec is not None and prec == 0: + raise ValueError('the argument `prec` must be a positive integer') + K = PolynomialRing(A.base_ring(), var) nrows = A.nrows() ncols = A.ncols() @@ -454,7 +475,7 @@ def permanental_minor_polynomial(A, permanent_only=False, var='t'): vars_to_do.remove(jj) else: j += 1 - p = prm_mul(p, p1, mask_free) + p = prm_mul(p, p1, mask_free, prec) if not p: return K.zero() From 6f2c1c7ee024981aa1f761bbea8819a59089e46a Mon Sep 17 00:00:00 2001 From: Mario Pernici Date: Fri, 12 Dec 2014 20:05:46 +0100 Subject: [PATCH 057/217] used default "ButeraPernici" algorithm in `rook_vector` --- src/sage/matrix/matrix2.pyx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/sage/matrix/matrix2.pyx b/src/sage/matrix/matrix2.pyx index bf54461064d..40bbcdf7660 100644 --- a/src/sage/matrix/matrix2.pyx +++ b/src/sage/matrix/matrix2.pyx @@ -970,7 +970,7 @@ cdef class Matrix(matrix1.Matrix): pm = pm + self.matrix_from_rows_and_columns(rows, cols).permanent() return pm - def rook_vector(self, algorithm="Ryser", check=False): + def rook_vector(self, algorithm="ButeraPernici", check=False): r""" Return the rook vector of the matrix ``self``. @@ -994,8 +994,7 @@ cdef class Matrix(matrix1.Matrix): - ``check`` -- Boolean (default: ``False``) determining whether to check that ``self`` is a (0,1)-matrix. - - ``algorithm`` - either "Ryser" or "ButeraPernici" (default). The - Butera-Pernici algorithm is very well suited for band matrices but + - ``algorithm`` - either "Ryser" or "ButeraPernici" (default) Ryser one might be faster on simple and small instances. OUTPUT: From 472f7e0148e2a1748fd79a03a03cd70946f087d4 Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Sat, 13 Dec 2014 12:10:39 +0100 Subject: [PATCH 058/217] trac #17385: fix doctest in affine finite crystals --- src/doc/en/thematic_tutorials/lie/affine_finite_crystals.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/doc/en/thematic_tutorials/lie/affine_finite_crystals.rst b/src/doc/en/thematic_tutorials/lie/affine_finite_crystals.rst index e11fe6d9c83..dd714b7baee 100644 --- a/src/doc/en/thematic_tutorials/lie/affine_finite_crystals.rst +++ b/src/doc/en/thematic_tutorials/lie/affine_finite_crystals.rst @@ -356,8 +356,8 @@ up to a relabeling of the arrows:: sage: f = { 1:1, 0:2, 2:0 } sage: for u,v,label in Gdual.edges(): ....: Gdual.set_edge_label(u,v,f[label]) - sage: G.is_isomorphic(Gdual, edge_labels = True, certify = True) - (True, {[[1]]: [[-2]], [[2]]: [[-1]], [[-2]]: [[1]], [[-1]]: [[2]], []: [[0]]}) + sage: G.is_isomorphic(Gdual, edge_labels = True) + True .. image:: ../media/KR_Atwisted_dual.png :scale: 60 From 19350a4574f4caa2e0b5ad08f66e51930b647a42 Mon Sep 17 00:00:00 2001 From: Mario Pernici Date: Mon, 15 Dec 2014 12:36:42 +0100 Subject: [PATCH 059/217] Added "Godsil" algorithm to `rook_vector`. --- src/sage/matrix/matrix2.pyx | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/sage/matrix/matrix2.pyx b/src/sage/matrix/matrix2.pyx index 40bbcdf7660..48fe24c26d0 100644 --- a/src/sage/matrix/matrix2.pyx +++ b/src/sage/matrix/matrix2.pyx @@ -995,6 +995,7 @@ cdef class Matrix(matrix1.Matrix): to check that ``self`` is a (0,1)-matrix. - ``algorithm`` - either "Ryser" or "ButeraPernici" (default) + or "Godsil"; Ryser one might be faster on simple and small instances. OUTPUT: @@ -1021,14 +1022,10 @@ cdef class Matrix(matrix1.Matrix): - Jaap Spies (2006-02-24) - Mario Pernici (2014-07-01) """ - #TODO: do we need to forbid m <= n?? m = self._nrows n = self._ncols - if not m <= n: - raise ValueError("must have m <= n, but m (=%s) and n (=%s)" - % (m, n)) - if check: + if check or algorithm == "Godsil": # verify that self[i, j] in {0, 1} for i in range(m): for j in range(n): @@ -1037,14 +1034,25 @@ cdef class Matrix(matrix1.Matrix): raise ValueError("must have zero or one, but we have (=%s)" % x) if algorithm == "Ryser": + #TODO: do we need to forbid m <= n?? + if not m <= n: + raise ValueError("must have m <= n, but m (=%s) and n (=%s)" + % (m, n)) return [self.permanental_minor(k,algorithm="Ryser") for k in range(m+1)] elif algorithm == "ButeraPernici": p = permanental_minor_polynomial(self) return [p[k] for k in range(m+1)] + elif algorithm == "Godsil": + from sage.graphs.bipartite_graph import BipartiteGraph + g = BipartiteGraph(self) + p = g.matching_polynomial() + n = p.degree() + return [p[i]*(-1)**((n - i)/2) for i in range(n,-1,-2)] + else: - raise ValueError("algorithm must be one of \"Ryser\" or \"ButeraPernici\".") + raise ValueError('algorithm must be one of "Ryser", "ButeraPernici" or "Godsil".') def minors(self, k): r""" From 330915f59af659b9b8a0a384b9f8a6a75214f82e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Mon, 15 Dec 2014 15:33:22 -0500 Subject: [PATCH 060/217] trac #16412 refactoring the faq --- src/doc/en/faq/faq-general.rst | 74 +++------------------------------- src/doc/en/faq/faq-usage.rst | 48 +++++++++++----------- 2 files changed, 31 insertions(+), 91 deletions(-) diff --git a/src/doc/en/faq/faq-general.rst b/src/doc/en/faq/faq-general.rst index e092455f96c..eee14537cfd 100644 --- a/src/doc/en/faq/faq-general.rst +++ b/src/doc/en/faq/faq-general.rst @@ -207,79 +207,17 @@ lines of new Python and Cython code. See for source code analysis of the latest stable Sage release. -Who uses Sage? -"""""""""""""" - -The following is an incomplete list of institutions and projects that -use Sage. If any institution or project is missing, please let us know -by reporting to the -`sage-devel `_ -mailing list. - -#. `California Institute of Technology `_, - Pasadena, California, USA -#. `California Polytechnic State University `_, - San Luis Obispo, CA, USA -#. `Chang Gung University `_, - Taiwan -#. `Chapman University `_, - Orange, CA, USA -#. `Clemson University `_, - Clemson, South Carolina, USA -#. `Drake University `_, - Des Moines, IA, USA -#. `FEMhub `_, - an open source distribution of scientific computing code enhanced - with a unified Python interface. The FEMhub Notebook is based on - the Sage Notebook. -#. `Gordon College `_, - Wenham, MA, USA -#. `Korea Advanced Institute of Science and Technology `_, - Daejeon, Korea -#. `Mendel University in Brno `_, - Czech Republic -#. `Reykjavik University `_, - Iceland -#. `Universidad Autónoma de Madrid `_, - Spain -#. `Universidad de la República `_, - Montevideo, Uruguay -#. `Universitat Politècnica de Catalunya `_, - Barcelona, Catalonia, Spain -#. `Université Claude Bernard Lyon 1 `_, - France -#. `Université de Provence `_, - Marseille, France -#. `Universiteit Leiden `_, - The Netherlands -#. `University of Canterbury `_, - Christchurch, New Zealand -#. `University of Minnesota Duluth `_, - Duluth, MN, USA -#. `University of Nevada, Reno `_, - Reno, NV, USA -#. `University of Puget Sound `_, - Tacoma, WA, USA -#. `University of Washington `_, - Seattle, Washington, USA -#. `University of Wisconsin, Oshkosh `_, - Oshkosh, WI, USA -#. `US Naval Academy `_, - Annapolis, Maryland, USA - - How do I get help? """""""""""""""""" -Sage has two very active email lists: +For support about usage of Sage, there are two options: +* The question-and-answer website `ask.sagemath.org `_ +* The email list `sage-support `_ -* ``sage-devel``: http://groups.google.com/group/sage-devel -* ``sage-support``: http://groups.google.com/group/sage-support +For support about development of Sage, there is an email list +`sage-devel `_ -There is also a very active IRC channels: ``#sage-devel`` on -freenode. Many developers also actively blog and also post other -Sage-related tutorials and talks. See -http://www.sagemath.org/help.html for a listing of these resources. +See http://www.sagemath.org/help.html for a listing of other resources. Wouldn't it be way better if Sage did not ship as a gigantic bundle? diff --git a/src/doc/en/faq/faq-usage.rst b/src/doc/en/faq/faq-usage.rst index 5d948693728..26dbe8a65f0 100644 --- a/src/doc/en/faq/faq-usage.rst +++ b/src/doc/en/faq/faq-usage.rst @@ -8,6 +8,23 @@ FAQ: Using Sage How do I get started? """"""""""""""""""""" +You can try out Sage without downloading anything using +SageMathCloud™. Go to http://cloud.sagemath.org and set up a free +account. If you log in, you will access to the latest version of Sage +and to many other software. + +There is also a "once-off" version of Sage called the Sage cell server, +available for doing one computation at a time at http://sagecell.sagemath.org/ + +A final possibility is to go to a public Sage notebook server and +set up a free account. If you log in, you will be working on a free Sage +notebook server that will work identically to the one you get with +Sage. The server http://sagenb.org is one such, though it runs an older +version of Sage and is scheduled to be retired in the future in favor of +the SageMathCloud. + +To download a pre-built binary Sage distribution, visit the page + You can try out Sage without downloading anything. Go to http://www.sagenb.org and set up a free account. If you log in, you will be working on a free Sage notebook server that will work @@ -17,16 +34,17 @@ http://www.sagemath.org/download.html and click on the link for the binary for your operating system. The source code of Sage is also available for you to download and use. Go to http://www.sagemath.org/download-source.html to download the tar -archive for any release of Sage. Previous releases of Sage are -available at http://www.sagemath.org/src-old. +archive for any release of Sage. + + +The Sage notebook runs within a web browser. To start the notebook, +issue the following command in a terminal, if ``sage`` is in your ``PATH`` :: -The Sage notebook runs within a web browser. You can run Sage in a -browser that is not the system default. To do so, issue the following -command :: + sage -notebook - env SAGE_BROWSER=opera /usr/bin/sage -notebook +You can also run it from the command line of sage:: -either from the command prompt or as a menu command for Sage. + sage: notebook() What are Sage's prerequisites? @@ -368,22 +386,6 @@ and to reload it, you would just do :: my_stuff = load(DATA + "my_stuff") -I get an error from jsMath or the math symbols don't look right when displaying in the notebook. -"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" - -If you see the error :: - - It looks like jsMath failed to set up properly (error code -7). I will try to keep going, but it could get ugly. - -you have not installed the TeX fonts which help jsMath render -beautiful typeset mathematics. To get the nice TeX display with -jsMath, please download a set of fonts from here from -http://www.math.union.edu/~dpvc/jsMath/download/jsMath-fonts.html. -If you are on Linux/Unix, ignore the instructions on the page and just -unzip the fonts into your ``~/.fonts`` directory. You can also install -the ``jsmath-fonts`` package. - - Does Sage contain a function similar to Mathematica's ToCharacterCode[]? """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" From 7679633493e48e68d4631b27536ba84b920f4f99 Mon Sep 17 00:00:00 2001 From: Karl-Dieter Crisman Date: Mon, 15 Dec 2014 16:12:29 -0500 Subject: [PATCH 061/217] More improvements to FAQ --- src/doc/en/faq/faq-general.rst | 52 +++++++++++++++++++++------------- src/doc/en/faq/faq-usage.rst | 26 ++++++++++------- src/doc/en/faq/index.rst | 6 ++-- 3 files changed, 50 insertions(+), 34 deletions(-) diff --git a/src/doc/en/faq/faq-general.rst b/src/doc/en/faq/faq-general.rst index eee14537cfd..a8e5b199078 100644 --- a/src/doc/en/faq/faq-general.rst +++ b/src/doc/en/faq/faq-general.rst @@ -15,11 +15,13 @@ predecessors, known as HECKE and Manin, came about because William Stein needed to write them as part of his research in number theory. Started by William in 2005 during his time at Harvard University, Sage combines best-of-breed free open source mathematics -software, packaging and unifying them through a common interface. Many -researchers in number theory, including William himself, use this -common interface to build upon and extend the functionalities of -underlying packages for number theory research. Such software packages -include +software, packaging and unifying them through a common interface. Since +then Sage has become something used not just by researchers in +number theory, but throughout the mathematical sciences. + +Sage builds upon and extends functionalities of many underlying +packages. Even from early on, when Sage was primarily used for +number theory, this included `Givaro `_, `MPIR `_, `NTL `_, @@ -28,7 +30,9 @@ and many others too numerous to list here. Students, teachers, professors, researchers throughout the world use Sage because they require a comprehensive free open source mathematics package that offers symbolic and numerical computation. Most of the time, people -are happy with what Sage has to offer. As is common throughout the +are happy with what Sage has to offer. + +As is common throughout the free open source software (FOSS) world, many people often identify cases where Sage lacks certain mathematics functionalities that they require. And so they delve into the underlying source code that @@ -39,7 +43,9 @@ use their favourite mathematics software packages from within Sage. The team is comprised of researchers in algebraic combinatorics. The team's stated mission is to improve Sage as an extensible toolbox for computer exploration in algebraic combinatorics, and foster code -sharing between researchers in this area. For detailed information +sharing between researchers in this area. + +For detailed information about why Sage exists, see William's personal `mathematics software biography `_. @@ -60,9 +66,10 @@ You pronounce "Sage" similar to how you would pronounce "sage" which refers to a wise person, or "sage" which refers to a plant. Some people pronounce "Sage" as "sarge", similar to how you would pronounce `Debian `_ -Sarge. However you pronounce "Sage", please -do not confuse the Sage project with an accounting software by the -same name. +Sarge. + +However you pronounce "Sage", please do not confuse the Sage project +with an accounting software by the same name. Who is behind this project? @@ -74,8 +81,9 @@ professors, researchers, software engineers, and people working in diverse areas of mathematics, science, engineering, software development, and all levels of education. The development of Sage has benefited from the financial support of numerous institutions, and the -previous and ongoing work of many authors of included components. A -list of direct contributors can be found on the +previous and ongoing work of many authors of included components. + +A list of (some) direct contributors can be found on the `Sage Development Map `_ and the history of changes can be found in the high-level `changelog `_. Refer @@ -97,8 +105,9 @@ practices. An underlying philosophical principle of Sage is to apply the system of open exchange and peer review that characterizes scientific communication to the development of mathematics software. Neither the Sage project nor the Sage Development Team make -any claims to being the original proponents of this principle. The -development model of Sage is largely inspired by the free software +any claims to being the original proponents of this principle. + +The development model of Sage is largely inspired by the free software movement as spearheaded by the `Free Software Foundation `_, and by the open source movement. One source of inspiration from within @@ -192,7 +201,7 @@ functionalities are made possible through FOSS projects such as * And many more too numerous to list here. An up-to-date list can be found on the page for the -`standard packages repository `_. +`standard packages repository `_. The principle programming languages of Sage are `Python `_ and @@ -203,7 +212,7 @@ and interfacing with C libraries and C extensions for Python. Sage integrates over 90 FOSS packages into a common interface. On top of these packages is the Sage library, which consists of over 700,000 lines of new Python and Cython code. See -`ohloh.net `_ +`openhub.net `_ for source code analysis of the latest stable Sage release. @@ -256,13 +265,16 @@ neither the Sage community nor the Sage Development Team make any claims that Sage is free of bugs. To do so would be an act of dishonesty. -A Sage release cycle usually lasts for about 3 to 4 weeks. Each +A Sage release cycle usually lasts for a few months, with several +betas appearing at a 2-3 week intervals. Each release cycle is usually chaired by a single release manager who looks after the Sage merge tree for the duration of the release cycle. During that time, the release manager often needs to devote the equivalent of full-time work to quality management and actively interacts with an international community of Sage users, developers, -and potential contributors. There have been a number of cases where +and potential contributors. + +There have been a number of cases where two Sage contributors paired up to be the release managers for a Sage release cycle. However, it is often the case that few people have the equivalent of 3 weeks' worth of free time to devote to release @@ -278,7 +290,9 @@ contributor can discourage another, so tradeoffs need to be made. To decide that a stabilization release would merge patches with bug fixes, and only fix bugs, would likely discourage someone from contributing when they have been told in advance that their positively -reviewed patches will not be merged. The Sage community believes in +reviewed patches will not be merged. + +The Sage community believes in the principle of "release early, release often". How the Sage project is organized and run differ greatly from that of a commercial software company. Contributors are all volunteers and this changes the dynamic diff --git a/src/doc/en/faq/faq-usage.rst b/src/doc/en/faq/faq-usage.rst index 26dbe8a65f0..80e68bd0f57 100644 --- a/src/doc/en/faq/faq-usage.rst +++ b/src/doc/en/faq/faq-usage.rst @@ -552,17 +552,6 @@ tracking the issue at so stay tuned. -I am using Mac OS X. Where do I put the jsMath "font" directory to eliminate the red box? -""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" - -See http://www.math.union.edu/~dpvc/jsMath/download/jsMath-fonts.html -where it says:: - - For Mac OS X users: download and unpack the archive, then drag - the fonts to your Library/Fonts folder (or to the FontBook, or - just double-click them and press the "install" button). - - The show command for plotting 3-D objects does not work. """""""""""""""""""""""""""""""""""""""""""""""""""""""" @@ -737,3 +726,18 @@ It is because of how functions are defined in Sage with the make this mistake inside of an ``if`` statement, you will get a ``SyntaxError`` before anything else goes wrong. So in this case, there is no problem. + + +How do I use a different browser with the Sage notebook? +"""""""""""""""""""""""""""""""""""""""""""""""""""""""" + +You will need to do this from the command line. Just run a command like this. + +* Linux (assuming you have Sage in ``/usr/bin``):: + + env SAGE_BROWSER=opera /usr/bin/sage -notebook + +* Mac (assuming you are in the directory of your downloaded Sage):: + + SAGE_BROWSER='open -a Firefox' ./sage -notebook + SAGE_BROWSER='open -a Google\ Chrome' ./sage -notebook diff --git a/src/doc/en/faq/index.rst b/src/doc/en/faq/index.rst index 3284dec5c99..5ccf5ddbbaa 100644 --- a/src/doc/en/faq/index.rst +++ b/src/doc/en/faq/index.rst @@ -6,11 +6,9 @@ Welcome to the Sage FAQ! ======================== -This FAQ was compiled and is maintained by Minh Van Nguyen -. - This work is licensed under a `Creative Commons Attribution-Share Alike -3.0 License`__. +3.0 License`__. With grateful thanks, we acknowledge it as being +originally compiled by Minh Van Nguyen. __ http://creativecommons.org/licenses/by-sa/3.0/ From 3e7297b35f94986b8e0f37f30a3b3d6ee3d3b699 Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Fri, 19 Dec 2014 00:20:46 +0100 Subject: [PATCH 062/217] trac #16603: documentation commit --- src/sage/matrix/matrix2.pyx | 246 +++++++++++++++++++-------------- src/sage/matrix/matrix_misc.py | 6 +- 2 files changed, 143 insertions(+), 109 deletions(-) diff --git a/src/sage/matrix/matrix2.pyx b/src/sage/matrix/matrix2.pyx index 48fe24c26d0..7ccce6b6371 100644 --- a/src/sage/matrix/matrix2.pyx +++ b/src/sage/matrix/matrix2.pyx @@ -682,17 +682,10 @@ cdef class Matrix(matrix1.Matrix): def permanent(self, algorithm="Ryser"): r""" - Calculate and return the permanent of the `m \times n` - matrix ``self``. + Return the permanent of this matrix. - By default it uses Ryser's algorithm, but setting ``algorithm`` to - "ButeraPernici" you can use the algorithm of Butera and Pernici (which - is well suited for band matrices, i.e. matrices whose entries are - concentrated near the diagonal). - - Let `A = (a_{i,j})` be an `m \times n` matrix over - any commutative ring, with `m \le n`. The permanent of - `A` is + Let `A = (a_{i,j})` be an `m \times n` matrix over any + commutative ring with `m \le n`. The permanent of `A` is .. MATH:: @@ -700,15 +693,19 @@ cdef class Matrix(matrix1.Matrix): = \sum_\pi a_{1,\pi(1)} a_{2,\pi(2)} \cdots a_{m,\pi(m)} where the summation extends over all one-to-one functions - `\pi` from `\{1, \ldots, m\}` to - `\{1, \ldots, n\}`. + `\pi` from `\{1, \ldots, m\}` to `\{1, \ldots, n\}`. The product `a_{1,\pi(1)} a_{2,\pi(2)} \cdots a_{m,\pi(m)}` is - called diagonal product. So the permanent of an + called *diagonal product*. So the permanent of an `m \times n` matrix `A` is the sum of all the diagonal products of `A`. + By default, this method uses Ryser's algorithm, but setting + ``algorithm`` to "ButeraPernici" you can use the algorithm of Butera and + Pernici (which is well suited for band matrices, i.e. matrices whose + entries are concentrated near the diagonal). + INPUT: - ``A`` -- matrix of size `m \times n` with `m \leq n` @@ -717,10 +714,6 @@ cdef class Matrix(matrix1.Matrix): Butera-Pernici algorithm takes advantage of presence of zeros and is very well suited for sparse matrices. - OUTPUT: - - permanent of the matrix `A` - ALGORITHM: The Ryser algorithm is implemented in the method @@ -735,28 +728,19 @@ cdef class Matrix(matrix1.Matrix): EXAMPLES:: - sage: M = MatrixSpace(ZZ,4,4) - sage: A = M([1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]) + sage: A = ones_matrix(4,4) sage: A.permanent() 24 - :: - - sage: M = MatrixSpace(QQ,3,6) - sage: A = M([1,1,1,1,0,0,0,1,1,1,1,0,0,0,1,1,1,1]) + sage: A = matrix(3,6,[1,1,1,1,0,0,0,1,1,1,1,0,0,0,1,1,1,1]) sage: A.permanent() 36 - - :: - - sage: M = MatrixSpace(RR,3,6) - sage: A = M([1.0,1.0,1.0,1.0,0,0,0,1.0,1.0,1.0,1.0,0,0,0,1.0,1.0,1.0,1.0]) - sage: A.permanent() + sage: B = A.change_ring(RR) + sage: B.permanent() 36.0000000000000 - - See Sloane's sequence OEIS A079908(3) = 36, "The Dancing School - Problems" + The permanent above is directed to the Sloane's sequence OEIS + A079908(3) = 36, "The Dancing School Problems" :: @@ -767,49 +751,43 @@ cdef class Matrix(matrix1.Matrix): :: - sage: M = MatrixSpace(ZZ,4,5) - sage: A = M([1,1,0,1,1,0,1,1,1,1,1,0,1,0,1,1,1,0,1,0]) + sage: A = matrix(4,5,[1,1,0,1,1,0,1,1,1,1,1,0,1,0,1,1,1,0,1,0]) sage: A.permanent() 32 - A huge determinant that can not be reasonably computed with the Ryser - algorithm (a `100 \times 100` band matrix with width `5`):: + algorithm (a `50 \times 50` band matrix with width `5`):: - sage: n, w = 100, 5 - sage: m = matrix([[(i+j)%5 + 1 if abs(i-j) <= 5 else 0 - ....: for i in range(100)] for j in range(100)]) - sage: m.permanent(algorithm="ButeraPernici") - 40201088396031257747704937070726537334335585753726771681430778348900577273826792657139261321475297717474885781433 + sage: n, w = 50, 5 + sage: A = matrix(ZZ, n, n, lambda i,j: (i+j)%5 + 1 if abs(i-j) <= w else 0) + sage: A.permanent(algorithm="ButeraPernici") + 57766972735511097036962481710892268404670105604676932908 See Minc: Permanents, Example 2.1, p. 5. :: - sage: M = MatrixSpace(QQ,2,2) - sage: A = M([1/5,2/7,3/2,4/5]) + sage: A = matrix(QQ,2,2,[1/5,2/7,3/2,4/5]) sage: A.permanent() 103/175 :: sage: R. = PolynomialRing(ZZ) - sage: A = MatrixSpace(R,2)([[a,1], [a,a+1]]) + sage: A = matrix(R,2,2,[a,1,a,a+1]) sage: A.permanent() a^2 + 2*a :: sage: R. = PolynomialRing(ZZ,2) - sage: A = MatrixSpace(R,2)([x, y, x^2, y^2]) + sage: A = matrix(R,2,2,[x, y, x^2, y^2]) sage: A.permanent() x^2*y + x*y^2 AUTHORS: - - Jaap Spies (2006-02-16) - - - Jaap Spies (2006-02-21): added definition of permanent + - Jaap Spies (2006-02-16 and 2006-02-21) """ if algorithm == "Ryser": return self._permanent_ryser() @@ -857,41 +835,42 @@ cdef class Matrix(matrix1.Matrix): def permanental_minor(self, Py_ssize_t k, algorithm="Ryser"): r""" - Return the permanental `k`-minor of an `m \times n` matrix. + Return the permanental `k`-minor of this matrix. - This is the sum of the permanents of all possible `k` by - `k` submatrices of `A`. + The *permanental* `k`-*minor* of a matrix `A` is the sum of the + permanents of all possible `k` by `k` submatrices of `A`. Note that the + maximal permanental minor is just the permanent. + + For a (0,1)-matrix `A` the permanental `k`-minor + counts the number of different selections of `k` 1's of + `A` with no two of the 1's on the same row and no two of the + 1's on the same column. See Brualdi and Ryser: Combinatorial Matrix Theory, p. 203. Note the typo `p_0(A) = 0` in that reference! For applications see Theorem 7.2.1 and Theorem 7.2.4. - Note that the permanental `m`-minor equals - `\mathrm{per}(A)` if `m = n`. + .. SEEALSO: - For a (0,1)-matrix `A` the permanental `k`-minor - counts the number of different selections of `k` 1's of - `A` with no two of the 1's on the same row and no two of the - 1's on the same column. + The method :meth:`rook_vector` returns the list of all permanental + minors. INPUT: - ``k`` -- the size of the minor - - ``algorithm`` -- either "Reiser" (default) or "ButeraPernici". The + - ``algorithm`` -- either "Ryser" (default) or "ButeraPernici". The Butera-Pernici algorithm is well suited for band matrices. EXAMPLES:: - sage: M = MatrixSpace(ZZ,4,4) - sage: A = M([1,0,1,0,1,0,1,0,1,0,10,10,1,0,1,1]) + sage: A = matrix(4,[1,0,1,0,1,0,1,0,1,0,10,10,1,0,1,1]) sage: A.permanental_minor(2) 114 :: - sage: M = MatrixSpace(ZZ,3,6) - sage: A = M([1,1,1,1,0,0,0,1,1,1,1,0,0,0,1,1,1,1]) + sage: A = matrix(3,6,[1,1,1,1,0,0,0,1,1,1,1,0,0,0,1,1,1,1]) sage: A.permanental_minor(0) 1 sage: A.permanental_minor(1) @@ -907,22 +886,22 @@ cdef class Matrix(matrix1.Matrix): sage: A.permanent() 36 - :: - - sage: A.permanental_minor(5) - 0 + The permanental minors of the "complement" matrix of `A` is + related to the permanent of `A`:: - For `C` the "complement" of `A`:: - - sage: M = MatrixSpace(ZZ,3,6) - sage: C = M([0,0,0,0,1,1,1,0,0,0,0,1,1,1,0,0,0,0]) sage: m, n = 3, 6 - sage: sum([(-1)^k * C.permanental_minor(k)*factorial(n-k)/factorial(n-m) for k in range(m+1)]) + sage: C = matrix(m, n, lambda i,j: 1 - A[i,j]) + sage: sum((-1)^k * C.permanental_minor(k)*factorial(n-k)/factorial(n-m) for k in range(m+1)) 36 See Theorem 7.2.1 of Brualdi and Ryser: Combinatorial Matrix Theory: per(A) + TESTS:: + + sage: A.permanental_minor(5) + 0 + AUTHORS: - Jaap Spies (2006-02-19) @@ -955,8 +934,6 @@ cdef class Matrix(matrix1.Matrix): """ m = self._nrows n = self._ncols - if not m <= n: - raise ValueError("must have m <= n, but m (=%s) and n (=%s)"%(m,n)) R = self._base_ring if k == 0: @@ -972,51 +949,107 @@ cdef class Matrix(matrix1.Matrix): def rook_vector(self, algorithm="ButeraPernici", check=False): r""" - Return the rook vector of the matrix ``self``. + Return the rook vector of this matrix. - Let `A` be an `m` by `n` (0,1)-matrix with `m \le n`. We identify - `A` with a chessboard where rooks can be placed on the fields - `(i, j)` with `a_{i,j} = 1`. The number - `r_k = p_k(A)` (the permanental `k`-minor) counts the number of + Let `A` be an `m` by `n` (0,1)-matrix. We identify `A` with a chessboard + where rooks can be placed on the fields `(i, j)` with `A_{i,j} = 1`. The + number `r_k = p_k(A)` (the permanental `k`-minor) counts the number of ways to place `k` rooks on this board so that no rook can attack another. - The rook vector of the matrix `A` is the list consisting of - `r_0, r_1, \ldots, r_m`. - - The rook polynomial is defined by + The *rook vector* of the matrix `A` is the list consisting of `r_0, + r_1, \ldots, r_m`. The *rook polynomial* is defined by `r(x) = \sum_{k=0}^m r_k x^k`. + The rook vector can be generalized to matrices defined over any rings + using permanental minors. Among the available algorithms, only "Godsil" + needs the condition on the entries to be either `0` or `1`. + + See :wikipedia:`Rook_polynomial` for more information and also the + method :meth:`permanental_minor` to compute individual permanental + minor. + INPUT: - - ``self`` -- an `m` by `n` (0,1)-matrix with `m \le n` + - ``self`` -- an `m` by `n` (0,1)-matrix - ``check`` -- Boolean (default: ``False``) determining whether to check that ``self`` is a (0,1)-matrix. - - ``algorithm`` - either "Ryser" or "ButeraPernici" (default) - or "Godsil"; - Ryser one might be faster on simple and small instances. + - ``algorithm`` - a string which must be either "Ryser" or + "ButeraPernici" (default) or "Godsil"; Ryser one might be faster on + simple and small instances. Godsil only accepts input in 0,1. - OUTPUT: + EXAMPLES: - The rook vector of the matrix ``self``. + The standard chessboard is an `8` by `8` grid in which any positions is + allowed. In that case one gets that the number of ways to position `4` + non-attacking rooks is `117600` while for `8` rooks it is `40320`:: - EXAMPLES:: + sage: ones_matrix(8,8).rook_vector() + [1, 64, 1568, 18816, 117600, 376320, 564480, 322560, 40320] + + These numbers are the coefficients of a modified Laguerre polynomial:: + + sage: x = polygen(ZZ) + sage: factorial(8) * laguerre(8,-x) + x^8 + 64*x^7 + 1568*x^6 + 18816*x^5 + 117600*x^4 + 376320*x^3 + + 564480*x^2 + 322560*x + 40320 + + An other example that we convert into a rook polynomial:: - sage: M = MatrixSpace(ZZ,3,6) - sage: A = M([1,1,1,1,0,0,0,1,1,1,1,0,0,0,1,1,1,1]) + sage: A = matrix(3,6, [1,1,1,1,0,0,0,1,1,1,1,0,0,0,1,1,1,1]) + sage: A + [1 1 1 1 0 0] + [0 1 1 1 1 0] + [0 0 1 1 1 1] sage: A.rook_vector() [1, 12, 40, 36] - :: - - sage: R. = PolynomialRing(ZZ) - sage: rv = A.rook_vector() - sage: rook_polynomial = sum([rv[k] * x^k for k in range(len(rv))]) - sage: rook_polynomial + sage: R = PolynomialRing(ZZ, 'x') + sage: R(A.rook_vector()) 36*x^3 + 40*x^2 + 12*x + 1 + Different algorithms are available:: + + sage: A = matrix([[1,0,0,1],[0,1,1,0],[0,1,1,0],[1,0,0,1]]) + sage: A.rook_vector(algorithm="ButeraPernici") + [1, 8, 20, 16, 4] + sage: A.rook_vector(algorithm="Ryser") + [1, 8, 20, 16, 4] + sage: A.rook_vector(algorithm="Godsil") + [1, 8, 20, 16, 4] + + An example with an exotic matrix (for which only Butera-Pernici and + Ryser algorithms are available):: + + sage: R. = PolynomialRing(GF(5)) + sage: A = matrix(R,[[1,x,y],[x*y,x**2+y,0]]) + sage: A.rook_vector(algorithm="ButeraPernici") + [1, x^2 + x*y + x + 2*y + 1, 2*x^2*y + x*y^2 + x^2 + y^2 + y] + sage: A.rook_vector(algorithm="Ryser") + [1, x^2 + x*y + x + 2*y + 1, 2*x^2*y + x*y^2 + x^2 + y^2 + y] + sage: A.rook_vector(algorithm="Godsil") + Traceback (most recent call last): + ... + ValueError: coefficients must be zero or one, but we have 'x' in + position (0,1). + + sage: B = A.transpose() + sage: B.rook_vector(algorithm="ButeraPernici") + [1, x^2 + x*y + x + 2*y + 1, 2*x^2*y + x*y^2 + x^2 + y^2 + y] + sage: B.rook_vector(algorithm="Ryser") + [1, x^2 + x*y + x + 2*y + 1, 2*x^2*y + x*y^2 + x^2 + y^2 + y] + + TESTS:: + + sage: matrix([[0,0],[0,0]]).rook_vector(algorithm="ButeraPernici") + [1, 0, 0] + sage: matrix([[0,0],[0,0]]).rook_vector(algorithm="Ryser") + [1, 0, 0] + sage: matrix([[0,0],[0,0]]).rook_vector(algorithm="Godsil") + [1, 0, 0] + AUTHORS: - Jaap Spies (2006-02-24) @@ -1024,32 +1057,31 @@ cdef class Matrix(matrix1.Matrix): """ m = self._nrows n = self._ncols + mn = min(m,n) if check or algorithm == "Godsil": # verify that self[i, j] in {0, 1} + zero = self.base_ring().zero() + one = self.base_ring().one() for i in range(m): for j in range(n): x = self.get_unsafe(i, j) - if not (x == 0 or x == 1): - raise ValueError("must have zero or one, but we have (=%s)" % x) + if x != zero and x != one: + raise ValueError("coefficients must be zero or one, but we have '{}' in position ({},{}).".format(x,i,j)) if algorithm == "Ryser": - #TODO: do we need to forbid m <= n?? - if not m <= n: - raise ValueError("must have m <= n, but m (=%s) and n (=%s)" - % (m, n)) - return [self.permanental_minor(k,algorithm="Ryser") for k in range(m+1)] + return [self.permanental_minor(k,algorithm="Ryser") for k in range(mn+1)] elif algorithm == "ButeraPernici": p = permanental_minor_polynomial(self) - return [p[k] for k in range(m+1)] + return [p[k] for k in range(mn+1)] elif algorithm == "Godsil": from sage.graphs.bipartite_graph import BipartiteGraph g = BipartiteGraph(self) p = g.matching_polynomial() - n = p.degree() - return [p[i]*(-1)**((n - i)/2) for i in range(n,-1,-2)] + d = p.degree() + return [p[i]*(-1)**((d - i)/2) for i in range(d,-1,-2)] else: raise ValueError('algorithm must be one of "Ryser", "ButeraPernici" or "Godsil".') diff --git a/src/sage/matrix/matrix_misc.py b/src/sage/matrix/matrix_misc.py index 676ee79d6d5..253ae26d70f 100644 --- a/src/sage/matrix/matrix_misc.py +++ b/src/sage/matrix/matrix_misc.py @@ -443,8 +443,10 @@ def permanental_minor_polynomial(A, permanent_only=False, var='t', prec=None): """ if permanent_only: prec = None - if prec is not None and prec == 0: - raise ValueError('the argument `prec` must be a positive integer') + else: + prec = int(prec) + if prec == 0: + raise ValueError('the argument `prec` must be a positive integer') K = PolynomialRing(A.base_ring(), var) nrows = A.nrows() From b057e61d3cedd3815870617f6c352b448642bace Mon Sep 17 00:00:00 2001 From: Martin von Gagern Date: Fri, 27 Jun 2014 15:33:56 +0200 Subject: [PATCH 063/217] Add APNG support. The benefit here is that APNG does not add any extra dependencies. The downside is that only some browsers support APNG rendering. Others would default to showing only the first frame, but that might be confusing to users who expect a full animation. --- src/sage/plot/animate.py | 430 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 415 insertions(+), 15 deletions(-) diff --git a/src/sage/plot/animate.py b/src/sage/plot/animate.py index fe0aa497af6..7ea432a0266 100644 --- a/src/sage/plot/animate.py +++ b/src/sage/plot/animate.py @@ -2,11 +2,18 @@ Animated plots Animations are generated from a list (or other iterable) of graphics -objects. Images are produced by calling the ``save_image`` method on -each input object, and using ImageMagick's ``convert`` program [IM] or -``ffmpeg`` [FF] to generate an animation. The output format is GIF by -default, but can be any of the formats supported by ``convert`` or -``ffmpeg``. +objects. +Images are produced by calling the ``save_image`` method on each input +object, creating a sequence of PNG files. +These are then assembled to various target formats using different +tools. +In particular, the ``convert`` program from ImageMagick_ can be used to +generate an animated GIF file. +FFmpeg_ (with the command line program ``ffmpeg``) provides support for +various video formats, but also an alternative method of generating +animated GIFs. +For `browsers which support it`_, APNG_ can be used as another +alternative which works without any extra dependencies. .. Warning:: @@ -15,7 +22,8 @@ convert`` at a command prompt to see if ``convert`` (part of the ImageMagick suite) is installed. If it is, you will be given its location. Similarly, you can check for ``ffmpeg`` with ``which - ffmpeg``. See [IM] or [FF] for installation instructions. + ffmpeg``. See the websites of ImageMagick_ or FFmpeg_ for + installation instructions. EXAMPLES: @@ -27,10 +35,14 @@ Animation with 5 frames sage: a.show() # optional -- ImageMagick -Animate using ffmpeg instead of ImageMagick:: +Animate using FFmpeg_ instead of ImageMagick:: sage: f = tmp_filename(ext='.gif') - sage: a.save(filename=f,use_ffmpeg=True) # optional -- ffmpeg + sage: a.save(filename=f, use_ffmpeg=True) # optional -- ffmpeg + +Animate as an APNG_:: + + sage: a.apng() # long time An animated :class:`sage.plot.graphics.GraphicsArray` of rotating ellipses:: @@ -82,12 +94,14 @@ - William Stein - John Palmieri - Niles Johnson (2013-12): Expand to animate more graphics objects +- Martin von Gagern -REFERENCES: - -.. [IM] http://www.imagemagick.org +.. REFERENCES (not rendered as a section, but linked inline): -.. [FF] http://www.ffmpeg.org +.. _ImageMagick: http://www.imagemagick.org +.. _FFmpeg: http://www.ffmpeg.org +.. _APNG: https://wiki.mozilla.org/APNG_Specification +.. _`browsers which support it`: http://caniuse.com/#feat=apng """ @@ -98,6 +112,8 @@ ############################################################################ import os +import struct +import zlib from sage.structure.sage_object import SageObject from sage.misc.temporary_file import tmp_dir, graphics_filename @@ -503,9 +519,8 @@ def gif(self, delay=20, savefile=None, iterations=0, show_path=False, This method will only work if either (a) the ImageMagick software suite is installed, i.e., you have the ``convert`` - command or (b) ``ffmpeg`` is installed. See - [IM] for more about ImageMagick, and see - [FF] for more about ``ffmpeg``. By default, this + command or (b) ``ffmpeg`` is installed. See the web sites of + ImageMagick_ and FFmpeg_ for more details. By default, this produces the gif using ``convert`` if it is present. If this can't find ``convert`` or if ``use_ffmpeg`` is True, then it uses ``ffmpeg`` instead. @@ -552,6 +567,11 @@ def gif(self, delay=20, savefile=None, iterations=0, show_path=False, packages, so please install one of them and try again. See www.imagemagick.org and www.ffmpeg.org for more information. + + .. REFERENCES (not rendered as a section, but linked inline): + + .. _ImageMagick: http://www.imagemagick.org + .. _FFmpeg: http://www.ffmpeg.org """ from sage.misc.sage_ostools import have_program have_convert = have_program('convert') @@ -816,6 +836,64 @@ def ffmpeg(self, savefile=None, show_path=False, output_format=None, print "Error running ffmpeg." raise + def apng(self, savefile=None, show_path=False, delay=20, iterations=0): + r""" + Creates an animated PNG composed from rendering the graphics + objects in self. Return the absolute path to that file. + + Notice that not all web browsers are capable of displaying APNG + files, though they should still present the first frame of the + animation as a fallback. + + The generated file is not optimized, so it may be quite large. + + Input: + + - ``delay`` - (default: 20) delay in hundredths of a + second between frames + + - ``savefile`` - file that the animated gif gets saved + to + + - ``iterations`` - integer (default: 0); number of + iterations of animation. If 0, loop forever. + + - ``show_path`` - boolean (default: False); if True, + print the path to the saved file + + EXAMPLES:: + + sage: a = animate([sin(x + float(k)) for k in srange(0,2*pi,0.7)], + ....: xmin=0, xmax=2*pi, figsize=[2,1]) + sage: dir = tmp_dir() + sage: a.apng() # long time + sage: a.apng(savefile=dir + 'my_animation.png', delay=35, iterations=3) # long time + sage: a.apng(savefile=dir + 'my_animation.png', show_path=True) # long time + Animation saved to .../my_animation.png. + + If the individual frames have different sizes, an error will be raised:: + + sage: a = animate([plot(sin(x), (x, 0, k)) for k in range(1,4)], + ....: ymin=-1, ymax=1, aspect_ratio=1, figsize=[2,1]) + sage: a.apng() # long time + Traceback (most recent call last): + ... + ValueError: Chunk IHDR mismatch + + """ + pngdir = self.png() + if savefile is None: + savefile = graphics_filename('.png') + with open(savefile, "wb") as out: + apng = APngAssembler( + out, len(self), + delay=delay, num_plays=iterations) + for i in range(len(self)): + png = os.path.join(pngdir, "%08d.png" % i) + apng.add_frame(png) + if show_path: + print "Animation saved to file %s." % savefile + def save(self, filename=None, show_path=False, use_ffmpeg=False): """ Save this animation. @@ -876,3 +954,325 @@ def save(self, filename=None, show_path=False, use_ffmpeg=False): else: self.ffmpeg(savefile=filename, show_path=show_path) return + + +class APngAssembler(object): + """ + Builds an APNG_ (Animated PNG) from a sequence of PNG files. + This is used by the :meth:`sage.plot.animate.Animation.apng` method. + + This code is quite simple; it does little more than copying chunks + from input PNG files to the output file. There is no optimization + involved. This does not depend on external programs or libraries. + + INPUT: + + - ``out`` -- a file opened for binary writing to which the data + will be written + + - ``num_frames`` -- the number of frames in the animation + + - ``num_plays`` -- how often to iterate, 0 means infinitely + + - ``delay`` -- numerator of the delay fraction in seconds + + - ``delay_denominator`` -- denominator of the delay in seconds + + EXAMPLES:: + + sage: from sage.plot.animate import APngAssembler + sage: def assembleAPNG(): + ....: a = animate([sin(x + float(k)) for k in srange(0,2*pi,0.7)], + ....: xmin=0, xmax=2*pi, figsize=[2,1]) + ....: pngdir = a.png() + ....: outfile = sage.misc.temporary_file.tmp_filename(ext='.png') + ....: with open(outfile, "wb") as f: + ....: apng = APngAssembler(f, len(a)) + ....: for i in range(len(a)): + ....: png = os.path.join(pngdir, "{:08d}.png".format(i)) + ....: apng.add_frame(png, delay=10*i + 10) + ....: return outfile + ....: + sage: assembleAPNG() # long time + '...png' + + .. REFERENCES: + + .. _APNG: https://wiki.mozilla.org/APNG_Specification + """ + + magic = b"\x89PNG\x0d\x0a\x1a\x0a" + mustmatch = frozenset([b"IHDR", b"PLTE", b"bKGD", b"cHRM", b"gAMA", + b"pHYs", b"sBIT", b"tRNS"]) + + def __init__(self, out, num_frames, + num_plays=0, delay=200, delay_denominator=100): + """ + Initialize for creation of an APNG file. + """ + self._last_seqno = -1 + self._idx = 0 + self._first = True + self.out = out + self.num_frames = num_frames + self.num_plays = num_plays + self.default_delay_numerator = delay + self.default_delay_denominator = delay_denominator + self._matchref = dict() + self.out.write(self.magic) + + def add_frame(self, pngfile, delay=None, delay_denominator=None): + r""" + Adds a single frame to the APNG file. + + INPUT: + + - ``pngfile`` -- file name of the PNG file with data for this frame + + - ``delay`` -- numerator of the delay fraction in seconds + + - ``delay_denominator`` -- denominator of the delay in seconds + + If the delay is not specified, the default from the constructor + applies. + + TESTS:: + + sage: from sage.plot.animate import APngAssembler + sage: from StringIO import StringIO + sage: def h2b(h): + ....: b = [] + ....: while h: + ....: if h[0] in ' \n': # ignore whitespace + ....: h = h[1:] + ....: elif h[0] in '0123456789abcdef': # hex byte + ....: b.append(int(h[:2], 16)) + ....: h = h[2:] + ....: elif h[0] == '.': # for chunk type + ....: b.extend(ord(h[i]) for i in range(1,5)) + ....: h = h[5:] + ....: else: # for PNG magic + ....: b.append(ord(h[0])) + ....: h = h[1:] + ....: return ''.join(map(chr,b)) + ....: + sage: fn = tmp_filename(ext='.png') + sage: buf = StringIO() + sage: apng = APngAssembler(buf, 2) + sage: with open(fn, 'wb') as f: f.write(h2b('89 PNG 0d0a1a0a' + ....: '0000000d.IHDR 00000003000000020800000000 b81f39c6' + ....: '00000004.gAMA 000186a0 31e8965f' + ....: '00000007.tIME 07de061b0b2624 1f307ad5' + ....: '00000008.IDAT 696d673164617461 ce8a4999' + ....: '00000000.IEND ae426082')) + sage: apng.add_frame(fn, delay=0x567, delay_denominator=0x1234) + sage: with open(fn, 'wb') as f: f.write(h2b('89 PNG 0d0a1a0a' + ....: '0000000d.IHDR 00000003000000020800000000 b81f39c6' + ....: '00000004.gAMA 000186a0 31e8965f' + ....: '00000004.IDAT 696d6732 0e69ab1d' + ....: '00000004.IDAT 64617461 6694cb78' + ....: '00000000.IEND ae426082')) + sage: apng.add_frame(fn) + sage: len(buf.getvalue()) + 217 + sage: expected = h2b('89 PNG 0d0a1a0a' + ....: '0000000d.IHDR 00000003000000020800000000 b81f39c6' + ....: '00000004.gAMA 000186a0 31e8965f' + ....: '00000008.acTL 0000000200000000 f38d9370' + ....: '0000001a.fcTL 000000000000000300000002' + ....: ' 0000000000000000056712340100 b4f729c9' + ....: '00000008.IDAT 696d673164617461 ce8a4999' + ....: '0000001a.fcTL 000000010000000300000002' + ....: ' 000000000000000000c800640100 1b92eb4d' + ....: '00000008.fdAT 00000002696d6732 9cfb89a3' + ....: '00000008.fdAT 0000000364617461 c966c076' + ....: '00000000.IEND ae426082') + sage: buf.getvalue() == expected + True + sage: apng.add_frame(fn) + Traceback (most recent call last): + ... + RuntimeError: Already reached the declared number of frames + + """ + if self._idx == self.num_frames: + raise RuntimeError("Already reached the declared number of frames") + self.delay_numerator = self.default_delay_numerator + self.delay_denominator = self.default_delay_denominator + self._actl_written = False + self._fctl_written = False + if delay is not None: + self.delay_numerator = delay + if delay_denominator is not None: + self.delay_denominator = delay_denominator + self._add_png(pngfile) + self._idx += 1 + if self._idx == self.num_frames: + self._chunk(b"IEND", b"") + + def set_default(self, pngfile): + r""" + Adds a default image for the APNG file. + + This image is used as a fallback in case some application does + not understand the APNG format. This method must be called + prior to any calls to the ``add_frame`` method, if it is called + at all. If it is not called, then the first frame of the + animation will be the default. + + INPUT: + + - ``pngfile`` -- file name of the PNG file with data + for the default image + + TESTS:: + + sage: from sage.plot.animate import APngAssembler + sage: from StringIO import StringIO + sage: def h2b(h): + ....: b = [] + ....: while h: + ....: if h[0] in ' \n': # ignore whitespace + ....: h = h[1:] + ....: elif h[0] in '0123456789abcdef': # hex byte + ....: b.append(int(h[:2], 16)) + ....: h = h[2:] + ....: elif h[0] == '.': # for chunk type + ....: b.extend(ord(h[i]) for i in range(1,5)) + ....: h = h[5:] + ....: else: # for PNG magic + ....: b.append(ord(h[0])) + ....: h = h[1:] + ....: return ''.join(map(chr,b)) + ....: + sage: fn = tmp_filename(ext='.png') + sage: buf = StringIO() + sage: apng = APngAssembler(buf, 1) + sage: with open(fn, 'wb') as f: f.write(h2b('89 PNG 0d0a1a0a' + ....: '0000000d.IHDR 00000003000000020800000000 b81f39c6' + ....: '00000004.gAMA 000186a0 31e8965f' + ....: '00000007.tIME 07de061b0b2624 1f307ad5' + ....: '00000008.IDAT 696d673164617461 ce8a4999' + ....: '00000000.IEND ae426082')) + sage: apng.set_default(fn) + sage: with open(fn, 'wb') as f: f.write(h2b('89 PNG 0d0a1a0a' + ....: '0000000d.IHDR 00000003000000020800000000 b81f39c6' + ....: '00000004.gAMA 000186a0 31e8965f' + ....: '00000004.IDAT 696d6732 0e69ab1d' + ....: '00000004.IDAT 64617461 6694cb78' + ....: '00000000.IEND ae426082')) + sage: apng.add_frame(fn, delay=0x567, delay_denominator=0x1234) + sage: len(buf.getvalue()) + 179 + sage: expected = h2b('89 PNG 0d0a1a0a' + ....: '0000000d.IHDR 00000003000000020800000000 b81f39c6' + ....: '00000004.gAMA 000186a0 31e8965f' + ....: '00000008.acTL 0000000100000000 b42de9a0' + ....: '00000008.IDAT 696d673164617461 ce8a4999' + ....: '0000001a.fcTL 000000000000000300000002' + ....: ' 0000000000000000056712340100 b4f729c9' + ....: '00000008.fdAT 00000001696d6732 db5bf373' + ....: '00000008.fdAT 0000000264617461 f406e9c6' + ....: '00000000.IEND ae426082') + sage: buf.getvalue() == expected + True + sage: apng.add_frame(fn) + Traceback (most recent call last): + ... + RuntimeError: Already reached the declared number of frames + + """ + if self._idx != 0: + raise RuntimeError("Default image must precede all animation frames") + self._actl_written = False + self._fctl_written = True + self._add_png(pngfile) + + def _add_png(self, pngfile): + """Add data from one PNG still image.""" + with open(pngfile, 'rb') as png: + if png.read(8) != self.magic: + raise ValueError("{} is not a PNG file".format(pngfile)) + while True: + chead = png.read(8) + if len(chead) == 0: + break + clen, ctype = struct.unpack(">L4s", chead) + cdata = png.read(clen) + ccrc = png.read(4) + utype = ctype.decode("ascii") + self._current_chunk = (chead[:4], ctype, cdata, ccrc) + if ctype in self.mustmatch: + ref = self._matchref.get(ctype) + if ref is None: + self._matchref[ctype] = cdata + self._copy() + else: + if cdata != ref: + raise ValueError("Chunk {} mismatch".format(utype)) + met = ("_first_" if self._first else "_next_") + utype + try: + met = getattr(self, met) + except AttributeError: + pass + else: + met(cdata) + self._first = False + + def _seqno(self): + """Generate next sequence number.""" + self._last_seqno += 1 + return struct.pack(">L", self._last_seqno) + + def _first_IHDR(self, data): + """Remember image size.""" + w, h, d, ctype, comp, filt, ilace = struct.unpack(">2L5B", data) + self.width = w + self.height = h + + def _first_IDAT(self, data): + """Write acTL and fcTL, then copy as IDAT.""" + self._actl() + self._fctl() + self._copy() + + def _next_IDAT(self, data): + """write fcTL, then convert to fdAT.""" + self._fctl() + maxlen = 0x7ffffffb + while len(data) > maxlen: + self._chunk(b"fdAT", self._seqno() + data[:maxlen]) + data = data[maxlen:] + self._chunk(b"fdAT", self._seqno() + data) + + def _copy(self): + """Copy an existing chunk without modification.""" + for d in self._current_chunk: + self.out.write(d) + + def _actl(self): + """Write animation control data (acTL).""" + if self._actl_written: + return + data = struct.pack(">2L", self.num_frames, self.num_plays) + self._chunk(b"acTL", data) + self._actl_written = True + + def _fctl(self): + """Write frame control data (fcTL).""" + if self._fctl_written: + return + data = struct.pack( + ">4L2H2B", + self.width, self.height, 0, 0, + self.delay_numerator, self.delay_denominator, + 1, 0) + self._chunk(b"fcTL", self._seqno() + data) + self._fctl_written = True + + def _chunk(self, ctype, cdata): + """Write a new (or modified) chunk of data""" + ccrc = struct.pack(">L", zlib.crc32(ctype + cdata) & 0xffffffff) + clen = struct.pack(">L", len(cdata)) + for d in [clen, ctype, cdata, ccrc]: + self.out.write(d) From ffeab37f74a0621ab60ff7a82dc3a029c7caef79 Mon Sep 17 00:00:00 2001 From: vince Date: Sun, 7 Dec 2014 18:36:14 +0000 Subject: [PATCH 064/217] Have parser documented and tested for the gambit LCP output --- src/sage/game_theory/parser.py | 123 ++++++++++++++++++++++++++++++++- 1 file changed, 120 insertions(+), 3 deletions(-) diff --git a/src/sage/game_theory/parser.py b/src/sage/game_theory/parser.py index ac4738bac6b..610cb242050 100644 --- a/src/sage/game_theory/parser.py +++ b/src/sage/game_theory/parser.py @@ -3,8 +3,8 @@ class Parser(): A class for parsing the outputs of different algorithms called in other software packages. - At present the only parser included is for the ``'lrs'`` algorithm however - this is actively being expanded to 'gambit'. + Two parsers are included, one for the ``'lrs'`` algorithm and another for + the ``'LCP'`` algorithm. """ def __init__(self, raw_string): @@ -15,7 +15,8 @@ def __init__(self, raw_string): TESTS: Simply checking that we have the correct string output - for the H representation:: + for the H representation (which is the format required + for the ``'lrs'`` algorithm):: sage: from sage.game_theory.parser import Parser sage: A = matrix([[1, 2], [3, 2]]) @@ -118,6 +119,10 @@ def __init__(self, raw_string): -1 1 1 1 0 end + + This class is also used to parse the output of the ``'LCP'`` + algorithm from gambit. This is done using the `format_LCP` + algorithm. """ self.raw_string = raw_string @@ -198,4 +203,116 @@ def format_lrs(self): return equilibria + def format_LCP(self, gambit_game): + """ + Parses the output of gambit so as to return vectors + corresponding to equilibria obtained using the LCP algorithm. + + TESTS: + + Here we construct a two by two game in gambit:: + + sage: import gambit # optional - gambit + sage: from sage.game_theory.parser import Parser + sage: g = gambit.Game.new_table([2,2]) # optional - gambit + sage: g[int(0), int(0)][int(0)] = int(2) # optional - gambit + sage: g[int(0), int(0)][int(1)] = int(1) # optional - gambit + sage: g[int(0), int(1)][int(0)] = int(0) # optional - gambit + sage: g[int(0), int(1)][int(1)] = int(0) # optional - gambit + sage: g[int(1), int(0)][int(0)] = int(0) # optional - gambit + sage: g[int(1), int(0)][int(1)] = int(0) # optional - gambit + sage: g[int(1), int(1)][int(0)] = int(1) # optional - gambit + sage: g[int(1), int(1)][int(1)] = int(2) # optional - gambit + sage: solver = gambit.nash.ExternalLCPSolver() # optional - gambit + + Here is the output of the LCP algorithm:: + + sage: LCP_output = solver.solve(g) # optional - gambit + sage: LCP_output # optional - gambit + [, + , + ] + + The Parser class outputs the equilibrium:: + + sage: nasheq = Parser(LCP_output).format_LCP(g) # optional - gambit + sage: nasheq # optional - gambit + [[(1.0, 0.0), (1.0, 0.0)], [(0.6666666667, 0.3333333333), (0.3333333333, 0.6666666667)], [(0.0, 1.0), (0.0, 1.0)]] + + Here is another game:: + + sage: g = gambit.Game.new_table([2,2]) # optional - gambit + sage: g[int(0), int(0)][int(0)] = int(4) # optional - gambit + sage: g[int(0), int(0)][int(1)] = int(8) # optional - gambit + sage: g[int(0), int(1)][int(0)] = int(0) # optional - gambit + sage: g[int(0), int(1)][int(1)] = int(1) # optional - gambit + sage: g[int(1), int(0)][int(0)] = int(1) # optional - gambit + sage: g[int(1), int(0)][int(1)] = int(3) # optional - gambit + sage: g[int(1), int(1)][int(0)] = int(1) # optional - gambit + sage: g[int(1), int(1)][int(1)] = int(0) # optional - gambit + sage: solver = gambit.nash.ExternalLCPSolver() # optional - gambit + + Here is the LCP output:: + + sage: LCP_output = solver.solve(g) # optional - gambit + sage: LCP_output # optional - gambit + [] + + The corresponding parsed equilibrium:: + + sage: nasheq = Parser(LCP_output).format_LCP(g) # optional - gambit + sage: nasheq # optional - gambit + [[(1.0, 0.0), (1.0, 0.0)]] + + Here is a larger degenerate game:: + + sage: g = gambit.Game.new_table([3,3]) # optional - gambit + sage: g[int(0), int(0)][int(0)] = int(-7) # optional - gambit + sage: g[int(0), int(0)][int(1)] = int(-9) # optional - gambit + sage: g[int(0), int(1)][int(0)] = int(-5) # optional - gambit + sage: g[int(0), int(1)][int(1)] = int(7) # optional - gambit + sage: g[int(0), int(2)][int(0)] = int(5) # optional - gambit + sage: g[int(0), int(2)][int(1)] = int(9) # optional - gambit + sage: g[int(1), int(0)][int(0)] = int(5) # optional - gambit + sage: g[int(1), int(0)][int(1)] = int(6) # optional - gambit + sage: g[int(1), int(1)][int(0)] = int(5) # optional - gambit + sage: g[int(1), int(1)][int(1)] = int(-2) # optional - gambit + sage: g[int(1), int(2)][int(0)] = int(3) # optional - gambit + sage: g[int(1), int(2)][int(1)] = int(-3) # optional - gambit + sage: g[int(2), int(0)][int(0)] = int(1) # optional - gambit + sage: g[int(2), int(0)][int(1)] = int(-4) # optional - gambit + sage: g[int(2), int(1)][int(0)] = int(-6) # optional - gambit + sage: g[int(2), int(1)][int(1)] = int(6) # optional - gambit + sage: g[int(2), int(2)][int(0)] = int(1) # optional - gambit + sage: g[int(2), int(2)][int(1)] = int(-10) # optional - gambit + sage: solver = gambit.nash.ExternalLCPSolver() # optional - gambit + + Here is the LCP output:: + + sage: LCP_output = solver.solve(g) # optional - gambit + sage: LCP_output # optional - gambit + [, + , + ] + + The corresponding parsed equilibrium:: + + sage: nasheq = Parser(LCP_output).format_LCP(g) # optional - gambit + sage: nasheq # optional - gambit + [[(1.0, 0.0, 0.0), (0.0, 0.0, 1.0)], + [(0.3333333333, 0.6666666667, 0.0), (0.1428571429, 0.0, 0.8571428571)], + [(0.0, 1.0, 0.0), (1.0, 0.0, 0.0)]] + + Note, that this differs from the same output of the lrs algorithm due + the fact that the game is degenerate. + """ + nice_stuff = [] + for gambitstrategy in self.raw_string: + gambitstrategy = eval(str(gambitstrategy)[str(gambitstrategy).index("["): str(gambitstrategy).index("]") + 1]) + profile = [tuple(gambitstrategy[:len(gambit_game.players[int(0)].strategies)])] + for player in list(gambit_game.players)[1:]: + previousplayerstrategylength = len(profile[-1]) + profile.append(tuple(gambitstrategy[previousplayerstrategylength: previousplayerstrategylength + len(player.strategies)])) + nice_stuff.append(profile) + return nice_stuff From f90b13e8c586fcc20ebcf7ad30feb9cbad618e70 Mon Sep 17 00:00:00 2001 From: vince Date: Mon, 8 Dec 2014 16:40:34 +0000 Subject: [PATCH 065/217] Have added lots of tests that fail - more can be added --- src/sage/game_theory/normal_form_game.py | 91 +++++++++++++++++++++--- 1 file changed, 82 insertions(+), 9 deletions(-) diff --git a/src/sage/game_theory/normal_form_game.py b/src/sage/game_theory/normal_form_game.py index 019c8861121..8955337a4d4 100644 --- a/src/sage/game_theory/normal_form_game.py +++ b/src/sage/game_theory/normal_form_game.py @@ -2,12 +2,12 @@ Normal Form games with N players. This module implements a class for normal form games (strategic form games) -[NN2007]_. At present 2 algorithms are implemented to compute equilibria -of these games (``'lrs'`` - interfaced with the 'lrs' library and support enumeration -built in Sage). The architecture for the class is based on the gambit -architecture to ensure an easy transition between gambit and Sage. -At present the algorithms for the computation of equilibria only solve 2 player -games. +[NN2007]_. At present 3 algorithms are implemented to compute equilibria +of these games (``'lrs'`` - interfaced with the 'lrs' library, ``'LCP'`` interfaced +with the 'gambit' library and support enumeration built in Sage). The architecture +for the class is based on the gambit architecture to ensure an easy transition +between gambit and Sage. At present the algorithms for the computation of equilibria +only solve 2 player games. A very simple and well known example of normal form game is referred to as the 'Battle of the Sexes' in which two players Amy and Bob @@ -201,12 +201,20 @@ sage: prisoners_dilemma.obtain_nash(algorithm='enumeration', maximization=False) [[(0, 1), (0, 1)]] -When obtaining Nash equilibrium there are 2 algorithms currently available: +When obtaining Nash equilibrium there are 3 algorithms currently available: * ``'lrs'``: Reverse search vertex enumeration for 2 player games. This algorithm uses the optional 'lrs' package. To install it type ``sage -i lrs`` at the command line. For more information see [A2000]_. +* ``LCP``: Linear complementarity program algorithm for 2 player games. + This algorithm uses the open source game theory package: + `Gambit `_ [MMAT2014]_. At present this is + the only gambit algorithm available in sage but further development will + hope to implement more algorithms + (in particular for games with more than 2 players). To install it + type ``sage -i gambit`` at the command line. + * ``'enumeration'``: Support enumeration for 2 player games. This algorithm is hard coded in Sage and checks through all potential supports of a strategy. Supports of a given size with a conditionally @@ -215,10 +223,12 @@ algorithm described in [NN2007]_ and a pruning component described in [SLB2008]_. -Below we show how the two algorithms are called:: +Below we show how the three algorithms are called:: sage: matching_pennies.obtain_nash(algorithm='lrs') # optional - lrs [[(1/2, 1/2), (1/2, 1/2)]] + sage: matching_pennies.obtain_nash(algorithm='LCP') # optional - gambit + [[(0.5, 0.5), (0.5, 0.5)]] sage: matching_pennies.obtain_nash(algorithm='enumeration') [[(1/2, 1/2), (1/2, 1/2)]] @@ -227,6 +237,7 @@ installed): 1. ``'lrs'`` (requires 'lrs') + 1. ``'LCP'`` (requires 'gambit') 2. ``'enumeration'`` Here is a game being constructed using gambit syntax (note that a @@ -348,6 +359,52 @@ ... NotImplementedError: Nash equilibrium for games with more than 2 players have not been implemented yet. Please see the gambit website (http://gambit.sourceforge.net/) that has a variety of available algorithms +There are however a variety of such algorithms available in gambit, +further compatibility between Sage and gambit is actively being developed: +https://github.com/tturocy/gambit/tree/sage_integration. + +Note that the Gambit implementation of `LCP` can only handle integer +payoffs. If a non integer payoff is used an error will be raised:: + + sage: A = matrix([[2, 1], [1, 2.5]]) + sage: B = matrix([[-1, 3], [2, 1]]) + sage: g = NormalFormGame([A, B]) + sage: g.obtain_nash(algorithm='LCP') # optional - gambit + Traceback (most recent call last): + ... + ValueError: The Gambit implementation of LCP only allows for integer valued payoffs. Please scale your payoff matrices. + +Other algorithms can handle these payoffs:: + + sage: g.obtain_nash(algorithm='enumeration') + [[(1/5, 4/5), (3/5, 2/5)]] + sage: g.obtain_nash(algorithm='lrs') + [[(1/5, 4/5), (3/5, 2/5)]] + +It can be shown that linear scaling of the payoff matrices conserves the +equilibrium values:: + + sage: A = 2 * A + sage: g = NormalFormGame([A, B]) + sage: g.obtain_nash(algorithm='LCP') # optional - gambit + [[(0.2, 0.8), (0.6, 0.4)]] + +It is also possible to generate a Normal Form Game from a gambit Game:: + + sage: from gambit import Game + sage: gambitgame= Game.new_table([2, 2]) + sage: gambitgame[int(0), int(0)][int(0)] = int(8) + sage: gambitgame[int(0), int(0)][int(1)] = int(8) + sage: gambitgame[int(0), int(1)][int(0)] = int(2) + sage: gambitgame[int(0), int(1)][int(1)] = int(10) + sage: gambitgame[int(1), int(0)][int(0)] = int(10) + sage: gambitgame[int(1), int(0)][int(1)] = int(2) + sage: gambitgame[int(1), int(1)][int(0)] = int(5) + sage: gambitgame[int(1), int(1)][int(1)] = int(5) + sage: g = NormalFormGame(gambitgame) + sage: g + {(0, 1): [2.0, 10.0], (1, 0): [10.0, 2.0], (0, 0): [8.0, 8.0], (1, 1): [5.0, 5.0]} + Here is a slightly longer game that would take too long to solve with ``'enumeration'``. Consider the following: @@ -378,6 +435,8 @@ sage: g = NormalFormGame([A, B]) sage: g.obtain_nash(algorithm='lrs') # optional - lrs [[(1, 0, 0, 0, 0, 0, 0, 0, 0), (1, 0, 0, 0, 0, 0, 0, 0, 0)]] + sage: g.obtain_nash(algorithm='LCP') # optional - gambit + [[(1, 0, 0, 0, 0, 0, 0, 0, 0), (1, 0, 0, 0, 0, 0, 0, 0, 0)]] The output is a pair of vectors (as before) showing the Nash equilibrium. In particular it here shows that out of the 10 possible strategies both @@ -395,6 +454,8 @@ sage: degenerate_game = NormalFormGame([A,B]) sage: degenerate_game.obtain_nash(algorithm='lrs') # optional - lrs [[(0, 1/3, 2/3), (1/3, 2/3)], [(1, 0, 0), (2/3, 1/3)], [(1, 0, 0), (1, 0)]] + sage: degenerate_game.obtain_nash(algorithm='LCP') # optional - gambit + NOTE SURE WHAT OUTPUT WILL BE BUT THIS SHOULD FAIL sage: degenerate_game.obtain_nash(algorithm='enumeration') [[(0, 1/3, 2/3), (1/3, 2/3)], [(1, 0, 0), (1, 0)]] @@ -934,6 +995,9 @@ def obtain_nash(self, algorithm=False, maximization=True): * ``'lrs'`` - This algorithm is only suited for 2 player games. See the lrs web site (http://cgm.cs.mcgill.ca/~avis/C/lrs.html). + * ``"LCP"`` - This algorithm is only suited for 2 player games. + See the gambit web site (http://gambit.sourceforge.net/). + * ``'enumeration'`` - This is a very inefficient algorithm (in essence a brute force approach). @@ -1060,7 +1124,7 @@ def obtain_nash(self, algorithm=False, maximization=True): Note that outputs for all algorithms are as lists of lists of tuples and the equilibria have been sorted so that all algorithms give - a comparable output:: + a comparable output (although ``'LCP'`` returns floats):: sage: enumeration_eqs = g.obtain_nash(algorithm='enumeration') sage: [[type(s) for s in eq] for eq in enumeration_eqs] @@ -1068,12 +1132,21 @@ def obtain_nash(self, algorithm=False, maximization=True): sage: lrs_eqs = g.obtain_nash(algorithm='lrs') # optional - lrs sage: [[type(s) for s in eq] for eq in lrs_eqs] # optional - lrs [[, ], [, ], [, ]] + sage: LCP_eqs = g.obtain_nash(algorithm='LCP') # optional - gambit + sage: [[type(s) for s in eq] for eq in LCP_eqs] # optional - gambit + [[, ], [, ], [, ]] sage: enumeration_eqs == sorted(enumeration_eqs) True sage: lrs_eqs == sorted(lrs_eqs) # optional - lrs True + sage: LCP_eqs == sorted(LCP_eqs) # optional - LCP + True sage: lrs_eqs == enumeration_eqs # optional - lrs True + sage: lrs_eqs == LCP_eqs # optional - lrs, gambit + False + sage: [(float(k) for k in eq) for eq in lrs_eqs] == LCP_eqs # optional - lrs, gambit + True """ if len(self.players) > 2: From c28b5477ce99d40f7eb45040fff43f55085f45b6 Mon Sep 17 00:00:00 2001 From: vince Date: Fri, 12 Dec 2014 08:42:44 +0000 Subject: [PATCH 066/217] Changing name of gambit.py to gambit_doc.py and including code that fails tests --- .../game_theory/{gambit.py => gambit_docs.py} | 0 src/sage/game_theory/normal_form_game.py | 38 +++++++++++++++++++ 2 files changed, 38 insertions(+) rename src/sage/game_theory/{gambit.py => gambit_docs.py} (100%) diff --git a/src/sage/game_theory/gambit.py b/src/sage/game_theory/gambit_docs.py similarity index 100% rename from src/sage/game_theory/gambit.py rename to src/sage/game_theory/gambit_docs.py diff --git a/src/sage/game_theory/normal_form_game.py b/src/sage/game_theory/normal_form_game.py index 8955337a4d4..69c5fd802be 100644 --- a/src/sage/game_theory/normal_form_game.py +++ b/src/sage/game_theory/normal_form_game.py @@ -1171,6 +1171,17 @@ def obtain_nash(self, algorithm=False, maximization=True): return self._solve_lrs(maximization) + if algorithm == "LCP": + if not is_package_installed('gambit'): + raise NotImplementedError("gambit is not installed") + for strategy_profile in self.utilities: + payoffs = self.utilities[strategy_profile] + if payoffs != [int(payoffs[0]), int(payoffs[1])]: + raise ValueError("""The Gambit implementation of LCP only + allows for integer valued payoffs. + Please scale your payoff matrices.""") + return self._solve_LCP(maximization) + if algorithm == "enumeration": return self._solve_enumeration(maximization) @@ -1236,6 +1247,33 @@ def _solve_lrs(self, maximization=True): nasheq = Parser(lrs_output).format_lrs() return sorted(nasheq) + def _solve_LCP(self, maximization): + r""" + Solves a NormalFormGame using Gambit's LCP algorithm. + EXAMPLES: + Simple example. :: + sage: a = matrix([[1, 0], [1, 4]]) + sage: b = matrix([[2, 3], [2, 4]]) + sage: c = NormalFormGame([a, b]) + sage: c._solve_LCP(maximization=True) # optional - gambit + [[(0.0, 1.0), (0.0, 1.0)]] + """ + from gambit.nash import ExternalLCPSolver + from gambit import Game + strategy_sizes = [p.num_strategies for p in self.players] + g = Game.new_table(strategy_sizes) + scalar = 1 + if maximization is False: + scalar *= -1 + for strategy_profile in self.utilities: + g[strategy_profile][0] = int(scalar * + self.utilities[strategy_profile][0]) + g[strategy_profile][1] = int(scalar * + self.utilities[strategy_profile][1]) + output = ExternalLCPSolver().solve(g) + nasheq = Parser(output).format_LCP(g) + return nasheq + def _solve_enumeration(self, maximization=True): r""" Obtains the Nash equilibria using support enumeration. From 22fe917369205c3a48ff25ecfa785909424e2c3a Mon Sep 17 00:00:00 2001 From: James Campbell Date: Thu, 11 Dec 2014 16:13:07 +0000 Subject: [PATCH 067/217] makes gambit and lrs tests optional, now pass when not installed --- src/sage/game_theory/normal_form_game.py | 26 ++++++++++++------------ 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/sage/game_theory/normal_form_game.py b/src/sage/game_theory/normal_form_game.py index 69c5fd802be..94af9ef6a06 100644 --- a/src/sage/game_theory/normal_form_game.py +++ b/src/sage/game_theory/normal_form_game.py @@ -378,7 +378,7 @@ sage: g.obtain_nash(algorithm='enumeration') [[(1/5, 4/5), (3/5, 2/5)]] - sage: g.obtain_nash(algorithm='lrs') + sage: g.obtain_nash(algorithm='lrs') # optional - lrs [[(1/5, 4/5), (3/5, 2/5)]] It can be shown that linear scaling of the payoff matrices conserves the @@ -391,18 +391,18 @@ It is also possible to generate a Normal Form Game from a gambit Game:: - sage: from gambit import Game - sage: gambitgame= Game.new_table([2, 2]) - sage: gambitgame[int(0), int(0)][int(0)] = int(8) - sage: gambitgame[int(0), int(0)][int(1)] = int(8) - sage: gambitgame[int(0), int(1)][int(0)] = int(2) - sage: gambitgame[int(0), int(1)][int(1)] = int(10) - sage: gambitgame[int(1), int(0)][int(0)] = int(10) - sage: gambitgame[int(1), int(0)][int(1)] = int(2) - sage: gambitgame[int(1), int(1)][int(0)] = int(5) - sage: gambitgame[int(1), int(1)][int(1)] = int(5) - sage: g = NormalFormGame(gambitgame) - sage: g + sage: from gambit import Game # optional - gambit + sage: gambitgame= Game.new_table([2, 2]) # optional - gambit + sage: gambitgame[int(0), int(0)][int(0)] = int(8) # optional - gambit + sage: gambitgame[int(0), int(0)][int(1)] = int(8) # optional - gambit + sage: gambitgame[int(0), int(1)][int(0)] = int(2) # optional - gambit + sage: gambitgame[int(0), int(1)][int(1)] = int(10) # optional - gambit + sage: gambitgame[int(1), int(0)][int(0)] = int(10) # optional - gambit + sage: gambitgame[int(1), int(0)][int(1)] = int(2) # optional - gambit + sage: gambitgame[int(1), int(1)][int(0)] = int(5) # optional - gambit + sage: gambitgame[int(1), int(1)][int(1)] = int(5) # optional - gambit + sage: g = NormalFormGame(gambitgame) # optional - gambit + sage: g # optional - gambit {(0, 1): [2.0, 10.0], (1, 0): [10.0, 2.0], (0, 0): [8.0, 8.0], (1, 1): [5.0, 5.0]} Here is a slightly longer game that would take too long to solve with From d8a2f1c43205c649010665da08e25fae465099e8 Mon Sep 17 00:00:00 2001 From: James Campbell Date: Thu, 11 Dec 2014 16:41:38 +0000 Subject: [PATCH 068/217] Integrating gambit in to NormalFormGame - all tests pass --- src/sage/game_theory/normal_form_game.py | 97 ++++++++++++++++++++---- 1 file changed, 81 insertions(+), 16 deletions(-) diff --git a/src/sage/game_theory/normal_form_game.py b/src/sage/game_theory/normal_form_game.py index 94af9ef6a06..0e7afc501a8 100644 --- a/src/sage/game_theory/normal_form_game.py +++ b/src/sage/game_theory/normal_form_game.py @@ -156,7 +156,6 @@ If we compute the Nash equilibria we see that this corresponds to a point at which both players are indifferent:: - sage: y = var('y') sage: A = matrix([[1, -1], [-1, 1]]) sage: B = matrix([[-1, 1], [1, -1]]) sage: matching_pennies = NormalFormGame([A, B]) @@ -237,7 +236,6 @@ installed): 1. ``'lrs'`` (requires 'lrs') - 1. ``'LCP'`` (requires 'gambit') 2. ``'enumeration'`` Here is a game being constructed using gambit syntax (note that a @@ -403,7 +401,7 @@ sage: gambitgame[int(1), int(1)][int(1)] = int(5) # optional - gambit sage: g = NormalFormGame(gambitgame) # optional - gambit sage: g # optional - gambit - {(0, 1): [2.0, 10.0], (1, 0): [10.0, 2.0], (0, 0): [8.0, 8.0], (1, 1): [5.0, 5.0]} + Normal Form Game with the following utilities: {(0, 1): [2.0, 10.0], (1, 0): [10.0, 2.0], (0, 0): [8.0, 8.0], (1, 1): [5.0, 5.0]} Here is a slightly longer game that would take too long to solve with ``'enumeration'``. Consider the following: @@ -436,7 +434,7 @@ sage: g.obtain_nash(algorithm='lrs') # optional - lrs [[(1, 0, 0, 0, 0, 0, 0, 0, 0), (1, 0, 0, 0, 0, 0, 0, 0, 0)]] sage: g.obtain_nash(algorithm='LCP') # optional - gambit - [[(1, 0, 0, 0, 0, 0, 0, 0, 0), (1, 0, 0, 0, 0, 0, 0, 0, 0)]] + [[(1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0), (1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)]] The output is a pair of vectors (as before) showing the Nash equilibrium. In particular it here shows that out of the 10 possible strategies both @@ -455,10 +453,15 @@ sage: degenerate_game.obtain_nash(algorithm='lrs') # optional - lrs [[(0, 1/3, 2/3), (1/3, 2/3)], [(1, 0, 0), (2/3, 1/3)], [(1, 0, 0), (1, 0)]] sage: degenerate_game.obtain_nash(algorithm='LCP') # optional - gambit - NOTE SURE WHAT OUTPUT WILL BE BUT THIS SHOULD FAIL + [[(0.0, 0.3333333333, 0.6666666667), (0.3333333333, 0.6666666667)], + [(1.0, -0.0, 0.0), (0.6666666667, 0.3333333333)], + [(1.0, 0.0, 0.0), (1.0, 0.0)]] sage: degenerate_game.obtain_nash(algorithm='enumeration') [[(0, 1/3, 2/3), (1/3, 2/3)], [(1, 0, 0), (1, 0)]] +Note the 'negative' `-0.0` output by gambit. This is due to the numerical +nature of the algorithm used. + Here is an example with the trivial game where all payoffs are 0:: sage: g = NormalFormGame() @@ -530,7 +533,10 @@ lazy_import('sage.misc.temporary_file', 'tmp_filename') lazy_import('sage.rings.arith', 'lcm') lazy_import('sage.rings.rational', 'Rational') - +try: + from gambit import Game +except: + pass class NormalFormGame(SageObject, MutableMapping): r""" @@ -585,6 +591,22 @@ def __init__(self, generator=None): ... NotImplementedError: Nash equilibrium for games with more than 2 players have not been implemented yet. Please see the gambit website (http://gambit.sourceforge.net/) that has a variety of available algorithms + Can initialise a game from a gambit game object: + + sage: from gambit import Game # optional - gambit + sage: gambitgame= Game.new_table([2, 2]) # optional - gambit + sage: gambitgame[int(0), int(0)][int(0)] = int(5) # optional - gambit + sage: gambitgame[int(0), int(0)][int(1)] = int(8) # optional - gambit + sage: gambitgame[int(0), int(1)][int(0)] = int(2) # optional - gambit + sage: gambitgame[int(0), int(1)][int(1)] = int(11) # optional - gambit + sage: gambitgame[int(1), int(0)][int(0)] = int(10) # optional - gambit + sage: gambitgame[int(1), int(0)][int(1)] = int(7) # optional - gambit + sage: gambitgame[int(1), int(1)][int(0)] = int(5) # optional - gambit + sage: gambitgame[int(1), int(1)][int(1)] = int(5) # optional - gambit + sage: g = NormalFormGame(gambitgame) # optional - gambit + sage: g # optional - gambit + Normal Form Game with the following utilities: {(0, 1): [2.0, 11.0], (1, 0): [10.0, 7.0], (0, 0): [5.0, 8.0], (1, 1): [5.0, 5.0]} + TESTS: Raise error if matrices aren't the same size:: @@ -610,7 +632,7 @@ def __init__(self, generator=None): sage: error = NormalFormGame({4:6, 6:9}) Traceback (most recent call last): ... - TypeError: Generator function must be a list or nothing + TypeError: Generator function must be a list, gambit game or nothing When passing nothing, the utilities then need to be entered manually:: @@ -622,8 +644,12 @@ def __init__(self, generator=None): self.players = [] self.utilities = {} matrices = [] - if type(generator) is not list and generator is not None: - raise TypeError("Generator function must be a list or nothing") + if type(generator) is not list and generator != None: + if is_package_installed('gambit'): + if type(generator) is not Game: + raise TypeError("Generator function must be a list, gambit game or nothing") + else: + raise TypeError("Generator function must be a list or nothing") if type(generator) is list: if len(generator) == 1: @@ -632,6 +658,10 @@ def __init__(self, generator=None): if matrices[0].dimensions() != matrices[1].dimensions(): raise ValueError("matrices must be the same size") self._two_matrix_game(matrices) + elif is_package_installed('gambit'): + if type(generator) is Game: + game = generator + self._gambit_game(game) def __delitem__(self, key): r""" @@ -813,6 +843,36 @@ def _two_matrix_game(self, matrices): self.utilities[strategy_profile] = [matrices[0][strategy_profile], matrices[1][strategy_profile]] + def _gambit_game(self, game): + r""" + Creates a ``NormalFormGame`` object from a Gambit game. + + TESTS:: + + sage: from gambit import Game # optional - gambit + sage: testgame = Game.new_table([2, 2]) # optional - gambit + sage: testgame[int(0), int(0)][int(0)] = int(8) # optional - gambit + sage: testgame[int(0), int(0)][int(1)] = int(8) # optional - gambit + sage: testgame[int(0), int(1)][int(0)] = int(2) # optional - gambit + sage: testgame[int(0), int(1)][int(1)] = int(10) # optional - gambit + sage: testgame[int(1), int(0)][int(0)] = int(10) # optional - gambit + sage: testgame[int(1), int(0)][int(1)] = int(2) # optional - gambit + sage: testgame[int(1), int(1)][int(0)] = int(5) # optional - gambit + sage: testgame[int(1), int(1)][int(1)] = int(5) # optional - gambit + sage: g = NormalFormGame() # optional - gambit + sage: g._gambit_game(testgame) # optional - gambit + sage: g # optional - gambit + Normal Form Game with the following utilities: {(0, 1): [2.0, 10.0], (1, 0): [10.0, 2.0], (0, 0): [8.0, 8.0], (1, 1): [5.0, 5.0]} + """ + self.players = [] + self.utilities = {} + for player in game.players: + num_strategies = len(player.strategies) + self.add_player(num_strategies) + for strategy_profile in self.utilities: + utility_vector = [float(game[strategy_profile][i]) for i in range(len(self.players))] + self.utilities[strategy_profile] = utility_vector + def payoff_matrices(self): r""" Returns 2 matrices representing the payoffs for each player. @@ -996,7 +1056,8 @@ def obtain_nash(self, algorithm=False, maximization=True): See the lrs web site (http://cgm.cs.mcgill.ca/~avis/C/lrs.html). * ``"LCP"`` - This algorithm is only suited for 2 player games. - See the gambit web site (http://gambit.sourceforge.net/). + See the gambit web site (http://gambit.sourceforge.net/). Note + that the output differs to the other algorithms: floats are returned. * ``'enumeration'`` - This is a very inefficient algorithm (in essence a brute force approach). @@ -1090,6 +1151,10 @@ def obtain_nash(self, algorithm=False, maximization=True): sage: g=NormalFormGame([A, B]) sage: g.obtain_nash(algorithm='enumeration') [[(0, 0, 3/4, 1/4), (1/28, 27/28, 0)]] + sage: g.obtain_nash(algorithm='lrs') + [[(0, 0, 3/4, 1/4), (1/28, 27/28, 0)]] + sage: g.obtain_nash(algorithm='LCP') + [[(0.0, 0.0, 0.75, 0.25), (0.0357142857, 0.9642857143, 0.0)]] 2 random matrices:: @@ -1108,7 +1173,8 @@ def obtain_nash(self, algorithm=False, maximization=True): [[(1, 0, 0, 0, 0), (0, 1, 0, 0, 0)]] sage: fivegame.obtain_nash(algorithm='lrs') # optional - lrs [[(1, 0, 0, 0, 0), (0, 1, 0, 0, 0)]] - + sage: fivegame.obtain_nash(algorithm='LCP') # optional - LCP + [[(1.0, 0.0, 0.0, 0.0, 0.0), (0.0, 1.0, 0.0, 0.0, 0.0)]] Here is an example of a 3 by 2 game with 3 Nash equilibrium:: @@ -1143,9 +1209,9 @@ def obtain_nash(self, algorithm=False, maximization=True): True sage: lrs_eqs == enumeration_eqs # optional - lrs True - sage: lrs_eqs == LCP_eqs # optional - lrs, gambit + sage: enumeration_eqs == LCP_eqs # optional - gambit False - sage: [(float(k) for k in eq) for eq in lrs_eqs] == LCP_eqs # optional - lrs, gambit + sage: [[[round(float(p), 6) for p in str] for str in eq] for eq in enumeration_eqs] == [[[round(float(p), 6) for p in str] for str in eq] for eq in LCP_eqs] # optional - gambit True """ @@ -1259,7 +1325,6 @@ def _solve_LCP(self, maximization): [[(0.0, 1.0), (0.0, 1.0)]] """ from gambit.nash import ExternalLCPSolver - from gambit import Game strategy_sizes = [p.num_strategies for p in self.players] g = Game.new_table(strategy_sizes) scalar = 1 @@ -1272,7 +1337,7 @@ def _solve_LCP(self, maximization): self.utilities[strategy_profile][1]) output = ExternalLCPSolver().solve(g) nasheq = Parser(output).format_LCP(g) - return nasheq + return sorted(nasheq) def _solve_enumeration(self, maximization=True): r""" @@ -1470,7 +1535,7 @@ def _solve_indifference(self, p1_support, p2_support, M1, M2): \left(\sum_{j \in S(\rho_2)}^{A_{i,j} - A_{i+1,j}\right){\rho_2}_j for all `1\leq i \leq |S(\rho_1)|` (where `A` has been modified to only - contain the row corresponding to `S(\rho_1)`. We also require all + contain the row corresponding to `S(\rho_1)`). We also require all elements of `\rho_2` to sum to 1: .. MATH:: From b336d6f789904a19a5f6a6d1335533948e9a1227 Mon Sep 17 00:00:00 2001 From: vince Date: Sat, 20 Dec 2014 11:43:36 +0000 Subject: [PATCH 069/217] Have added gambit as a standalone package documentation --- src/doc/en/reference/game_theory/index.rst | 1 + src/sage/game_theory/gambit_docs.py | 7 ++++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/doc/en/reference/game_theory/index.rst b/src/doc/en/reference/game_theory/index.rst index eb8beb1e6b4..3aa2d658155 100644 --- a/src/doc/en/reference/game_theory/index.rst +++ b/src/doc/en/reference/game_theory/index.rst @@ -6,6 +6,7 @@ Game Theory sage/game_theory/cooperative_game sage/game_theory/normal_form_game + sage/game_theory/gambit_docs sage/game_theory/matching_game .. include:: ../footer.txt diff --git a/src/sage/game_theory/gambit_docs.py b/src/sage/game_theory/gambit_docs.py index 98bffbe6fe1..0ecc1c5d805 100644 --- a/src/sage/game_theory/gambit_docs.py +++ b/src/sage/game_theory/gambit_docs.py @@ -1,8 +1,8 @@ """ -Gambit +Using Gambit as a standalone package This file contains some information and tests for the use of -`gambit`_ as a stand alone package. +`Gambit `_ as a stand alone package. To install gambit as an optional package run (from root of Sage):: @@ -110,7 +110,8 @@ two player strategic form games. Gambit supports mulitple player games as well as extensive form games: for more details see http://www.gambit-project.org/. -If one really wants to use gambit directly in Sage then integers must first be +If one really wants to use gambit directly in Sage (without using the +`NormalFormGame` class as a wrapper) then integers must first be converted to Python integers (due to the preparser). Here is an example showing the Battle of the Sexes:: From 20c2ac58ba61c0f200e1fed017c7534b7b908590 Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Sat, 20 Dec 2014 17:31:57 +0100 Subject: [PATCH 070/217] Avoid is_package_installed --- src/sage/game_theory/normal_form_game.py | 27 +++++++++++------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/src/sage/game_theory/normal_form_game.py b/src/sage/game_theory/normal_form_game.py index 0e7afc501a8..a67033337a5 100644 --- a/src/sage/game_theory/normal_form_game.py +++ b/src/sage/game_theory/normal_form_game.py @@ -533,10 +533,11 @@ lazy_import('sage.misc.temporary_file', 'tmp_filename') lazy_import('sage.rings.arith', 'lcm') lazy_import('sage.rings.rational', 'Rational') + try: from gambit import Game except: - pass + Game = None class NormalFormGame(SageObject, MutableMapping): r""" @@ -644,12 +645,9 @@ def __init__(self, generator=None): self.players = [] self.utilities = {} matrices = [] - if type(generator) is not list and generator != None: - if is_package_installed('gambit'): - if type(generator) is not Game: - raise TypeError("Generator function must be a list, gambit game or nothing") - else: - raise TypeError("Generator function must be a list or nothing") + if generator is not None: + if type(generator) is not list and type(generator) is not Game: + raise TypeError("Generator function must be a list, gambit game or nothing") if type(generator) is list: if len(generator) == 1: @@ -658,10 +656,9 @@ def __init__(self, generator=None): if matrices[0].dimensions() != matrices[1].dimensions(): raise ValueError("matrices must be the same size") self._two_matrix_game(matrices) - elif is_package_installed('gambit'): - if type(generator) is Game: - game = generator - self._gambit_game(game) + elif type(generator) is Game: + game = generator + self._gambit_game(game) def __delitem__(self, key): r""" @@ -1151,9 +1148,9 @@ def obtain_nash(self, algorithm=False, maximization=True): sage: g=NormalFormGame([A, B]) sage: g.obtain_nash(algorithm='enumeration') [[(0, 0, 3/4, 1/4), (1/28, 27/28, 0)]] - sage: g.obtain_nash(algorithm='lrs') + sage: g.obtain_nash(algorithm='lrs') # optional - lrs [[(0, 0, 3/4, 1/4), (1/28, 27/28, 0)]] - sage: g.obtain_nash(algorithm='LCP') + sage: g.obtain_nash(algorithm='LCP') # optional - gambit [[(0.0, 0.0, 0.75, 0.25), (0.0357142857, 0.9642857143, 0.0)]] 2 random matrices:: @@ -1238,8 +1235,8 @@ def obtain_nash(self, algorithm=False, maximization=True): return self._solve_lrs(maximization) if algorithm == "LCP": - if not is_package_installed('gambit'): - raise NotImplementedError("gambit is not installed") + if Game is None: + raise NotImplementedError("gambit is not installed") for strategy_profile in self.utilities: payoffs = self.utilities[strategy_profile] if payoffs != [int(payoffs[0]), int(payoffs[1])]: From 0b67999d6553a7e85238d5179c3ec046b07f7e6a Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Sat, 20 Dec 2014 17:39:51 +0100 Subject: [PATCH 071/217] Lazily import all of game theory --- src/sage/game_theory/all.py | 8 +++++--- src/sage/game_theory/normal_form_game.py | 12 +++++------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/sage/game_theory/all.py b/src/sage/game_theory/all.py index cdb000b4d28..36a2d3a4f26 100644 --- a/src/sage/game_theory/all.py +++ b/src/sage/game_theory/all.py @@ -1,3 +1,5 @@ -from cooperative_game import CooperativeGame -from normal_form_game import NormalFormGame -from matching_game import MatchingGame +from sage.misc.lazy_import import lazy_import + +lazy_import('sage.game_theory.cooperative_game', 'CooperativeGame') +lazy_import('sage.game_theory.normal_form_game', 'NormalFormGame') +lazy_import('sage.game_theory.matching_game', 'MatchingGame') diff --git a/src/sage/game_theory/normal_form_game.py b/src/sage/game_theory/normal_form_game.py index a67033337a5..453675a3e02 100644 --- a/src/sage/game_theory/normal_form_game.py +++ b/src/sage/game_theory/normal_form_game.py @@ -518,21 +518,19 @@ # (at your option) any later version. # http://www.gnu.org/licenses/ #***************************************************************************** + from collections import MutableMapping from itertools import product from parser import Parser from sage.combinat.cartesian_product import CartesianProduct from sage.misc.latex import latex -from sage.misc.lazy_import import lazy_import from sage.misc.misc import powerset from sage.rings.all import QQ from sage.structure.sage_object import SageObject -lazy_import('sage.matrix.constructor', 'matrix') -lazy_import('sage.matrix.constructor', 'vector') -lazy_import('sage.misc.package', 'is_package_installed') -lazy_import('sage.misc.temporary_file', 'tmp_filename') -lazy_import('sage.rings.arith', 'lcm') -lazy_import('sage.rings.rational', 'Rational') +from sage.matrix.constructor import matrix +from sage.matrix.constructor import vector +from sage.misc.package import is_package_installed +from sage.misc.temporary_file import tmp_filename try: from gambit import Game From 949bed877c94be46e104b195fb2e85b9d829b4bd Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Sat, 20 Dec 2014 17:47:31 +0100 Subject: [PATCH 072/217] Fix documentation formatting --- src/sage/game_theory/normal_form_game.py | 117 +++++++++++------------ 1 file changed, 57 insertions(+), 60 deletions(-) diff --git a/src/sage/game_theory/normal_form_game.py b/src/sage/game_theory/normal_form_game.py index 453675a3e02..ec08f7082f7 100644 --- a/src/sage/game_theory/normal_form_game.py +++ b/src/sage/game_theory/normal_form_game.py @@ -590,7 +590,7 @@ def __init__(self, generator=None): ... NotImplementedError: Nash equilibrium for games with more than 2 players have not been implemented yet. Please see the gambit website (http://gambit.sourceforge.net/) that has a variety of available algorithms - Can initialise a game from a gambit game object: + Can initialise a game from a gambit game object:: sage: from gambit import Game # optional - gambit sage: gambitgame= Game.new_table([2, 2]) # optional - gambit @@ -789,28 +789,26 @@ def _latex_(self): LaTeX method shows the two payoff matrices for a two player game:: - sage: A = matrix([[-1, -2], [-12, 2]]) - sage: B = matrix([[1, 0], [1, -1]]) - sage: g = NormalFormGame([A, B]) - sage: latex(g) - \left(\left(\begin{array}{rr} - -1 & -2 \\ - -12 & 2 - \end{array}\right), \left(\begin{array}{rr} - 1 & 0 \\ - 1 & -1 - \end{array}\right)\right) + sage: A = matrix([[-1, -2], [-12, 2]]) + sage: B = matrix([[1, 0], [1, -1]]) + sage: g = NormalFormGame([A, B]) + sage: latex(g) + \left(\left(\begin{array}{rr} + -1 & -2 \\ + -12 & 2 + \end{array}\right), \left(\begin{array}{rr} + 1 & 0 \\ + 1 & -1 + \end{array}\right)\right) LaTeX method shows nothing interesting for games with more players:: - sage: g = NormalFormGame() - sage: g.add_player(2) # Adding first player with 2 strategies - sage: g.add_player(2) # Adding second player with 2 strategies - sage: g.add_player(2) # Creating a game with three players - sage: latex(g) - \text{\texttt{Normal{ }Form{ }Game{ }...[False,{ }False,{ }False]{\char`\}}}} - - + sage: g = NormalFormGame() + sage: g.add_player(2) # Adding first player with 2 strategies + sage: g.add_player(2) # Adding second player with 2 strategies + sage: g.add_player(2) # Creating a game with three players + sage: latex(g) + \text{\texttt{Normal{ }Form{ }Game{ }...[False,{ }False,{ }False]{\char`\}}}} """ if len(self.players) == 2: M1, M2 = self.payoff_matrices() @@ -872,7 +870,7 @@ def payoff_matrices(self): r""" Returns 2 matrices representing the payoffs for each player. - EXAMPLES :: + EXAMPLES:: sage: p1 = matrix([[1, 2], [3, 4]]) sage: p2 = matrix([[3, 3], [1, 4]]) @@ -954,12 +952,11 @@ def _generate_utilities(self, replacement): INPUT: - - replacement - Boolean value of whether previously created + - ``replacement`` -- Boolean value of whether previously created profiles should be replaced or not. TESTS:: - sage: from sage.game_theory.normal_form_game import _Player sage: g = NormalFormGame() sage: g.players.append(_Player(2)) @@ -1260,32 +1257,31 @@ def _solve_lrs(self, maximization=True): 2 random matrices:: - sage: p1 = matrix([[-1, 4, 0, 2, 0], - ....: [-17, 246, -5, 1, -2], - ....: [0, 1, 1, -4, -4], - ....: [1, -3, 9, 6, -1], - ....: [2, 53, 0, -5, 0]]) - sage: p2 = matrix([[0, 1, 1, 3, 1], - ....: [3, 9, 44, -1, -1], - ....: [1, -4, -1, -3, 1], - ....: [1, 0, 0, 0, 0,], - ....: [1, -3, 1, 21, -2]]) - sage: biggame = NormalFormGame([p1, p2]) - sage: biggame._solve_lrs() # optional - lrs - [[(0, 0, 0, 20/21, 1/21), (11/12, 0, 0, 1/12, 0)]] - + sage: p1 = matrix([[-1, 4, 0, 2, 0], + ....: [-17, 246, -5, 1, -2], + ....: [0, 1, 1, -4, -4], + ....: [1, -3, 9, 6, -1], + ....: [2, 53, 0, -5, 0]]) + sage: p2 = matrix([[0, 1, 1, 3, 1], + ....: [3, 9, 44, -1, -1], + ....: [1, -4, -1, -3, 1], + ....: [1, 0, 0, 0, 0,], + ....: [1, -3, 1, 21, -2]]) + sage: biggame = NormalFormGame([p1, p2]) + sage: biggame._solve_lrs() # optional - lrs + [[(0, 0, 0, 20/21, 1/21), (11/12, 0, 0, 1/12, 0)]] Another test:: - sage: p1 = matrix([[-7, -5, 5], - ....: [5, 5, 3], - ....: [1, -6, 1]]) - sage: p2 = matrix([[-9, 7, 9], - ....: [6, -2, -3], - ....: [-4, 6, -10]]) - sage: biggame = NormalFormGame([p1, p2]) - sage: biggame._solve_lrs() # optional - lrs - [[(0, 1, 0), (1, 0, 0)], [(1/3, 2/3, 0), (0, 1/6, 5/6)], [(1/3, 2/3, 0), (1/7, 0, 6/7)], [(1, 0, 0), (0, 0, 1)]] + sage: p1 = matrix([[-7, -5, 5], + ....: [5, 5, 3], + ....: [1, -6, 1]]) + sage: p2 = matrix([[-9, 7, 9], + ....: [6, -2, -3], + ....: [-4, 6, -10]]) + sage: biggame = NormalFormGame([p1, p2]) + sage: biggame._solve_lrs() # optional - lrs + [[(0, 1, 0), (1, 0, 0)], [(1/3, 2/3, 0), (0, 1/6, 5/6)], [(1/3, 2/3, 0), (1/7, 0, 6/7)], [(1, 0, 0), (0, 0, 1)]] """ from subprocess import PIPE, Popen m1, m2 = self.payoff_matrices() @@ -1311,8 +1307,9 @@ def _solve_lrs(self, maximization=True): def _solve_LCP(self, maximization): r""" Solves a NormalFormGame using Gambit's LCP algorithm. - EXAMPLES: - Simple example. :: + + EXAMPLES:: + sage: a = matrix([[1, 0], [1, 4]]) sage: b = matrix([[2, 3], [2, 4]]) sage: c = NormalFormGame([a, b]) @@ -1442,7 +1439,7 @@ def _solve_enumeration(self, maximization=True): sage: N = NormalFormGame([matrix(2,[7,-8,-4,-8,7,0]),matrix(2,[-9,-1,-8,3,2,3])]) sage: N._solve_enumeration() [[(0, 1), (0, 0, 1)]] - """ + """ M1, M2 = self.payoff_matrices() if maximization is False: @@ -1756,23 +1753,23 @@ def _Hrepresentation(self, m1, m2): class _Player(): def __init__(self, num_strategies): r""" - TESTS: + TESTS:: - sage: from sage.game_theory.normal_form_game import _Player - sage: p = _Player(5) - sage: p.num_strategies - 5 + sage: from sage.game_theory.normal_form_game import _Player + sage: p = _Player(5) + sage: p.num_strategies + 5 """ self.num_strategies = num_strategies def add_strategy(self): r""" - TESTS: + TESTS:: - sage: from sage.game_theory.normal_form_game import _Player - sage: p = _Player(5) - sage: p.add_strategy() - sage: p.num_strategies - 6 + sage: from sage.game_theory.normal_form_game import _Player + sage: p = _Player(5) + sage: p.add_strategy() + sage: p.num_strategies + 6 """ self.num_strategies += 1 From aa5a8f7498143db87a93dad2a9bac875d812e863 Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Sat, 20 Dec 2014 17:54:15 +0100 Subject: [PATCH 073/217] Remove bare "except:" statements --- src/sage/game_theory/normal_form_game.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/sage/game_theory/normal_form_game.py b/src/sage/game_theory/normal_form_game.py index ec08f7082f7..7831d01e53e 100644 --- a/src/sage/game_theory/normal_form_game.py +++ b/src/sage/game_theory/normal_form_game.py @@ -534,7 +534,7 @@ try: from gambit import Game -except: +except ImportError: Game = None class NormalFormGame(SageObject, MutableMapping): @@ -1600,13 +1600,13 @@ def _solve_indifference(self, p1_support, p2_support, M1, M2): try: a = linearsystem1.solve_right(linearsystemrhs1) b = linearsystem2.solve_right(linearsystemrhs2) - - if self._is_NE(a, b, p1_support, p2_support, M1, M2): - return [a, b] - return None - except: + except ValueError: return None + if self._is_NE(a, b, p1_support, p2_support, M1, M2): + return [a, b] + return None + def _is_NE(self, a, b, p1_support, p2_support, M1, M2): r""" For vectors that obey indifference for a given support pair, From 8f3cc37cb1f3decb38311b7b2a6a152e04c0b9fd Mon Sep 17 00:00:00 2001 From: vince Date: Sun, 21 Dec 2014 07:16:09 +0000 Subject: [PATCH 074/217] Fixing incorrect optional tag --- src/sage/game_theory/normal_form_game.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/game_theory/normal_form_game.py b/src/sage/game_theory/normal_form_game.py index 7831d01e53e..65ebbe03bd9 100644 --- a/src/sage/game_theory/normal_form_game.py +++ b/src/sage/game_theory/normal_form_game.py @@ -1165,7 +1165,7 @@ def obtain_nash(self, algorithm=False, maximization=True): [[(1, 0, 0, 0, 0), (0, 1, 0, 0, 0)]] sage: fivegame.obtain_nash(algorithm='lrs') # optional - lrs [[(1, 0, 0, 0, 0), (0, 1, 0, 0, 0)]] - sage: fivegame.obtain_nash(algorithm='LCP') # optional - LCP + sage: fivegame.obtain_nash(algorithm='LCP') # optional - gambit [[(1.0, 0.0, 0.0, 0.0, 0.0), (0.0, 1.0, 0.0, 0.0, 0.0)]] Here is an example of a 3 by 2 game with 3 Nash equilibrium:: From aa3fc67ee4fa477bde51b95ca4ae54b47cd58749 Mon Sep 17 00:00:00 2001 From: vince Date: Sun, 21 Dec 2014 07:18:13 +0000 Subject: [PATCH 075/217] Fixing wording for LCP algorithm differences --- src/sage/game_theory/normal_form_game.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sage/game_theory/normal_form_game.py b/src/sage/game_theory/normal_form_game.py index 65ebbe03bd9..392faa1e9d7 100644 --- a/src/sage/game_theory/normal_form_game.py +++ b/src/sage/game_theory/normal_form_game.py @@ -1049,7 +1049,8 @@ def obtain_nash(self, algorithm=False, maximization=True): * ``"LCP"`` - This algorithm is only suited for 2 player games. See the gambit web site (http://gambit.sourceforge.net/). Note - that the output differs to the other algorithms: floats are returned. + that the output differs from the other algorithms: floats are + returned. * ``'enumeration'`` - This is a very inefficient algorithm (in essence a brute force approach). From 8e1bffa9abae86c482d780e80d2679e8ab1b971f Mon Sep 17 00:00:00 2001 From: Ben Hutz Date: Sun, 21 Dec 2014 21:15:01 -0500 Subject: [PATCH 076/217] 17535: fix homogenize for function fields --- src/sage/schemes/affine/affine_morphism.py | 23 +++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/src/sage/schemes/affine/affine_morphism.py b/src/sage/schemes/affine/affine_morphism.py index 5821d182e40..d98459b4318 100644 --- a/src/sage/schemes/affine/affine_morphism.py +++ b/src/sage/schemes/affine/affine_morphism.py @@ -429,6 +429,16 @@ def homogenize(self,n): sage: f = H([x^2 - 2*x*y + z*x, z^2 -y^2 , 5*z*y]) sage: f.homogenize(2).dehomogenize(2) == f True + + :: + + sage: K. = FunctionField(QQ) + sage: A. = AffineSpace(K,1) + sage: f = Hom(A,A)([x^2 + c]) + sage: f.homogenize(1) + Scheme endomorphism of Projective Space of dimension 1 over Rational function field in c over Rational Field + Defn: Defined on coordinates by sending (x0 : x1) to + (x0^2 + c*x1^2 : x1^2) """ #it is possible to homogenize the domain and codomain at different coordinates if isinstance(n,(tuple,list)): @@ -459,7 +469,7 @@ def homogenize(self,n): F = [S(R(self[i].numerator()*l[i]).subs(D)) for i in range(M)] #homogenize - F.insert(ind[1], S(prod(L).subs(D))) #coerce in case l is a constant + F.insert(ind[1], S(R(prod(L)).subs(D))) #coerce in case l is a constant try: #remove possible gcd of the polynomials g = gcd(F) @@ -535,6 +545,17 @@ def dynatomic_polynomial(self,period): sage: f = H([x^2+CC.0]) sage: f.dynatomic_polynomial(2) x^2 + x + 1.00000000000000 + 1.00000000000000*I + + :: + + sage: K. = FunctionField(QQ) + sage: A. = AffineSpace(K,1) + sage: f = Hom(A,A)([x^2 + c]) + sage: f.dynatomic_polynomial(4) + x^12 + 6*c*x^10 + x^9 + (15*c^2 + 3*c)*x^8 + 4*c*x^7 + (20*c^3 + 12*c^2 + 1)*x^6 + + (6*c^2 + 2*c)*x^5 + (15*c^4 + 18*c^3 + 3*c^2 + 4*c)*x^4 + (4*c^3 + 4*c^2 + 1)*x^3 + + (6*c^5 + 12*c^4 + 6*c^3 + 5*c^2 + c)*x^2 + (c^4 + 2*c^3 + c^2 + 2*c)*x + + c^6 + 3*c^5 + 3*c^4 + 3*c^3 + 2*c^2 + 1 """ if self.domain() != self.codomain(): raise TypeError("Must have same domain and codomain to iterate") From 9b59a4652cd15e469609f1823a01f3dd3392b0fb Mon Sep 17 00:00:00 2001 From: vince Date: Tue, 23 Dec 2014 10:59:32 +0000 Subject: [PATCH 077/217] Fixed reference to just two algorithms --- src/sage/game_theory/normal_form_game.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/game_theory/normal_form_game.py b/src/sage/game_theory/normal_form_game.py index 7831d01e53e..50f4e807932 100644 --- a/src/sage/game_theory/normal_form_game.py +++ b/src/sage/game_theory/normal_form_game.py @@ -445,7 +445,7 @@ Note that degenerate games can cause problems for most algorithms. The following example in fact has an infinite quantity of equilibria which -is evidenced by the two algorithms returning different solutions:: +is evidenced by the various algorithms returning different solutions:: sage: A = matrix([[3,3],[2,5],[0,6]]) sage: B = matrix([[3,3],[2,6],[3,1]]) From 2533822c6535302cadbadbdc3c7c55000fa1563b Mon Sep 17 00:00:00 2001 From: vince Date: Tue, 23 Dec 2014 11:24:43 +0000 Subject: [PATCH 078/217] Have added link to gambit docs from norma form game docs at relevant point --- src/sage/game_theory/normal_form_game.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/sage/game_theory/normal_form_game.py b/src/sage/game_theory/normal_form_game.py index 50f4e807932..47d601b84a6 100644 --- a/src/sage/game_theory/normal_form_game.py +++ b/src/sage/game_theory/normal_form_game.py @@ -361,7 +361,7 @@ further compatibility between Sage and gambit is actively being developed: https://github.com/tturocy/gambit/tree/sage_integration. -Note that the Gambit implementation of `LCP` can only handle integer +Note that the Gambit implementation of ``LCP`` can only handle integer payoffs. If a non integer payoff is used an error will be raised:: sage: A = matrix([[2, 1], [1, 2.5]]) @@ -403,6 +403,11 @@ sage: g # optional - gambit Normal Form Game with the following utilities: {(0, 1): [2.0, 10.0], (1, 0): [10.0, 2.0], (0, 0): [8.0, 8.0], (1, 1): [5.0, 5.0]} +For more information on using Gambit in Sage see: :mod:`Using Gambit in +Sage`. This includes how to access Gambit +directly using the version of IPython shipped with Sage and an explanation +as to why the ``int`` calls are needed to handle the Sage preparser. + Here is a slightly longer game that would take too long to solve with ``'enumeration'``. Consider the following: From 601a80180604d64b1498a5e0ea2a76cec78da9ad Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Wed, 24 Dec 2014 19:12:53 +0530 Subject: [PATCH 079/217] trac #17545: Rephrase the 'Reporting bugs' section of the developer's manual --- src/doc/en/developer/reviewer_checklist.rst | 4 +- src/doc/en/developer/trac.rst | 135 +++++++++----------- 2 files changed, 65 insertions(+), 74 deletions(-) diff --git a/src/doc/en/developer/reviewer_checklist.rst b/src/doc/en/developer/reviewer_checklist.rst index e3d9925d22f..7bf3b7f2332 100644 --- a/src/doc/en/developer/reviewer_checklist.rst +++ b/src/doc/en/developer/reviewer_checklist.rst @@ -27,8 +27,8 @@ You can now begin the review by reading the diff code. **Read the diff:** the diff (i.e. the ticket's content) can be obtained by clicking on the (green) branch's name that appears on the trac ticket. If that -name appears in red you can say so in a comment and set the ticket to -``needs_work`` (see :ref:`section-trac-ticket-status`). +name appears in red (see :ref:`section-trac-fields`) you can say so in a comment +and set the ticket to ``needs_work`` (see :ref:`section-trac-ticket-status`). **Build the code:** while you read the code, you can :ref:`rebuild Sage with the new code `. If you do not know how to **download the diff --git a/src/doc/en/developer/trac.rst b/src/doc/en/developer/trac.rst index cea12f14224..f8327eb0e43 100644 --- a/src/doc/en/developer/trac.rst +++ b/src/doc/en/developer/trac.rst @@ -174,83 +174,60 @@ issuing some basic gitolite commands, for example:: perms writable +.. _trac-bug-report: + +.. _section-trac-bug-report: Reporting Bugs ============== -If you think you have found a bug in Sage, you should first search -through the following Google groups for postings related to your -possible bug: - -* ``sage-devel``: http://groups.google.com/group/sage-devel -* ``sage-support``: http://groups.google.com/group/sage-support - -Maybe the problem you have encountered has already been discussed. You -should also search the trac server to see if anyone else has opened a -ticket about your bug. - -If you do not find anything, and you are not sure that you have found -a bug, ask about it on ``sage-devel``. You might be asked to open a -new ticket on the trac server. As mentioned above, you need an account -to do this. To report a bug, login and click on the "New ticket" -button. Type a meaningful one-liner in the "Short summary" box, with -more information in the larger box below. You should include at least -one explicit, reproducible example illustrating your bug (and/or the -steps required to reproduce the buggy behavior). You should also -include the version of Sage (and any relevant packages) you are using, -and operating system information, being precise as possible (32-bit, -64-bit, ...). - -Between the "Summary" and "Full description" boxes, there is a -place to choose the "Type" of the ticket: "Defect", "Enhancement", -or "Task". Use your best judgment here; a bug should probably be -reported as a "Defect". - -Also pick a component for your bug; this is sometimes -straightforward. If your bug deals with Sage's calculus -implementation, choose "calculus". If it is not obvious, do your -best. Choose a milestone; if you are not sure what to choose, just -choose the numbered version of Sage from the menu ("sage-5.10", for -example). Type in some helpful keywords. In the box labeled "Assign -to", type "somebody" if you are not sure what else to do. - -Hit the "Preview" button to make sure everything looks okay, and -then hit "Submit ticket". - -If you do not have an account on the trac system to report directly, -you are still encouraged to report any possible bug to the -``sage-devel`` mailing list at ``sage-devel@googlegroups.com``. -The list is moderated for new users and requires subscription. -In your bug report to ``sage-devel``, make sure to include the -following information: - -- **operating system**: as precise as possible and architecture - (32-bit, 64-bit, ...) - -- affected version: the exact **version number** and the downloaded - package (source, precompiled, virtual machine image, or an upgrade - from a previous version (which one?)) - -- provide a **reproducible example** and/or define the steps to - reproduce the erroneous behaviour. +If you think you have found a bug in Sage, here is the procedure: + +- Search through our Google groups for postings related to your possible bug (it + may have been fixed/reported already): + + * ``sage-devel``: ``_ + * ``sage-support``: ``_ + + Similarly, you can search :ref:`chapter-sage-trac` to see if anyone else has + opened a ticket about your bug. + +- If you do not find anything, and you are not sure that you have found a bug, + ask about it on `sage-devel `_. A + bug report should contain: + + - An explicit and **reproducible example** illustrating your bug (and/or the + steps required to reproduce the buggy behavior). + + - The **version** of Sage you run, as well as the version of the optional + packages that may be involved in the bug. + + - Describe your **operating system** as accurately as you can and your + architecture (32-bit, 64-bit, ...). + +- You might be asked to open a new ticket. In this case, follow the + :ref:`section-trac-new-ticket`. Thank you in advance for reporting bugs to improve Sage in the future! +.. _section-trac-new-ticket: Guidelines for Opening Tickets ============================== -In addition to bug reports, you should also open a ticket if you -have some new code which extends Sage's capabilities. If you have a -feature request, start a discussion on ``sage-devel`` first, -and then if there seems to be general agreement that you have a -good idea, open a ticket describing the idea. +In addition to bug reports (see :ref:`trac-bug-report`), you should also open a +ticket if you have some new code that makes Sage a better tool. If you have a +feature request, start a discussion on ``sage-devel`` first, and then if there +seems to be general agreement that you have a good idea, open a ticket +describing the idea. + +- Do you already have a **trac account**? If not, :ref:`click here + `. -When you consider opening a new ticket, please bear the following -points in mind. +**Before** opening a new ticket, consider the following points: -- Before opening a ticket, make sure that nobody else has opened a - ticket about the same or closely related issue. +- Make sure that nobody else has opened a ticket about the same or closely + related issue. - It is much better to open several specific tickets than one that is very broad. Indeed, a single ticket which deals with lots of @@ -265,9 +242,17 @@ points in mind. "Make Sage the best mathematical software in the world". There is no metric to measure this properly and it is highly subjective. -- If appropriate, provide URLs to background information or email - threads relevant to the problem you are reporting. +- For bug reports: the ticket's description should contain the information + described at :ref:`section-trac-bug-report`. +- If appropriate, provide URLs to background information or sage-devel + conversation relevant to the problem you are reporting. + +**When creating** the ticket, you may find useful to read +:ref:`section-trac-fields`. + +Unless you know what you are doing, leave the milestone field to its default +value. .. _section-trac-fields: @@ -281,8 +266,11 @@ of fields that can be changed. Here is a comprehensive overview (for the * **Reported by:** The trac account name of whoever created the ticket. Cannot be changed. -* **Owned by:** Trac account name of owner, by default the person in - charge of the **Component:**. Generally not used in the Sage trac. +* **Owned by:** Trac account name of owner, by default the person in charge of + the Component (see below). Generally not used in the Sage trac. + +* **Type:** One of ``enhancement`` (e.g. a new feature), ``defect`` (e.g. a bug + fix), or ``task`` (rarely used). * **Priority:** The priority of the ticket. Keep in mind that the "blocker" label should be used very sparingly. @@ -306,10 +294,9 @@ of fields that can be changed. Here is a comprehensive overview (for the * **Merged in:** The Sage release where the ticket was merged in. Only changed by the release manager. -* **Authors:** Real name of the ticket author (or list of authors). +* **Authors:** Real name of the ticket author(s). -* **Reviewers:** Real name of the ticket reviewer (or list of - reviewers). +* **Reviewers:** Real name of the ticket reviewer(s). * **Report Upstream:** If the ticket is a bug in an upstream component of Sage, this field is used to summarize the communication with the @@ -318,7 +305,11 @@ of fields that can be changed. Here is a comprehensive overview (for the * **Work issues:** Issues that need to be resolved before the ticket can leave the "needs work" status. -* **Branch:** See :ref:`section-walkthrough-branch` +* **Branch:** The Git branch containing the ticket's code (see + :ref:`section-walkthrough-branch`). It is displayed in green color, + unless there is a conflict between the branch and the latest beta + release (red color). In this case, the branch should be merged or + rebased. * **Dependencies:** Does the ticket depend on another ticket? Sometimes, a ticket requires that another ticket be applied From 49ab0bfde612e284685535e1d9387b2a1849559e Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Sat, 27 Dec 2014 19:08:33 +0530 Subject: [PATCH 080/217] trac #17555: Remove the dev scripts' documentation --- src/doc/en/developer/dev_script.rst | 460 ---------------------------- src/doc/en/developer/index.rst | 3 - src/doc/en/reference/dev/index.rst | 1 - 3 files changed, 464 deletions(-) delete mode 100644 src/doc/en/developer/dev_script.rst diff --git a/src/doc/en/developer/dev_script.rst b/src/doc/en/developer/dev_script.rst deleted file mode 100644 index 0bcec7d2b7c..00000000000 --- a/src/doc/en/developer/dev_script.rst +++ /dev/null @@ -1,460 +0,0 @@ -.. _chapter-devscript: - -==================== -The Sage Dev Scripts -==================== - -As an alternative to using git directly or ``git trac``, Sage comes with -a set of developer scripts which help you with common interactions with -the bug tracker (see :ref:`chapter-sage-trac`) and with handling revisions -of your code. The developer scripts use the git distributed revision -control system under the hood; you'll need to configure this -(:ref:`section-git-setup-name`), but you do not need to know anything -about it (see :ref:`chapter-manual-git` only if you want to). - -The dev scripts are mainly intended as a bridge to full use of the -revision control system as one gains experience with it; for many -users it will be more advantageous to learn to use git directly, -either via :ref:`git trac ` or in its full power. The dev -scripts are not as powerful and may have bugs that git will not, and -are not as well documented. Long-term knowledge of git is currently -very useful in projects outside of Sage, especially for open source -and academic collaboration; that said, there is a somewhat steeper -learning curve to using git directly. - -.. hint:: - - We recommend to not use the developer scripts. You will have to - :ref:`learn git ` sooner or later, why not - start now? - - -Introduction -============ - -We assume here that the ``sage`` executable of your development -installation of Sage is in your ``PATH``. If this is not the case, you -might have to replace ``sage`` by ``./sage`` or ``/path/to/sage`` in -the following. You can also use the developer scripts from the Sage -prompt. All commandline options to ``sage -dev`` are also available as -methods of the ``dev`` object in a Sage session. That is, for example, -to checkout a ticket you can either run:: - - [user@localhost]$ sage -dev checkout --ticket 1729 - On ticket #1729 with associated local branch "ticket/1729". - - # Use "sage --dev merge" to include another ticket/branch. - # Use "sage --dev commit" to save changes into a new commit. - -in a terminal or, equivalently, within Sage - -.. skip # don't actually doctest - -:: - - sage: dev.checkout(1729) - On ticket #1729 with associated local branch "ticket/1729". - - # Use "dev.merge()" to include another ticket/branch. - # Use "dev.commit()" to save changes in a new commit. - -Note that the number sign ``#`` (a.k.a. hash or pound sign) is the -comment marker for both the shell and Python. So if you were to input -``#1729``, it will be interpreted as the comment "1729" and not passed -to the development scripts. Always specify the ticket number as a -plain number, without the number sign in front. - -.. warning:: - - During the transitional period it can happen that you end up - on a branch where the developer scripts are not available or - outdated. If this is the case, i.e., if ``sage -dev`` does not - work properly anymore, run:: - - git pull git://trac.sagemath.org/sage.git master - sage -b - - This will merge the latest version of the developer scripts - with your current branch. After rebuilding the Sage library, - the dev scripts will work again. - - -Finally, keep in mind that there is help and a good listing of options -for ``sage -dev`` available:: - - [user@localhost]$ ./sage -dev -h - usage: sage-dev [-h] subcommand ... - - The developer interface for sage. - - optional arguments: - -h, --help show this help message and exit - - subcommands: - abandon Abandon a ticket or branch. - checkout Checkout another branch. - ... - vanilla Return to a clean version of Sage. - - -.. _section-devscript-add: - -Contributing to the Sage Source Code -==================================== - -.. _section-devscript-add-create: - -Create a Ticket ---------------- - -Suppose you have written an algorithm for calculating the last twin prime, and -want to add it to Sage. You would first open a ticket for that:: - - [user@localhost]$ sage -dev create-ticket - -This will give you an editor in which you can give a summary and a -description of what you want to do. If you are not sure which values -to put for the other fields, you can leave the defaults or have a look -at :ref:`section-trac-fields`. After you close the editor, a new -ticket will be opened on the trac server. From that point on, everyone -can see what you intend to do which lets us avoid duplicating work. If -you want to cancel the creation of a ticket, then you can just save an -empty file. This will abort the operation. - -Alternatively, you can use the `web interface to the Sage trac -development server `_ to open a new ticket, -just log in and click on "Create Ticket". - - -.. _section-devscript-add-edit: - -Editing the Source Code ------------------------ - -If you want to work on a ticket which you or somebody else created, -you first need to make a local "branch". The development scripts -maintain a mapping between local branches and trac tickets. Creating a -new local branch for a ticket is easy:: - - [user@localhost]$ sage -dev checkout --ticket 1729 - On ticket #1729 with associated local branch "ticket/1729". - - # Use "sage --dev merge" to include another ticket/branch. - # Use "sage --dev commit" to save changes into a new commit. - -Essentially, a branch is a copy (except that it doesn't take up twice -the space) of the Sage source code where you can store your -modifications to the Sage source code and which you can upload to trac -tickets. Your new branch is now called ``ticket/``. Unless -you upload ("push") it, see below, it will only be on your local -system and not visible to anyone else. - -.. note:: - - Unless you have moved to the ``develop`` branch of the source, this - will be based on the last stable release of Sage. It is worth - checking to see that no one else has already worked on the files - you are working on in more recent development versions. - -At this point you can start editing the source code. :ref:`Other -chapters ` of this developer guide -explain how your code should look like to fit into Sage, and how we -ensure high code quality throughout. - -Whenever you have reached one of your goals, you should make a *commit*. -This takes a snapshot of the whole Sage source code that you have been -working on and records the changes into your local branch:: - - [user@localhost]$ sage -dev commit - Commit your changes to branch "ticket/1729"? [Yes/no] y - - # Use "sage --dev push" to push your commits to the trac server once you are - # done. - -You will be asked to write a message describing your changes. It is -common to write a one line summary, then a blank line, and then a 1-2 -paragraph explanation of your changes. If your changes are minor, then -just the one-line summary can be enough. - -If you are working on a larger project, it can be useful to break up -your work into multiple commits: Each commit is saved, enabling you to -retrieve older versions of files from the repository. So, even if you -accidentally delete something, you can get it back later. Also, if you -find a mistake in one of your earlier commits, then you just correct -it in the Sage source code and then add another commit on top. - - -.. _section-devscript-add-push: - -Uploading Changes to Trac -------------------------- - -At some point, you may wish to share your changes with the rest of us: -maybe it is ready for review, or maybe you are collaborating with -someone and want to share your changes "up until now". This is simply -done by:: - - [user@localhost]$ sage -dev push - -On trac, your remote branch will be called -``u//ticket/``. This name will automatically be -added to the "Branch:" field on the ticket. Other developers then know -where to find your work in the git repository. - -It is common to go through some iterations of ``sage -dev commit`` -before you upload, and you will probably also have uploaded a few -times before your changes are ready for review. - -If you are happy with the changes you uploaded, you want somebody else -to review them, so they can be included into the next version of -Sage. If your ticket is ready for review, you should set it to -``needs_review`` on the trac server. This can be done though the `web -interface `_, or, alternatively, using the -development scripts. For the latter, run:: - - [user@localhost]$ sage -dev edit-ticket - -This will give you an editor in which you can edit the ticket. Change the -status to:: - - Status: needs_review - -And add yourself as an author for that ticket by inserting the following as the -first line:: - - Authors: Your Real Name - -If you want to add an additional comment for potential reviewers, run:: - - [user@localhost]$ sage -dev comment - - -.. _section-devscript-add-local: - -Starting Without a Ticket -------------------------- - -You might not want to create a trac ticket for your changes. For -example, if you are only working on your own code or if you are making -experimental changes that you are likely to throw away if they do not -work out. In that case, you can also start a branch that only lives in -your local repository. To do this, you use checkout but specify a -branch name instead of the ticket number. For example, to create a new -branch ``my_branch``, you would run:: - - [user@localhost]$ sage -dev checkout --branch my_branch - -This is assuming that you do not already have a local branch called -``my_branch``. If that were the case, you would just switch to the -already-existing branch. Once on your branch, you can work with it as -described in :ref:`section-devscript-add-edit`. - -You can upload your local branch later to an existing ticket. This -works exactly like in the case where you started with a ticket, except -that you have to specify the ticket number. That is:: - - [user@localhost]$ sage -dev push --ticket - -where you have to replace ```` with the number of the trac -ticket. - - -.. _section-devscript-merge: - -Merging -======= - -As soon as you are working on a bigger project that spans multiple -tickets you will want to base your work on branches that have not been -merged into Sage yet. This is natural in collaborative development, -and in fact you are very much encouraged to split your work into -logically different parts. Ideally, each part that is useful on its -own and and can be reviewed independently should be a different -ticket, instead of a huge patch bomb. - -For this purpose, you can incorporate branches from other tickets (or -just other local branches) into your current branch. This is called -merging, and all it does is include commits from other branches into -your current branch. In particular, this is done when a new Sage -release is made: the finished tickets are merged with the Sage master -and the result is the next Sage version. Git is smart enough to not -merge commits twice. In particular, it is possible to merge two -branches, one of which had already merged the other branch. - -The syntax for merging is easy. If the code that you want to -incorporate is on a trac ticket number ````, use:: - - [user@localhost]$ sage -dev merge --ticket - -Optionally, you can add the merged ticket to the trac "Dependency:" -field. Note that the merged commits become part of the current branch, -regardless of whether they are noted on trac. Adding a dependency -implies that the dependency must be reviewed first. After the -dependency is reviewed, the commits that came from the dependency are -no longer listed in the output of ``sage -dev diff``. - -.. warning:: - - You should avoid merging tickets both ways. Once ticket A merged - ticket B and ticket B merged ticket A, there is no way to - distinguish commits that were originally made in ticket A or in - ticket B. Effectively, merging both ways combines the branches and - makes individual review impossible. - - In practice, you should only merge when one of the following holds: - - * Either two tickets conflict, then you have to merge one into the - other in order to resolve the merge conflict. - - * Or you definitely need a feature that has been developed as part - of another branch. - -A special case of merging is merging in the ``master`` branch. This -brings your local branch up to date with the newest Sage version. The -above warning against unnecessary merges still applies, though. Try to -do all of your development with the Sage version that you originally -started with. The only reason for merging in the master branch is if -you need a new feature or if your branch conflicts. - - -.. _section-devscript-review: - -Reviewing -========= - -This section gives an example how to review using the ``sage`` command. -For a detailed discussion of Sage's review process, -see :ref:`Reviewing Patches `. - -Now suppose you want to review the existing work on a ticket, such as the one -you created in the last section. For definiteness, suppose you want to review -#12270. You would do that as follows:: - - [user@localhost]$ sage -dev checkout --ticket 12270 - -This command will download the branch on Trac in case you do not have any local -work on ticket 12270. (If you do, you may have to merge your changes; see -below). You can now test the ticket; you'll probably want to call ``make`` or -``sage -b`` first to rebuild Sage with the changes. See -:ref:`section-walkthrough-make` for details of which to use. - -.. note:: - - This will be based on whatever previous branch you were on, which will - likely be the previous stable release. This means there may be quite a - bit a other changes that need to compile to get you up to speed. - -Another important command is:: - - [user@localhost]$ sage -dev diff - -which lists all source code changes that are part of the current -branch. That is, it lists the changes from the current directory to the -current branch. - -.. note:: - - For instance, if you based on the master branch, and just committed the - branch on Trac, you could do ``sage -dev diff --base master`` - to see what the difference is. - - -If the ticket were to be positively reviewed, this is -the code that will be added to Sage. Note that there is no way to -"exclude dependencies", just as there is no guarantee that unreviewed -dependencies will become part of Sage. The best way to exclude -dependencies from the diff output is to review them. Once the -dependency becomes part of the master branch, they are automatically -removed. - -Most likely, your will want to add a comment to the ticket as part of -your review:: - - [user@localhost]$ sage -dev comment - -This will open a text editor in which you can type, and upload the -result to Trac. - -It is also possible that you make some changes to the code as part of -your review. After you have done that, you can upload your changes -back to trac:: - - [user@localhost]$ sage -dev commit - [user@localhost]$ sage -dev push - -This will update the ticket to now point to your branch, including -your changes. Your branch is based on the original author's branch, so -s/he can easily incorporate your changes into his/her own branch (see -below). - -You may receive messages like the following:: - - [user@localhost]$ sage -dev push - The branch "u//ticket/12270" does not exist on the remote server. - Create new remote branch? [Yes/no] yes - The branch field of ticket #12270 needs to be updated from its current value - "u//branch/name" to "u//ticket/12270" - Change the "Branch:" field? [Yes/no] yes - -In this case, typically it is easiest to simply accept these questions, -though there is also the possibility of creating a public branch. - -.. _section-devscript-collaborate: - -Collaboration -============= - -It is very easy to collaborate by just going through the above steps any number of times:: - - # developer 1 - - sage -dev commit - sage -dev push - - # developer 2 - sage -dev pull - - sage -dev commit - sage -dev push - - # developer 1 - sage -dev pull - - sage -dev commit - sage -dev push - (etc) - -The obvious problem is when you both work on the same ticket simultaneously:: - - # developer 1 - - sage -dev commit - sage -dev push - - # developer 2 - - sage -dev commit - sage -dev push - Changes not compatible with remote branch - u//ticket/12270; consider - downloading first. Are you sure you want to continue? - -Developer 2 should probably select ``No``, and do as suggested:: - - sage -dev pull - -This will try to merge the changes developer 1 made into the ones that -developer 2 made. The latter should check whether all seems okay, and -if so, upload the changes:: - - sage -dev push # works now - -It is possible that the changes cannot be automatically merged. In -that case, developer 2 will have to do some manual fixup after -downloading and before uploading:: - - - sage -dev commit - sage -dev push - - diff --git a/src/doc/en/developer/index.rst b/src/doc/en/developer/index.rst index fd4c49d6ac5..873a0386db3 100644 --- a/src/doc/en/developer/index.rst +++ b/src/doc/en/developer/index.rst @@ -54,8 +54,6 @@ development! - :ref:`Unfamiliar with Git or revision control ? ` - :ref:`How to install it ? ` - :ref:`How to configure it for use with Trac ? ` - - :ref:`The dev scripts ` may be used without installing - git, but are only intended as a bridge to using it properly. Git and Trac ============ @@ -91,7 +89,6 @@ Putting your local changes on a Trac ticket. :maxdepth: 3 git_trac - dev_script .. _section-git-tricks-and-tips: diff --git a/src/doc/en/reference/dev/index.rst b/src/doc/en/reference/dev/index.rst index 7f5d1c41755..7af9589354f 100644 --- a/src/doc/en/reference/dev/index.rst +++ b/src/doc/en/reference/dev/index.rst @@ -4,7 +4,6 @@ Sage's Development Scripts .. toctree:: :maxdepth: 2 - sage/dev/sagedev sage/dev/git_interface sage/dev/trac_interface sage/dev/user_interface From 40220f712f59ca51491ad1a427f2db34e839e83b Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Sun, 28 Dec 2014 09:21:14 +0530 Subject: [PATCH 081/217] trac #17555: Some other references to the doc --- src/doc/en/reference/dev/conf.py | 1 - src/doc/en/reference/dev/index.rst | 11 ----------- src/doc/en/reference/index.rst | 1 - 3 files changed, 13 deletions(-) delete mode 120000 src/doc/en/reference/dev/conf.py delete mode 100644 src/doc/en/reference/dev/index.rst diff --git a/src/doc/en/reference/dev/conf.py b/src/doc/en/reference/dev/conf.py deleted file mode 120000 index 2bdf7e68470..00000000000 --- a/src/doc/en/reference/dev/conf.py +++ /dev/null @@ -1 +0,0 @@ -../conf_sub.py \ No newline at end of file diff --git a/src/doc/en/reference/dev/index.rst b/src/doc/en/reference/dev/index.rst deleted file mode 100644 index 7af9589354f..00000000000 --- a/src/doc/en/reference/dev/index.rst +++ /dev/null @@ -1,11 +0,0 @@ -Sage's Development Scripts -========================== - -.. toctree:: - :maxdepth: 2 - - sage/dev/git_interface - sage/dev/trac_interface - sage/dev/user_interface - -.. include:: ../footer.txt diff --git a/src/doc/en/reference/index.rst b/src/doc/en/reference/index.rst index bda723ef6c6..ab5f488f7f7 100644 --- a/src/doc/en/reference/index.rst +++ b/src/doc/en/reference/index.rst @@ -104,7 +104,6 @@ Doctesting, Interfaces, Databases, Miscellany --------------------------------------------- * :doc:`Doctesting ` -* :doc:`Development Scripts ` * :doc:`Interpreter Interfaces ` * :doc:`C/C++ Library Interfaces ` * :doc:`Databases ` From fbc0a401f2e984796aa9d6103fe5c2397c7d4d1a Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Sun, 28 Dec 2014 10:52:11 -0500 Subject: [PATCH 082/217] Trac #17556: Remove simplify_log() from simplify_full(). The simplify_log() method for symbolic expressions is only valid when its argument (the expression) is real. There is an elementary example -- now a doctest -- where simplify_log() produces an incorrect result for complex expressions. Since Sage assumes that expressions are complex by default, it's not safe to have simplify_log() as part of simplify_full(); i.e. under the "simplify" moniker. This commit removes simplify_log() from simplify_full(), and adds a doctest for the new behavior. Since the 'one' algorithm was used in simplify_log() within simplify_full(), the simplifications that it performed were extremely neutered. Its removal is therefore no great loss. --- src/sage/modules/vector_symbolic_dense.py | 2 +- src/sage/symbolic/expression.pyx | 12 +++++++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/sage/modules/vector_symbolic_dense.py b/src/sage/modules/vector_symbolic_dense.py index 19f15833db3..d7b62d807c9 100644 --- a/src/sage/modules/vector_symbolic_dense.py +++ b/src/sage/modules/vector_symbolic_dense.py @@ -17,7 +17,7 @@ sage: type(u) sage: u.simplify_full() - (1, log(6*y^2)) + (1, log(3*y) + log(2*y)) TESTS: diff --git a/src/sage/symbolic/expression.pyx b/src/sage/symbolic/expression.pyx index 139ed46fdaf..f962269e12a 100644 --- a/src/sage/symbolic/expression.pyx +++ b/src/sage/symbolic/expression.pyx @@ -8158,12 +8158,20 @@ cdef class Expression(CommutativeRingElement): 1/2*log(2*t) - 1/2*log(t) sage: forget() + Complex logs are not contracted, :trac:`17556`:: + + sage: x,y = SR.var('x,y') + sage: assume(y, 'complex') + sage: f = log(x*y) - (log(x) + log(y)) + sage: f.simplify_full() + log(x*y) - log(x) - log(y) + sage: forget() + """ x = self x = x.simplify_factorial() x = x.simplify_trig() x = x.simplify_rational() - x = x.simplify_log('one') x = x.simplify_rational() return x @@ -8940,8 +8948,6 @@ cdef class Expression(CommutativeRingElement): log((sqrt(2) + 1)*(sqrt(2) - 1)) sage: _.simplify_rational() 0 - sage: log_expr.simplify_full() # applies both simplify_log and simplify_rational - 0 We should use the current simplification domain rather than set it to 'real' explicitly (:trac:`12780`):: From 3ea1ded198974490f5e93409947344f03798e25b Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Sun, 28 Dec 2014 22:18:40 -0500 Subject: [PATCH 083/217] Trac #17556: Add simplify_rectform() to simplify_full(). There is a newer simplification, Expression.simplify_rectform(), which is not performed as part of Expression.simplify_full(). The rectform simplification is safe for complex expressions, so it makes sense to include it as part of the "full" simplification list. This commit also removes a redundant simplify_rational() left over from the removal of simplify_log() from simplify_full(). --- src/sage/symbolic/expression.pyx | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/sage/symbolic/expression.pyx b/src/sage/symbolic/expression.pyx index f962269e12a..2b3301d925a 100644 --- a/src/sage/symbolic/expression.pyx +++ b/src/sage/symbolic/expression.pyx @@ -8167,12 +8167,19 @@ cdef class Expression(CommutativeRingElement): log(x*y) - log(x) - log(y) sage: forget() + The simplifications from :meth:`simplify_rectform` are + performed, :trac:`17556`:: + + sage: f = ( e^(I*x) - e^(-I*x) ) / ( I*e^(I*x) + I*e^(-I*x) ) + sage: f.simplify_full() + sin(x)/cos(x) + """ x = self x = x.simplify_factorial() + x = x.simplify_rectform() x = x.simplify_trig() x = x.simplify_rational() - x = x.simplify_rational() return x full_simplify = simplify_full From 044db1d2b7bc4e914e92ed7aa87629cbe958bc52 Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Sun, 28 Dec 2014 22:46:41 -0500 Subject: [PATCH 084/217] Trac #17556: Add Expression.simplify_log() to Expression.simplify_real(). The Expression.simplify_log() simplification is only valid over the real numbers. It has been removed from Expression.simplify_full() for that reason, but there's no reason it can't be performed as part of simplify_real() where it is quite clear that the expression must be real. This commit performs simplify_log() as part of simplify_real(), and updates the documentation of simplify_real(). --- src/sage/symbolic/expression.pyx | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/sage/symbolic/expression.pyx b/src/sage/symbolic/expression.pyx index 2b3301d925a..09fe3bb1f8a 100644 --- a/src/sage/symbolic/expression.pyx +++ b/src/sage/symbolic/expression.pyx @@ -8329,7 +8329,8 @@ cdef class Expression(CommutativeRingElement): def simplify_real(self): r""" Simplify the given expression over the real numbers. This allows - the simplification of `\sqrt{x^{2}}` into `\left|x\right|`. + the simplification of `\sqrt{x^{2}}` into `\left|x\right|` and + the contraction of `\log(x) + \log(y)` into `\log(xy)`. INPUT: @@ -8346,6 +8347,13 @@ cdef class Expression(CommutativeRingElement): sage: f.simplify_real() abs(x) + :: + + sage: y = SR.var('y') + sage: f = log(x) + 2*log(y) + sage: f.simplify_real() + log(x*y^2) + TESTS: We set the Maxima ``domain`` variable to 'real' before we call @@ -8411,7 +8419,9 @@ cdef class Expression(CommutativeRingElement): for v in self.variables(): assume(v, 'real'); - result = self.simplify(); + # This will round trip through Maxima, essentially performing + # self.simplify() in the process. + result = self.simplify_log() # Set the domain back to what it was before we were called. maxima.eval('domain: %s$' % original_domain) From fb892e3b10f2a6b708c70ea3b579ac3ce3d03907 Mon Sep 17 00:00:00 2001 From: Clemens Heuberger Date: Mon, 29 Dec 2014 17:57:27 +0100 Subject: [PATCH 085/217] Trac 17563: New method: Expression.has_wild to check for wildcard Wrap ginac's function haswild to check whether self has a wildcard anywhere as a subexpression. --- src/sage/symbolic/expression.pyx | 15 +++++++++++++++ src/sage/symbolic/ginac.pxd | 1 + 2 files changed, 16 insertions(+) diff --git a/src/sage/symbolic/expression.pyx b/src/sage/symbolic/expression.pyx index 139ed46fdaf..5893af32a73 100644 --- a/src/sage/symbolic/expression.pyx +++ b/src/sage/symbolic/expression.pyx @@ -1603,6 +1603,21 @@ cdef class Expression(CommutativeRingElement): return self.operator().name() return self._maxima_init_() + def has_wild(self): + """ + Return ``True`` if this expression contains a wildcard. + + EXAMPLES:: + + sage: (1 + x^2).has_wild() + False + sage: (SR.wild(0) + x^2).has_wild() + True + sage: SR.wild(0).has_wild() + True + """ + return haswild(self._gobj) + def is_real(self): """ Return True if this expression is known to be a real number. diff --git a/src/sage/symbolic/ginac.pxd b/src/sage/symbolic/ginac.pxd index 29aef54e8b2..b1b514ec98d 100644 --- a/src/sage/symbolic/ginac.pxd +++ b/src/sage/symbolic/ginac.pxd @@ -127,6 +127,7 @@ cdef extern from "ginac_wrap.h": # Pattern matching wildcards GEx g_wild "wild"(unsigned int label) except + + bint haswild(GEx x) except + # Series back to poly GEx series_to_poly(GEx e) except + From f2a69ab9a9d5d3e276e937ae013bb63046a7eabd Mon Sep 17 00:00:00 2001 From: Simon King Date: Tue, 30 Dec 2014 10:15:07 +0100 Subject: [PATCH 086/217] More useful functions for biseq_t --- .../bounded_integer_sequences.pxd | 6 ++- .../bounded_integer_sequences.pyx | 48 +++++++++++++------ 2 files changed, 39 insertions(+), 15 deletions(-) diff --git a/src/sage/data_structures/bounded_integer_sequences.pxd b/src/sage/data_structures/bounded_integer_sequences.pxd index ee819024c27..b14a81e3f34 100644 --- a/src/sage/data_structures/bounded_integer_sequences.pxd +++ b/src/sage/data_structures/bounded_integer_sequences.pxd @@ -36,7 +36,11 @@ ctypedef biseq_s biseq_t[1] cdef bint biseq_init(biseq_t R, mp_size_t l, mp_bitcnt_t itemsize) except -1 cdef void biseq_dealloc(biseq_t S) cdef bint biseq_init_copy(biseq_t R, biseq_t S) except -1 +cdef tuple biseq_pickle(biseq_t S) +cdef bint biseq_unpickle(biseq_t R, tuple bitset_data, mp_bitcnt_t itembitsize, mp_size_t length) except -1 cdef bint biseq_init_list(biseq_t R, list data, size_t bound) except -1 +cdef inline Py_hash_t biseq_hash(biseq_t S) +cdef inline int biseq_cmp(biseq_t S1, biseq_t S2) cdef bint biseq_init_concat(biseq_t R, biseq_t S1, biseq_t S2) except -1 cdef inline bint biseq_startswith(biseq_t S1, biseq_t S2) except -1 cdef mp_size_t biseq_contains(biseq_t S1, biseq_t S2, mp_size_t start) except -2 @@ -54,4 +58,4 @@ cdef class BoundedIntegerSequence: cpdef list list(self) cpdef BoundedIntegerSequence maximal_overlap(self, BoundedIntegerSequence other) -cpdef BoundedIntegerSequence NewBISEQ(tuple data, mp_bitcnt_t itembitsize, mp_size_t length) +cpdef BoundedIntegerSequence NewBISEQ(tuple bitset_data, mp_bitcnt_t itembitsize, mp_size_t length) diff --git a/src/sage/data_structures/bounded_integer_sequences.pyx b/src/sage/data_structures/bounded_integer_sequences.pyx index 0ff59d82114..2e6950c1e0d 100644 --- a/src/sage/data_structures/bounded_integer_sequences.pyx +++ b/src/sage/data_structures/bounded_integer_sequences.pyx @@ -144,6 +144,20 @@ cdef bint biseq_init_copy(biseq_t R, biseq_t S) except -1: bitset_copy(R.data, S.data) sig_off() +# +# Pickling +# + +cdef tuple biseq_pickle(biseq_t S): + return (bitset_pickle(S.data), S.itembitsize, S.length) + +cdef bint biseq_unpickle(biseq_t R, tuple bitset_data, mp_bitcnt_t itembitsize, mp_size_t length) except -1: + biseq_init(R, length, itembitsize) + sig_on() + bitset_unpickle(R.data, bitset_data) + sig_off() + return 1 + # # Conversion # @@ -172,6 +186,22 @@ cdef bint biseq_init_list(biseq_t R, list data, size_t bound) except -1: biseq_inititem(R, index, item_c) index += 1 +cdef inline Py_hash_t biseq_hash(biseq_t S): + cdef Py_hash_t hash = 0 + cdef mp_size_t i + for i from 0 <= i < S.data.limbs: + hash = hash*(1073807360)+S.data.bits[i] + return hash + +cdef inline int biseq_cmp(biseq_t S1, biseq_t S2): + cdef int c = cmp(S1.itembitsize, S2.itembitsize) + if c: + return c + c = cmp(S1.length, S2.length) + if c: + return c + return bitset_cmp(S1.data, S2.data) + # # Arithmetics # @@ -714,7 +744,7 @@ cdef class BoundedIntegerSequence: True """ - return NewBISEQ, (bitset_pickle(self.data.data), self.data.itembitsize, self.data.length) + return NewBISEQ, biseq_pickle(self.data) def __len__(self): """ @@ -1290,13 +1320,7 @@ cdef class BoundedIntegerSequence: left = self except TypeError: return 1 - cdef int c = cmp(left.data.itembitsize, right.data.itembitsize) - if c: - return c - c = cmp(left.data.length, right.data.length) - if c: - return c - return bitset_cmp(left.data.data, right.data.data) + return biseq_cmp(left.data, right.data) def __hash__(self): """ @@ -1322,7 +1346,7 @@ cdef class BoundedIntegerSequence: True """ - return bitset_hash(self.data.data) + return biseq_hash(self.data) cpdef BoundedIntegerSequence NewBISEQ(tuple bitset_data, mp_bitcnt_t itembitsize, mp_size_t length): """ @@ -1354,11 +1378,7 @@ cpdef BoundedIntegerSequence NewBISEQ(tuple bitset_data, mp_bitcnt_t itembitsize """ cdef BoundedIntegerSequence out = BoundedIntegerSequence.__new__(BoundedIntegerSequence) - # bitset_unpickle assumes that out.data.data is initialised. - biseq_init(out.data, length, itembitsize) - sig_on() - bitset_unpickle(out.data.data, bitset_data) - sig_off() + biseq_unpickle(out.data, bitset_data, itembitsize, length) return out def _biseq_stresstest(): From fd3fd824ce08e1459ab2c140c59e713425a86e8f Mon Sep 17 00:00:00 2001 From: Simon King Date: Tue, 30 Dec 2014 11:39:17 +0100 Subject: [PATCH 087/217] Better hash function for biseq_t. Document the new functions. --- .../bounded_integer_sequences.pyx | 28 +++++++++++++++---- 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/src/sage/data_structures/bounded_integer_sequences.pyx b/src/sage/data_structures/bounded_integer_sequences.pyx index 2e6950c1e0d..346d64803d2 100644 --- a/src/sage/data_structures/bounded_integer_sequences.pyx +++ b/src/sage/data_structures/bounded_integer_sequences.pyx @@ -23,10 +23,27 @@ cimported in Cython modules: Initialize ``R`` as a copy of ``S``. +- ``cdef tuple biseq_pickle(biseq_t S)`` + + Return a triple ``(bitset_data, itembitsize, length)`` defining ``S``. + +- ``cdef bint biseq_unpickle(biseq_t R, tuple bitset_data, mp_bitcnt_t itembitsize, mp_size_t length) except -1`` + + Initialise ``R`` from data returned by ``biseq_pickle``. + - ``cdef bint biseq_init_list(biseq_t R, list data, size_t bound) except -1`` Convert a list to a bounded integer sequence, which must not be allocated. +- ``cdef inline Py_hash_t biseq_hash(biseq_t S)`` + + Hash value for ``S``. + +- ``cdef inline int biseq_cmp(biseq_t S1, biseq_t S2)`` + + Comparison of ``S1`` and ``S2``. This takes into account the bound, the + length, and the list of items of the two sequences. + - ``cdef bint biseq_init_concat(biseq_t R, biseq_t S1, biseq_t S2) except -1`` Concatenate ``S1`` and ``S2`` and write the result to ``R``. Does not test @@ -187,11 +204,7 @@ cdef bint biseq_init_list(biseq_t R, list data, size_t bound) except -1: index += 1 cdef inline Py_hash_t biseq_hash(biseq_t S): - cdef Py_hash_t hash = 0 - cdef mp_size_t i - for i from 0 <= i < S.data.limbs: - hash = hash*(1073807360)+S.data.bits[i] - return hash + return S.itembitsize*(1073807360)+bitset_hash(S.data) cdef inline int biseq_cmp(biseq_t S1, biseq_t S2): cdef int c = cmp(S1.itembitsize, S2.itembitsize) @@ -1346,7 +1359,10 @@ cdef class BoundedIntegerSequence: True """ - return biseq_hash(self.data) + cdef Py_hash_t h = biseq_hash(self.data) + if h == -1: + return 0 + return h cpdef BoundedIntegerSequence NewBISEQ(tuple bitset_data, mp_bitcnt_t itembitsize, mp_size_t length): """ From ecdc1bb29405fabd82f2f31321d585acefeee83e Mon Sep 17 00:00:00 2001 From: Martin von Gagern Date: Tue, 30 Dec 2014 21:10:11 +0100 Subject: [PATCH 088/217] Introduce method tracing fixture. This is the code as contained in 4c7d5f2 from #16571. --- src/sage/doctest/fixtures.py | 155 +++++++++++++++++++++++++++++++++++ 1 file changed, 155 insertions(+) create mode 100644 src/sage/doctest/fixtures.py diff --git a/src/sage/doctest/fixtures.py b/src/sage/doctest/fixtures.py new file mode 100644 index 00000000000..992762605f7 --- /dev/null +++ b/src/sage/doctest/fixtures.py @@ -0,0 +1,155 @@ +from functools import wraps + +class PropertyAccessTracerProxy(object): + r""" + Proxy object which prints all property and method access to an object. + + INPUT: + + - ``delegate``: The actual object to be proxied. + + - ``prefix``: String to prepend to each printed output. + + - ``reads``: Whether to trace read access as well + + EXAMPLE:: + + sage: class Foo(object): + ....: def f(self, *args): + ....: return self.x*self.x + ....: + sage: foo = Foo() + sage: from sage.doctest.fixtures import PropertyAccessTracerProxy + sage: pat = PropertyAccessTracerProxy(foo) + sage: pat.x = 2 + write x = 2 + sage: pat.x + read x = 2 + 2 + sage: pat.f(3) + call f(3) -> 4 + 4 + """ + + def __init__(self, delegate, prefix=" ", reads=True): + object.__setattr__(self, "delegate", delegate) + object.__setattr__(self, "prefix", prefix) + object.__setattr__(self, "reads", reads) + + def __getattribute__(self, name): + r""" + EXAMPLE:: + + sage: class Foo(object): + ....: def f(self, *args): + ....: return self.x*self.x + ....: + sage: foo = Foo() + sage: foo.x = 2 + sage: from sage.doctest.fixtures import PropertyAccessTracerProxy + sage: pat = PropertyAccessTracerProxy(foo) + sage: pat.x + read x = 2 + 2 + sage: pat.f(3) + call f(3) -> 4 + 4 + """ + delegate = object.__getattribute__(self, "delegate") + prefix = object.__getattribute__(self, "prefix") + fmt = object.__getattribute__(self, "fmt") + val = getattr(delegate, name) + if callable(val) and name not in delegate.__dict__: + @wraps(val) + def wrapper(*args, **kwds): + arglst = [fmt(arg) for arg in args] + arglst.extend("{}={}".format(k, fmt(v)) + for k, v in sorted(kwds.items())) + res = val(*args, **kwds) + print("{}call {}({}) -> {}" + .format(prefix, name, ", ".join(arglst), fmt(res))) + return res + return wrapper + else: + if object.__getattribute__(self, "reads"): + print("{}read {} = {}".format(prefix, name, fmt(val))) + return val + + def __setattr__(self, name, val): + r""" + EXAMPLE:: + + sage: class Foo(object): + ....: pass + ....: + sage: foo = Foo() + sage: from sage.doctest.fixtures import PropertyAccessTracerProxy + sage: pat = PropertyAccessTracerProxy(foo) + sage: pat.x = 2 + write x = 2 + sage: foo.x + 2 + """ + delegate = object.__getattribute__(self, "delegate") + prefix = object.__getattribute__(self, "prefix") + fmt = object.__getattribute__(self, "fmt") + print("{}write {} = {}".format(prefix, name, fmt(val))) + setattr(delegate, name, val) + + @classmethod + def fmt(cls, val): + if isinstance(val, frozenset): + return ("frozenset([{}])".format + (", ".join(map(cls.fmt, sorted(val))))) + if isinstance(val, set): + return ("set([{}])".format + (", ".join(map(cls.fmt, sorted(val))))) + r = repr(val) + return r + + +def traceMethod(obj, meth, **kwds): + r""" + Trace the doings of a given method. + It prints method entry with arguments, + access to members and other methods during method execution + as well as method exit with return value. + + INPUT: + + - ``obj``: The object containing the method. + + - ``meth``: The name of the method to be traced. + + EXAMPLE:: + + sage: class Foo(object): + ....: def f(self): + ....: self.y = self.g(self.x) + ....: def g(self, arg): + ....: return arg + 1 + ....: + sage: foo = Foo() + sage: foo.x = 3 + sage: from sage.doctest.fixtures import traceMethod + sage: traceMethod(foo, "f") + sage: foo.f() + enter f() + read x = 3 + call g(3) -> 4 + write y = 4 + exit f -> None + """ + f = getattr(obj, meth).__func__ + t = PropertyAccessTracerProxy(obj, **kwds) + fmt = PropertyAccessTracerProxy.fmt + @wraps(f) + def g(*args, **kwds): + arglst = [fmt(arg) for arg in args] + arglst.extend("{}={}".format(k, fmt(v)) + for k, v in sorted(kwds.items())) + print("enter {}({})".format(meth, ", ".join(arglst))) + res = f(t, *args, **kwds) + print("exit {} -> {}".format(meth, fmt(res))) + return res + setattr(obj, meth, g) From 9f2ecde3636dcdfed6bdb637304cb57e8d54517c Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Wed, 31 Dec 2014 16:01:44 +0530 Subject: [PATCH 089/217] trac #16412: Review --- src/doc/en/faq/faq-general.rst | 1 + src/doc/en/faq/faq-usage.rst | 52 ++++++++++++++++------------------ 2 files changed, 26 insertions(+), 27 deletions(-) diff --git a/src/doc/en/faq/faq-general.rst b/src/doc/en/faq/faq-general.rst index a8e5b199078..edb77996be5 100644 --- a/src/doc/en/faq/faq-general.rst +++ b/src/doc/en/faq/faq-general.rst @@ -220,6 +220,7 @@ How do I get help? """""""""""""""""" For support about usage of Sage, there are two options: + * The question-and-answer website `ask.sagemath.org `_ * The email list `sage-support `_ diff --git a/src/doc/en/faq/faq-usage.rst b/src/doc/en/faq/faq-usage.rst index 80e68bd0f57..664356182b1 100644 --- a/src/doc/en/faq/faq-usage.rst +++ b/src/doc/en/faq/faq-usage.rst @@ -8,34 +8,32 @@ FAQ: Using Sage How do I get started? """"""""""""""""""""" -You can try out Sage without downloading anything using -SageMathCloud™. Go to http://cloud.sagemath.org and set up a free -account. If you log in, you will access to the latest version of Sage -and to many other software. - -There is also a "once-off" version of Sage called the Sage cell server, -available for doing one computation at a time at http://sagecell.sagemath.org/ - -A final possibility is to go to a public Sage notebook server and -set up a free account. If you log in, you will be working on a free Sage -notebook server that will work identically to the one you get with -Sage. The server http://sagenb.org is one such, though it runs an older -version of Sage and is scheduled to be retired in the future in favor of -the SageMathCloud. - -To download a pre-built binary Sage distribution, visit the page - -You can try out Sage without downloading anything. Go to -http://www.sagenb.org and set up a free account. If you log in, you -will be working on a free Sage notebook server that will work -identically to the one you get with Sage. To download a pre-built -binary Sage distribution, visit the page -http://www.sagemath.org/download.html and click on the link for the -binary for your operating system. The source code of Sage is also -available for you to download and use. Go to -http://www.sagemath.org/download-source.html to download the tar -archive for any release of Sage. +You can try out Sage without downloading anything: +* **SageMathCloud™:** Go to http://cloud.sagemath.org and set up a free + account. + + If you log in, you will access to the latest version of Sage and to + many other software. + +* **Sage cell:** A "once-off" version of Sage, available for doing one + computation at a time. http://sagecell.sagemath.org/ + +* **Sagenb:** some public Sage notebook server allow you to create a free + account. + + If you log in, you will be working on a free Sage notebook server that will + work identically to the one you get within Sage. The server http://sagenb.org + is one such, though it runs an older version of Sage and is scheduled to be + retired in the future in favor of the SageMathCloud. + +To download a **pre-built binary** Sage distribution, visit +http://sagemath.org/download.html and click on the link for the binary for your +operating system. + +The **source code** of Sage is also available for you to download and use. Go to +http://www.sagemath.org/download-source.html to download the tar archive for any +release of Sage. The Sage notebook runs within a web browser. To start the notebook, issue the following command in a terminal, if ``sage`` is in your ``PATH`` :: From 87fb1030cd271861aafe2fb42e58ab03df87e34a Mon Sep 17 00:00:00 2001 From: Ralf Stephan Date: Wed, 31 Dec 2014 16:53:36 +0100 Subject: [PATCH 090/217] 17507: change error msg, add doctest --- src/sage/gsl/integration.pyx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/sage/gsl/integration.pyx b/src/sage/gsl/integration.pyx index 21291422ddf..f4415ff7e76 100644 --- a/src/sage/gsl/integration.pyx +++ b/src/sage/gsl/integration.pyx @@ -145,6 +145,11 @@ def numerical_integral(func, a, b=None, (0.10806674191683065, 1.1997818507228991e-15), (0.09745444625548845, 1.0819617008493815e-15), (0.088750683050217577, 9.8533051773561173e-16)] + sage: y = var('y') + sage: numerical_integral(x*y, 0, 1) + Traceback (most recent call last): + ... + ValueError: Integrand has 2 variables but only 0 parameters Note the parameters are always a tuple even if they have one component. @@ -249,7 +254,8 @@ def numerical_integral(func, a, b=None, return (((b - a) * func), 0.0) if len(vars) != 1: if len(params) + 1 != len(vars): - raise ValueError, "Integrand has wrong number of parameters" + raise ValueError, \ + "Integrand has {} variables but only {} parameters".format(len(vars), len(params)) to_sub = dict(zip(vars[1:], params)) func = func.subs(to_sub) func = func._fast_float_(str(vars[0])) From 749ad0b181f1bea2fb7d58dda82310e96371d1e2 Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Thu, 1 Jan 2015 14:43:09 +0530 Subject: [PATCH 091/217] trac #17577: IncidenceStructure.induced_substructure --- .../combinat/designs/incidence_structures.py | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/src/sage/combinat/designs/incidence_structures.py b/src/sage/combinat/designs/incidence_structures.py index cb8fc653033..7fb2cbdfa07 100644 --- a/src/sage/combinat/designs/incidence_structures.py +++ b/src/sage/combinat/designs/incidence_structures.py @@ -30,6 +30,7 @@ :meth:`~IncidenceStructure.is_isomorphic` | Return whether the two incidence structures are isomorphic. :meth:`~IncidenceStructure.edge_coloring` | Return an optimal edge coloring` :meth:`~IncidenceStructure.copy` | Return a copy of the incidence structure. + :meth:`~IncidenceStructure.induced_substructure` | Return the substructure induced by a set of points. REFERENCES: @@ -575,6 +576,64 @@ def copy(self): __copy__ = copy + def induced_substructure(self,points): + r""" + Return the substructure induced by a set of points. + + The substructure induced in `\mathcal H` by a set `X\subseteq V(\mathcal + H)` of points is the incidence structure `\mathcal H_X` defined on `X` + whose sets are all `S\in \mathcal H` such that `S\subseteq X`. + + INPUT: + + - ``points`` -- a set of points. + + .. NOTE:: + + This method goes over all sets of ``self`` before building a new + :class:`IncidenceStructure` (which involves some relabelling and + sorting). It probably should not be called in a performance-critical + code. + + EXAMPLE: + + A Fano plane with one point removed:: + + sage: F = designs.steiner_triple_system(7) + sage: F.induced_substructure([0..5]) + Incidence structure with 6 points and 4 blocks + + TESTS:: + + sage: F.induced_substructure([0..50]) + Traceback (most recent call last): + ... + ValueError: 7 is not a point of the incidence structure + sage: F.relabel(dict(enumerate("abcdefg"))) + sage: F.induced_substructure("abc") + Incidence structure with 3 points and ... + sage: F.induced_substructure("Y") + Traceback (most recent call last): + ... + ValueError: Y is not a point of the incidence structure + """ + # Checking the input + if self._point_to_index is None: + n = self.num_points() + for x in points: + x = int(x) + if x<0 or x >= n: + raise ValueError("{} is not a point of the incidence structure".format(x)) + int_points = points + else: + try: + int_points = [self._point_to_index[x] for x in points] + except KeyError: + raise ValueError("{} is not a point of the incidence structure".format(x)) + + points = set(points) + return IncidenceStructure(points, + [S for S in self._blocks if points.issuperset(S)]) def ground_set(self): r""" From 5fc4999530c6462e560b07dcb2c99463ae6a4a55 Mon Sep 17 00:00:00 2001 From: Ralf Stephan Date: Thu, 1 Jan 2015 16:53:34 +0100 Subject: [PATCH 092/217] 16201: implement global default series precision, accessors --- src/sage/misc/all.py | 3 ++- src/sage/misc/defaults.py | 15 +++++++++++++++ src/sage/rings/laurent_series_ring.py | 8 ++++---- src/sage/rings/power_series_ring.py | 12 ++++++++---- src/sage/symbolic/expression.pyx | 9 +++++++-- 5 files changed, 36 insertions(+), 11 deletions(-) diff --git a/src/sage/misc/all.py b/src/sage/misc/all.py index 08b0234dc8d..ed3399d332e 100644 --- a/src/sage/misc/all.py +++ b/src/sage/misc/all.py @@ -63,7 +63,8 @@ from mathml import mathml -from defaults import set_default_variable_name +from defaults import (set_default_variable_name, + series_precision, set_series_precision) from sage_eval import sage_eval, sageobj diff --git a/src/sage/misc/defaults.py b/src/sage/misc/defaults.py index 037da6c9f7a..e822a46bd2a 100644 --- a/src/sage/misc/defaults.py +++ b/src/sage/misc/defaults.py @@ -48,3 +48,18 @@ def set_default_variable_name(name, separator=''): global var_name, var_sep var_name = str(name) var_sep = str(separator) + + +# default series precision +series_prec = 20 + +def series_precision(): + return series_prec + +def set_series_precision(prec): + """ + Change the Sage-wide precision for series (symbolic, + power series, Laurent series). + """ + global series_prec + series_prec = prec \ No newline at end of file diff --git a/src/sage/rings/laurent_series_ring.py b/src/sage/rings/laurent_series_ring.py index b333b204d60..efacd3eb3ac 100644 --- a/src/sage/rings/laurent_series_ring.py +++ b/src/sage/rings/laurent_series_ring.py @@ -31,7 +31,7 @@ from sage.categories.complete_discrete_valuation import CompleteDiscreteValuationFields laurent_series = {} -def LaurentSeriesRing(base_ring, name=None, names=None, default_prec=20, sparse=False): +def LaurentSeriesRing(base_ring, name=None, names=None, default_prec=None, sparse=False): """ EXAMPLES:: @@ -136,7 +136,7 @@ class LaurentSeriesRing_generic(commutative_ring.CommutativeRing): sage: TestSuite(F).run() """ - def __init__(self, base_ring, name=None, default_prec=20, sparse=False, category=None): + def __init__(self, base_ring, name=None, default_prec=None, sparse=False, category=None): """ Initialization @@ -641,7 +641,7 @@ def power_series_ring(self): return self._power_series_ring class LaurentSeriesRing_domain(LaurentSeriesRing_generic, integral_domain.IntegralDomain): - def __init__(self, base_ring, name=None, default_prec=20, sparse=False): + def __init__(self, base_ring, name=None, default_prec=None, sparse=False): """ Initialization @@ -654,7 +654,7 @@ def __init__(self, base_ring, name=None, default_prec=20, sparse=False): class LaurentSeriesRing_field(LaurentSeriesRing_generic, field.Field): _default_category = CompleteDiscreteValuationFields() - def __init__(self, base_ring, name=None, default_prec=20, sparse=False): + def __init__(self, base_ring, name=None, default_prec=None, sparse=False): """ Initialization diff --git a/src/sage/rings/power_series_ring.py b/src/sage/rings/power_series_ring.py index 9fce0a68972..9f2e58a4507 100644 --- a/src/sage/rings/power_series_ring.py +++ b/src/sage/rings/power_series_ring.py @@ -164,8 +164,8 @@ def PowerSeriesRing(base_ring, name=None, arg2=None, names=None, - ``default_prec`` - the default precision used if an exact object must be changed to an approximate object in order to do an arithmetic - operation. If left as ``None``, it will be set to 20 in the - univariate case, and 12 in the multivariate case. + operation. If left as ``None``, it will be set to the global + default (20) in the univariate case, and 12 in the multivariate case. - ``sparse`` - (default: ``False``) whether power series are represented as sparse objects. @@ -349,7 +349,8 @@ def PowerSeriesRing(base_ring, name=None, arg2=None, names=None, # and thus that is what the code below expects; this behavior is being # deprecated, and will eventually be removed. if default_prec is None and arg2 is None: - default_prec = 20 + from sage.misc.defaults import series_precision + default_prec = series_precision() elif arg2 is not None: default_prec = arg2 @@ -454,7 +455,7 @@ class PowerSeriesRing_generic(UniqueRepresentation, commutative_ring.Commutative A power series ring. """ Element = power_series_poly.PowerSeries_poly - def __init__(self, base_ring, name=None, default_prec=20, sparse=False, + def __init__(self, base_ring, name=None, default_prec=None, sparse=False, use_lazy_mpoly_ring=False, category=None): """ Initializes a power series ring. @@ -503,6 +504,9 @@ def __init__(self, base_ring, name=None, default_prec=20, sparse=False, R = PolynomialRing(base_ring, name, sparse=sparse) self.__poly_ring = R self.__is_sparse = sparse + if default_prec is None: + from sage.misc.defaults import series_precision + default_prec = series_precision() self.__params = (base_ring, name, default_prec, sparse) if use_lazy_mpoly_ring and (is_MPolynomialRing(base_ring) or \ diff --git a/src/sage/symbolic/expression.pyx b/src/sage/symbolic/expression.pyx index 139ed46fdaf..0eaf1786861 100644 --- a/src/sage/symbolic/expression.pyx +++ b/src/sage/symbolic/expression.pyx @@ -3492,7 +3492,7 @@ cdef class Expression(CommutativeRingElement): for g in self.gradient()]) - def series(self, symbol, int order): + def series(self, symbol, int order=-1): r""" Return the power series expansion of self in terms of the given variable to the given order. @@ -3503,7 +3503,9 @@ cdef class Expression(CommutativeRingElement): such as ``x == 5``; if an equality is given, the expansion is around the value on the right hand side of the equality - - ``order`` - an integer + - ``order`` - an integer; if nothing given, it is set + to the global default (``20``), which can be changed + using :meth:`set_series_precision` OUTPUT: @@ -3564,6 +3566,9 @@ cdef class Expression(CommutativeRingElement): """ cdef Expression symbol0 = self.coerce_in(symbol) cdef GEx x + if order < 0: + from sage.misc.defaults import series_precision + order = series_precision() sig_on() try: x = self._gobj.series(symbol0._gobj, order, 0) From 70b61ab33aef1500ded86d5c750c7fe379f76b0c Mon Sep 17 00:00:00 2001 From: vince Date: Fri, 2 Jan 2015 10:11:55 +0000 Subject: [PATCH 093/217] Fixing capitalisation of ipython --- src/sage/game_theory/gambit_docs.py | 2 +- src/sage/game_theory/normal_form_game.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/game_theory/gambit_docs.py b/src/sage/game_theory/gambit_docs.py index 0ecc1c5d805..7f8a47f9e7d 100644 --- a/src/sage/game_theory/gambit_docs.py +++ b/src/sage/game_theory/gambit_docs.py @@ -10,7 +10,7 @@ The `python API documentation for gambit _` shows various examples -that can be run easily in Ipython. To run the Ipython packaged with Sage run +that can be run easily in ipython. To run the ipython packaged with Sage run (from root of Sage):: $ ./sage -ipython diff --git a/src/sage/game_theory/normal_form_game.py b/src/sage/game_theory/normal_form_game.py index c4a6dab4263..91eb35e31df 100644 --- a/src/sage/game_theory/normal_form_game.py +++ b/src/sage/game_theory/normal_form_game.py @@ -405,7 +405,7 @@ For more information on using Gambit in Sage see: :mod:`Using Gambit in Sage`. This includes how to access Gambit -directly using the version of IPython shipped with Sage and an explanation +directly using the version of iPython shipped with Sage and an explanation as to why the ``int`` calls are needed to handle the Sage preparser. Here is a slightly longer game that would take too long to solve with From cc282fbb24694785e1c3e5a5c75a838fdea4aee9 Mon Sep 17 00:00:00 2001 From: vince Date: Fri, 2 Jan 2015 10:13:52 +0000 Subject: [PATCH 094/217] Fixing syntax for hyperlink --- src/sage/game_theory/gambit_docs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/game_theory/gambit_docs.py b/src/sage/game_theory/gambit_docs.py index 7f8a47f9e7d..90401e732e8 100644 --- a/src/sage/game_theory/gambit_docs.py +++ b/src/sage/game_theory/gambit_docs.py @@ -9,7 +9,7 @@ $ ./sage -i gambit The `python API documentation for gambit -_` shows various examples +`_ shows various examples that can be run easily in ipython. To run the ipython packaged with Sage run (from root of Sage):: From c9f036afd06cb3d9949177d70e235c359db4f70c Mon Sep 17 00:00:00 2001 From: vince Date: Fri, 2 Jan 2015 10:19:11 +0000 Subject: [PATCH 095/217] Fixing syntaxing --- src/sage/game_theory/gambit_docs.py | 2 +- src/sage/game_theory/normal_form_game.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/game_theory/gambit_docs.py b/src/sage/game_theory/gambit_docs.py index 90401e732e8..92b1fbf31e8 100644 --- a/src/sage/game_theory/gambit_docs.py +++ b/src/sage/game_theory/gambit_docs.py @@ -100,7 +100,7 @@ In [12]: solver.solve(g) Out[12]: [] -If we solve this with the `LCP` solver we get the expected Nash equilibrium:: +If we solve this with the ``LCP`` solver we get the expected Nash equilibrium:: In [13]: solver = gambit.nash.ExternalLCPSolver() In [14]: solver.solve(g) diff --git a/src/sage/game_theory/normal_form_game.py b/src/sage/game_theory/normal_form_game.py index 91eb35e31df..80ffa11a6c6 100644 --- a/src/sage/game_theory/normal_form_game.py +++ b/src/sage/game_theory/normal_form_game.py @@ -206,7 +206,7 @@ algorithm uses the optional 'lrs' package. To install it type ``sage -i lrs`` at the command line. For more information see [A2000]_. -* ``LCP``: Linear complementarity program algorithm for 2 player games. +* ``'LCP'``: Linear complementarity program algorithm for 2 player games. This algorithm uses the open source game theory package: `Gambit `_ [MMAT2014]_. At present this is the only gambit algorithm available in sage but further development will @@ -1052,7 +1052,7 @@ def obtain_nash(self, algorithm=False, maximization=True): * ``'lrs'`` - This algorithm is only suited for 2 player games. See the lrs web site (http://cgm.cs.mcgill.ca/~avis/C/lrs.html). - * ``"LCP"`` - This algorithm is only suited for 2 player games. + * ``'LCP'`` - This algorithm is only suited for 2 player games. See the gambit web site (http://gambit.sourceforge.net/). Note that the output differs from the other algorithms: floats are returned. From 771948a52e02d77f99d6dc3d85155371a4aa7cbb Mon Sep 17 00:00:00 2001 From: Martin von Gagern Date: Fri, 2 Jan 2015 21:27:13 +0100 Subject: [PATCH 096/217] Address issues from first review. This addresses several issues raised by Niles Johnson in #16571 comment 26 and 27. - Renamed traceMethod to trace_method - Moved docstring from PropertyAccessTracerProxy class to __init__ method - Added docstring for fmt method - Added author block and copyright - Wrote module description to indicate the kind of code I expect here - Included file in reference manual - Adjusted doctests from control module --- src/doc/en/reference/doctest/index.rst | 1 + src/sage/doctest/control.py | 3 +- src/sage/doctest/fixtures.py | 117 +++++++++++++++++++------ 3 files changed, 93 insertions(+), 28 deletions(-) diff --git a/src/doc/en/reference/doctest/index.rst b/src/doc/en/reference/doctest/index.rst index 3383429f681..a537ed878ec 100644 --- a/src/doc/en/reference/doctest/index.rst +++ b/src/doc/en/reference/doctest/index.rst @@ -11,5 +11,6 @@ Sage's Doctesting Framework sage/doctest/reporting sage/doctest/test sage/doctest/util + sage/doctest/fixtures .. include:: ../footer.txt diff --git a/src/sage/doctest/control.py b/src/sage/doctest/control.py index 30b8789330c..ed1dd5c8136 100644 --- a/src/sage/doctest/control.py +++ b/src/sage/doctest/control.py @@ -579,7 +579,7 @@ def expand_files_into_sources(self): sage: DC = DocTestController(DD, [dirname]) sage: DC.expand_files_into_sources() sage: len(DC.sources) - 9 + 10 sage: DC.sources[0].options.optional True @@ -679,6 +679,7 @@ def sort_sources(self): sage.doctest.reporting sage.doctest.parsing sage.doctest.forker + sage.doctest.fixtures sage.doctest.control sage.doctest.all sage.doctest diff --git a/src/sage/doctest/fixtures.py b/src/sage/doctest/fixtures.py index 992762605f7..354d5f29673 100644 --- a/src/sage/doctest/fixtures.py +++ b/src/sage/doctest/fixtures.py @@ -1,37 +1,80 @@ +r""" +Fixtures to help testing functionality + +Utilities which modify or replace code to help with doctesting functionality. +Wrappers, proxies and mockups are typical examples of fixtures. + +AUTHORS: + +- Martin von Gagern (2014-12-15): PropertyAccessTracerProxy and trace_method + +EXAMPLES: + +You can use :func:`trace_method` to see how a method +communicates with its surroundings:: + + sage: class Foo(object): + ....: def f(self): + ....: self.y = self.g(self.x) + ....: def g(self, arg): + ....: return arg + 1 + ....: + sage: foo = Foo() + sage: foo.x = 3 + sage: from sage.doctest.fixtures import trace_method + sage: trace_method(foo, "f") + sage: foo.f() + enter f() + read x = 3 + call g(3) -> 4 + write y = 4 + exit f -> None +""" + +#***************************************************************************** +# Copyright (C) 2014 Martin von Gagern +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** + from functools import wraps class PropertyAccessTracerProxy(object): - r""" - Proxy object which prints all property and method access to an object. - INPUT: + def __init__(self, delegate, prefix=" ", reads=True): + r""" + Proxy object which prints all property and method access to an object. - - ``delegate``: The actual object to be proxied. + INPUT: - - ``prefix``: String to prepend to each printed output. + - ``delegate``: The actual object to be proxied. - - ``reads``: Whether to trace read access as well + - ``prefix``: String to prepend to each printed output. - EXAMPLE:: + - ``reads``: Whether to trace read access as well - sage: class Foo(object): - ....: def f(self, *args): - ....: return self.x*self.x - ....: - sage: foo = Foo() - sage: from sage.doctest.fixtures import PropertyAccessTracerProxy - sage: pat = PropertyAccessTracerProxy(foo) - sage: pat.x = 2 - write x = 2 - sage: pat.x - read x = 2 - 2 - sage: pat.f(3) - call f(3) -> 4 - 4 - """ + EXAMPLE:: - def __init__(self, delegate, prefix=" ", reads=True): + sage: class Foo(object): + ....: def f(self, *args): + ....: return self.x*self.x + ....: + sage: foo = Foo() + sage: from sage.doctest.fixtures import PropertyAccessTracerProxy + sage: pat = PropertyAccessTracerProxy(foo) + sage: pat.x = 2 + write x = 2 + sage: pat.x + read x = 2 + 2 + sage: pat.f(3) + call f(3) -> 4 + 4 + """ object.__setattr__(self, "delegate", delegate) object.__setattr__(self, "prefix", prefix) object.__setattr__(self, "reads", reads) @@ -98,6 +141,26 @@ def __setattr__(self, name, val): @classmethod def fmt(cls, val): + r""" + Format a value to be printed. + + This can be used to introduce normalization, + such that the printed value does not depend on factors + outside the control of the doctest. + One example is the order of elements in a hash-based structure. + For most objects, this is simply the ``repr`` of the object. + + EXAMPLE:: + + sage: from sage.doctest.fixtures import PropertyAccessTracerProxy + sage: fmt = PropertyAccessTracerProxy.fmt + sage: print(fmt(set(["a", "c", "b", "d"]))) + set(['a', 'b', 'c', 'd']) + sage: print(fmt(frozenset(["a", "c", "b", "d"]))) + frozenset(['a', 'b', 'c', 'd']) + sage: print(fmt("foo\nbar")) + 'foo\nbar' + """ if isinstance(val, frozenset): return ("frozenset([{}])".format (", ".join(map(cls.fmt, sorted(val))))) @@ -108,7 +171,7 @@ def fmt(cls, val): return r -def traceMethod(obj, meth, **kwds): +def trace_method(obj, meth, **kwds): r""" Trace the doings of a given method. It prints method entry with arguments, @@ -131,8 +194,8 @@ def traceMethod(obj, meth, **kwds): ....: sage: foo = Foo() sage: foo.x = 3 - sage: from sage.doctest.fixtures import traceMethod - sage: traceMethod(foo, "f") + sage: from sage.doctest.fixtures import trace_method + sage: trace_method(foo, "f") sage: foo.f() enter f() read x = 3 From 2cbaa017f5ac21c48d49a4ae4885dfcfe064e415 Mon Sep 17 00:00:00 2001 From: Martin von Gagern Date: Fri, 2 Jan 2015 22:06:07 +0100 Subject: [PATCH 097/217] Split property access tracing into proxy and helper classes. This should make it a lot simpler to add new features, since the helper can use normal attribute access syntax, without the ugly and lengthy object.__getattribute__ required inside the proxy. --- src/sage/doctest/fixtures.py | 172 ++++++++++++++++++++++++++--------- 1 file changed, 130 insertions(+), 42 deletions(-) diff --git a/src/sage/doctest/fixtures.py b/src/sage/doctest/fixtures.py index 354d5f29673..d0713c16782 100644 --- a/src/sage/doctest/fixtures.py +++ b/src/sage/doctest/fixtures.py @@ -43,19 +43,23 @@ from functools import wraps -class PropertyAccessTracerProxy(object): +class PropertyAccessTracerHelper(object): def __init__(self, delegate, prefix=" ", reads=True): r""" - Proxy object which prints all property and method access to an object. + Helper to print proxied access to properties. + + This class does the actual printing of access traces. + The fact that it's not a proxy at the same time + helps avoiding complicated attribute access syntax. INPUT: - - ``delegate``: The actual object to be proxied. + - ``delegate``: The actual object to be proxied. - - ``prefix``: String to prepend to each printed output. + - ``prefix``: String to prepend to each printed output. - - ``reads``: Whether to trace read access as well + - ``reads``: Whether to trace read access as well. EXAMPLE:: @@ -64,22 +68,22 @@ def __init__(self, delegate, prefix=" ", reads=True): ....: return self.x*self.x ....: sage: foo = Foo() - sage: from sage.doctest.fixtures import PropertyAccessTracerProxy - sage: pat = PropertyAccessTracerProxy(foo) - sage: pat.x = 2 + sage: from sage.doctest.fixtures import PropertyAccessTracerHelper + sage: pat = PropertyAccessTracerHelper(foo) + sage: pat.set("x", 2) write x = 2 - sage: pat.x + sage: pat.get("x") read x = 2 2 - sage: pat.f(3) + sage: pat.get("f")(3) call f(3) -> 4 4 """ - object.__setattr__(self, "delegate", delegate) - object.__setattr__(self, "prefix", prefix) - object.__setattr__(self, "reads", reads) + self.delegate = delegate + self.prefix = prefix + self.reads = reads - def __getattribute__(self, name): + def get(self, name): r""" EXAMPLE:: @@ -89,36 +93,34 @@ def __getattribute__(self, name): ....: sage: foo = Foo() sage: foo.x = 2 - sage: from sage.doctest.fixtures import PropertyAccessTracerProxy - sage: pat = PropertyAccessTracerProxy(foo) - sage: pat.x + sage: from sage.doctest.fixtures import PropertyAccessTracerHelper + sage: pat = PropertyAccessTracerHelper(foo) + sage: pat.get("x") read x = 2 2 - sage: pat.f(3) + sage: pat.get("f")(3) call f(3) -> 4 4 """ - delegate = object.__getattribute__(self, "delegate") - prefix = object.__getattribute__(self, "prefix") - fmt = object.__getattribute__(self, "fmt") - val = getattr(delegate, name) - if callable(val) and name not in delegate.__dict__: + val = getattr(self.delegate, name) + if callable(val) and name not in self.delegate.__dict__: @wraps(val) def wrapper(*args, **kwds): - arglst = [fmt(arg) for arg in args] - arglst.extend("{}={}".format(k, fmt(v)) + arglst = [self.fmt(arg) for arg in args] + arglst.extend("{}={}".format(k, self.fmt(v)) for k, v in sorted(kwds.items())) res = val(*args, **kwds) print("{}call {}({}) -> {}" - .format(prefix, name, ", ".join(arglst), fmt(res))) + .format(self.prefix, name, ", ".join(arglst), + self.fmt(res))) return res return wrapper else: - if object.__getattribute__(self, "reads"): - print("{}read {} = {}".format(prefix, name, fmt(val))) + if self.reads: + print("{}read {} = {}".format(self.prefix, name, self.fmt(val))) return val - def __setattr__(self, name, val): + def set(self, name, val): r""" EXAMPLE:: @@ -126,18 +128,15 @@ def __setattr__(self, name, val): ....: pass ....: sage: foo = Foo() - sage: from sage.doctest.fixtures import PropertyAccessTracerProxy - sage: pat = PropertyAccessTracerProxy(foo) - sage: pat.x = 2 + sage: from sage.doctest.fixtures import PropertyAccessTracerHelper + sage: pat = PropertyAccessTracerHelper(foo) + sage: pat.set("x", 2) write x = 2 sage: foo.x 2 """ - delegate = object.__getattribute__(self, "delegate") - prefix = object.__getattribute__(self, "prefix") - fmt = object.__getattribute__(self, "fmt") - print("{}write {} = {}".format(prefix, name, fmt(val))) - setattr(delegate, name, val) + print("{}write {} = {}".format(self.prefix, name, self.fmt(val))) + setattr(self.delegate, name, val) @classmethod def fmt(cls, val): @@ -152,8 +151,8 @@ def fmt(cls, val): EXAMPLE:: - sage: from sage.doctest.fixtures import PropertyAccessTracerProxy - sage: fmt = PropertyAccessTracerProxy.fmt + sage: from sage.doctest.fixtures import PropertyAccessTracerHelper + sage: fmt = PropertyAccessTracerHelper.fmt sage: print(fmt(set(["a", "c", "b", "d"]))) set(['a', 'b', 'c', 'd']) sage: print(fmt(frozenset(["a", "c", "b", "d"]))) @@ -171,6 +170,89 @@ def fmt(cls, val): return r +class PropertyAccessTracerProxy(object): + + def __init__(self, delegate, **kwds): + r""" + Proxy object which prints all property and method access to an object. + + The implementation is kept lean since all access to properties of + the proxy itself requires complicated syntax. + For this reason, the actual handling of property access + is delegated to a :class:`PropertyAccessTracerHelper`. + + INPUT: + + - ``delegate``: The actual object to be proxied. + + - ``prefix``: String to prepend to each printed output. + (Default: ``" "``) + + - ``reads``: Whether to trace read access as well. + (Default: ``True) + + EXAMPLE:: + + sage: class Foo(object): + ....: def f(self, *args): + ....: return self.x*self.x + ....: + sage: foo = Foo() + sage: from sage.doctest.fixtures import PropertyAccessTracerProxy + sage: pat = PropertyAccessTracerProxy(foo) + sage: pat.x = 2 + write x = 2 + sage: pat.x + read x = 2 + 2 + sage: pat.f(3) + call f(3) -> 4 + 4 + """ + helper = PropertyAccessTracerHelper(delegate, **kwds) + object.__setattr__(self, "helper", helper) + + def __getattribute__(self, name): + r""" + EXAMPLE:: + + sage: class Foo(object): + ....: def f(self, *args): + ....: return self.x*self.x + ....: + sage: foo = Foo() + sage: foo.x = 2 + sage: from sage.doctest.fixtures import PropertyAccessTracerProxy + sage: pat = PropertyAccessTracerProxy(foo) + sage: pat.x + read x = 2 + 2 + sage: pat.f(3) + call f(3) -> 4 + 4 + """ + helper = object.__getattribute__(self, "helper") + return helper.get(name) + + def __setattr__(self, name, val): + r""" + EXAMPLE:: + + sage: class Foo(object): + ....: pass + ....: + sage: foo = Foo() + sage: from sage.doctest.fixtures import PropertyAccessTracerProxy + sage: pat = PropertyAccessTracerProxy(foo) + sage: pat.x = 2 + write x = 2 + sage: foo.x + 2 + """ + helper = object.__getattribute__(self, "helper") + return helper.set(name, val) + + def trace_method(obj, meth, **kwds): r""" Trace the doings of a given method. @@ -180,9 +262,15 @@ def trace_method(obj, meth, **kwds): INPUT: - - ``obj``: The object containing the method. + - ``obj``: The object containing the method. + + - ``meth``: The name of the method to be traced. + + - ``prefix``: String to prepend to each printed output. + (Default: ``" "``) - - ``meth``: The name of the method to be traced. + - ``reads``: Whether to trace read access as well. + (Default: ``True) EXAMPLE:: @@ -205,7 +293,7 @@ def trace_method(obj, meth, **kwds): """ f = getattr(obj, meth).__func__ t = PropertyAccessTracerProxy(obj, **kwds) - fmt = PropertyAccessTracerProxy.fmt + fmt = PropertyAccessTracerHelper.fmt @wraps(f) def g(*args, **kwds): arglst = [fmt(arg) for arg in args] From f6334543439b49ded6ef7486b881d21817078e85 Mon Sep 17 00:00:00 2001 From: Martin von Gagern Date: Fri, 2 Jan 2015 22:28:01 +0100 Subject: [PATCH 098/217] Improve documentation style. These improvements are based on cues from the dev manual on coding basics. --- src/sage/doctest/fixtures.py | 61 +++++++++++++++++++++++++++--------- 1 file changed, 46 insertions(+), 15 deletions(-) diff --git a/src/sage/doctest/fixtures.py b/src/sage/doctest/fixtures.py index d0713c16782..c1c7a52fa4b 100644 --- a/src/sage/doctest/fixtures.py +++ b/src/sage/doctest/fixtures.py @@ -49,17 +49,20 @@ def __init__(self, delegate, prefix=" ", reads=True): r""" Helper to print proxied access to properties. - This class does the actual printing of access traces. + This class does the actual printing of access traces + for objects proxied by :class:`PropertyAccessTracerProxy`. The fact that it's not a proxy at the same time helps avoiding complicated attribute access syntax. INPUT: - - ``delegate``: The actual object to be proxied. + - ``delegate`` -- the actual object to be proxied. - - ``prefix``: String to prepend to each printed output. + - ``prefix`` -- (default: ``" "``) + string to prepend to each printed output. - - ``reads``: Whether to trace read access as well. + - ``reads`` -- (default: ``True``) + whether to trace read access as well. EXAMPLE:: @@ -85,6 +88,14 @@ def __init__(self, delegate, prefix=" ", reads=True): def get(self, name): r""" + Read an attribute from the wrapped delegate object. + + If that value is a method (i.e. a callable object which is not + contained in the dictionary of the object itself but instead + inherited from some class) then it is replaced by a wrapper + function to report arguments and return value. + Otherwise a property read access is reported. + EXAMPLE:: sage: class Foo(object): @@ -122,6 +133,10 @@ def wrapper(*args, **kwds): def set(self, name, val): r""" + Write an attribute to the wrapped delegate object. + + The name and new value are also reported in the output. + EXAMPLE:: sage: class Foo(object): @@ -183,13 +198,13 @@ def __init__(self, delegate, **kwds): INPUT: - - ``delegate``: The actual object to be proxied. + - ``delegate`` -- the actual object to be proxied. - - ``prefix``: String to prepend to each printed output. - (Default: ``" "``) + - ``prefix`` -- (default: ``" "``) + string to prepend to each printed output. - - ``reads``: Whether to trace read access as well. - (Default: ``True) + - ``reads`` -- (default: ``True``) + whether to trace read access as well. EXAMPLE:: @@ -208,12 +223,23 @@ def __init__(self, delegate, **kwds): sage: pat.f(3) call f(3) -> 4 4 + + .. automethod:: __getattribute__ + .. automethod:: __setattr__ """ helper = PropertyAccessTracerHelper(delegate, **kwds) object.__setattr__(self, "helper", helper) def __getattribute__(self, name): r""" + Read an attribute from the wrapped delegate object. + + If that value is a method (i.e. a callable object which is not + contained in the dictionary of the object itself but instead + inherited from some class) then it is replaced by a wrapper + function to report arguments and return value. + Otherwise a property read access is reported. + EXAMPLE:: sage: class Foo(object): @@ -236,6 +262,10 @@ def __getattribute__(self, name): def __setattr__(self, name, val): r""" + Write an attribute to the wrapped delegate object. + + The name and new value are also reported in the output. + EXAMPLE:: sage: class Foo(object): @@ -262,15 +292,16 @@ def trace_method(obj, meth, **kwds): INPUT: - - ``obj``: The object containing the method. + - ``obj`` -- the object containing the method. - - ``meth``: The name of the method to be traced. + - ``meth`` -- the name of the method to be traced. - - ``prefix``: String to prepend to each printed output. - (Default: ``" "``) + - ``prefix`` -- (default: ``" "``) + string to prepend to each printed output. - - ``reads``: Whether to trace read access as well. - (Default: ``True) + - ``reads`` -- (default: ``True``) + whether to trace read access as well. + EXAMPLE:: From 8afc55386e90160f14b917abfde096de9ee6e196 Mon Sep 17 00:00:00 2001 From: Martin von Gagern Date: Fri, 2 Jan 2015 22:40:42 +0100 Subject: [PATCH 099/217] Turn reproducible_repr from a class method to a standalone function. --- src/sage/doctest/fixtures.py | 93 +++++++++++++++++++++--------------- 1 file changed, 54 insertions(+), 39 deletions(-) diff --git a/src/sage/doctest/fixtures.py b/src/sage/doctest/fixtures.py index c1c7a52fa4b..8865129ac4e 100644 --- a/src/sage/doctest/fixtures.py +++ b/src/sage/doctest/fixtures.py @@ -7,6 +7,7 @@ AUTHORS: - Martin von Gagern (2014-12-15): PropertyAccessTracerProxy and trace_method +- Martin von Gagern (2015-01-02): Factor out TracerHelper and reproducible_repr EXAMPLES: @@ -43,6 +44,50 @@ from functools import wraps + +def reproducible_repr(val): + r""" + String representation of an object in a reproducible way. + + This tries to ensure that the returned string does not depend on + factors outside the control of the doctest. + One example is the order of elements in a hash-based structure. + For most objects, this is simply the ``repr`` of the object. + + All types which require special handling are covered by the + examples below. If a doctest requires special handling for + additional types, this function may be extended apropriately. + + INPUT: + + - ``val`` -- an object to be represented + + OUTPUT: + + A string representation of that object, similar to what ``repr`` + returns but for certain cases with more guarantees to ensure + exactly the same result for semantically equivalent objects. + + EXAMPLE:: + + sage: from sage.doctest.fixtures import reproducible_repr + sage: print(reproducible_repr(set(["a", "c", "b", "d"]))) + set(['a', 'b', 'c', 'd']) + sage: print(reproducible_repr(frozenset(["a", "c", "b", "d"]))) + frozenset(['a', 'b', 'c', 'd']) + sage: print(reproducible_repr("foo\nbar")) # demonstrate default case + 'foo\nbar' + """ + if isinstance(val, frozenset): + return ("frozenset([{}])".format + (", ".join(map(reproducible_repr, sorted(val))))) + if isinstance(val, set): + return ("set([{}])".format + (", ".join(map(reproducible_repr, sorted(val))))) + r = repr(val) + return r + + class PropertyAccessTracerHelper(object): def __init__(self, delegate, prefix=" ", reads=True): @@ -117,18 +162,19 @@ def get(self, name): if callable(val) and name not in self.delegate.__dict__: @wraps(val) def wrapper(*args, **kwds): - arglst = [self.fmt(arg) for arg in args] - arglst.extend("{}={}".format(k, self.fmt(v)) + arglst = [reproducible_repr(arg) for arg in args] + arglst.extend("{}={}".format(k, reproducible_repr(v)) for k, v in sorted(kwds.items())) res = val(*args, **kwds) print("{}call {}({}) -> {}" .format(self.prefix, name, ", ".join(arglst), - self.fmt(res))) + reproducible_repr(res))) return res return wrapper else: if self.reads: - print("{}read {} = {}".format(self.prefix, name, self.fmt(val))) + print("{}read {} = {}".format(self.prefix, name, + reproducible_repr(val))) return val def set(self, name, val): @@ -150,40 +196,10 @@ def set(self, name, val): sage: foo.x 2 """ - print("{}write {} = {}".format(self.prefix, name, self.fmt(val))) + print("{}write {} = {}".format(self.prefix, name, + reproducible_repr(val))) setattr(self.delegate, name, val) - @classmethod - def fmt(cls, val): - r""" - Format a value to be printed. - - This can be used to introduce normalization, - such that the printed value does not depend on factors - outside the control of the doctest. - One example is the order of elements in a hash-based structure. - For most objects, this is simply the ``repr`` of the object. - - EXAMPLE:: - - sage: from sage.doctest.fixtures import PropertyAccessTracerHelper - sage: fmt = PropertyAccessTracerHelper.fmt - sage: print(fmt(set(["a", "c", "b", "d"]))) - set(['a', 'b', 'c', 'd']) - sage: print(fmt(frozenset(["a", "c", "b", "d"]))) - frozenset(['a', 'b', 'c', 'd']) - sage: print(fmt("foo\nbar")) - 'foo\nbar' - """ - if isinstance(val, frozenset): - return ("frozenset([{}])".format - (", ".join(map(cls.fmt, sorted(val))))) - if isinstance(val, set): - return ("set([{}])".format - (", ".join(map(cls.fmt, sorted(val))))) - r = repr(val) - return r - class PropertyAccessTracerProxy(object): @@ -324,14 +340,13 @@ def trace_method(obj, meth, **kwds): """ f = getattr(obj, meth).__func__ t = PropertyAccessTracerProxy(obj, **kwds) - fmt = PropertyAccessTracerHelper.fmt @wraps(f) def g(*args, **kwds): arglst = [fmt(arg) for arg in args] - arglst.extend("{}={}".format(k, fmt(v)) + arglst.extend("{}={}".format(k, reproducible_repr(v)) for k, v in sorted(kwds.items())) print("enter {}({})".format(meth, ", ".join(arglst))) res = f(t, *args, **kwds) - print("exit {} -> {}".format(meth, fmt(res))) + print("exit {} -> {}".format(meth, reproducible_repr(res))) return res setattr(obj, meth, g) From b89fb603fce4e71a535b5bbf4bc052a70b33999d Mon Sep 17 00:00:00 2001 From: Martin von Gagern Date: Fri, 2 Jan 2015 22:43:59 +0100 Subject: [PATCH 100/217] Use term attribute instead of property. The python world tends to speak of attributes of objects, not properties. Staying with that terminology might avoid some confusion. --- src/sage/doctest/fixtures.py | 50 ++++++++++++++++++------------------ 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/src/sage/doctest/fixtures.py b/src/sage/doctest/fixtures.py index 8865129ac4e..c86539889c7 100644 --- a/src/sage/doctest/fixtures.py +++ b/src/sage/doctest/fixtures.py @@ -6,7 +6,7 @@ AUTHORS: -- Martin von Gagern (2014-12-15): PropertyAccessTracerProxy and trace_method +- Martin von Gagern (2014-12-15): AttributeAccessTracerProxy and trace_method - Martin von Gagern (2015-01-02): Factor out TracerHelper and reproducible_repr EXAMPLES: @@ -88,14 +88,14 @@ def reproducible_repr(val): return r -class PropertyAccessTracerHelper(object): +class AttributeAccessTracerHelper(object): def __init__(self, delegate, prefix=" ", reads=True): r""" - Helper to print proxied access to properties. + Helper to print proxied access to attributes. This class does the actual printing of access traces - for objects proxied by :class:`PropertyAccessTracerProxy`. + for objects proxied by :class:`AttributeAccessTracerProxy`. The fact that it's not a proxy at the same time helps avoiding complicated attribute access syntax. @@ -116,8 +116,8 @@ def __init__(self, delegate, prefix=" ", reads=True): ....: return self.x*self.x ....: sage: foo = Foo() - sage: from sage.doctest.fixtures import PropertyAccessTracerHelper - sage: pat = PropertyAccessTracerHelper(foo) + sage: from sage.doctest.fixtures import AttributeAccessTracerHelper + sage: pat = AttributeAccessTracerHelper(foo) sage: pat.set("x", 2) write x = 2 sage: pat.get("x") @@ -139,7 +139,7 @@ def get(self, name): contained in the dictionary of the object itself but instead inherited from some class) then it is replaced by a wrapper function to report arguments and return value. - Otherwise a property read access is reported. + Otherwise a attribute read access is reported. EXAMPLE:: @@ -149,8 +149,8 @@ def get(self, name): ....: sage: foo = Foo() sage: foo.x = 2 - sage: from sage.doctest.fixtures import PropertyAccessTracerHelper - sage: pat = PropertyAccessTracerHelper(foo) + sage: from sage.doctest.fixtures import AttributeAccessTracerHelper + sage: pat = AttributeAccessTracerHelper(foo) sage: pat.get("x") read x = 2 2 @@ -189,8 +189,8 @@ def set(self, name, val): ....: pass ....: sage: foo = Foo() - sage: from sage.doctest.fixtures import PropertyAccessTracerHelper - sage: pat = PropertyAccessTracerHelper(foo) + sage: from sage.doctest.fixtures import AttributeAccessTracerHelper + sage: pat = AttributeAccessTracerHelper(foo) sage: pat.set("x", 2) write x = 2 sage: foo.x @@ -201,16 +201,16 @@ def set(self, name, val): setattr(self.delegate, name, val) -class PropertyAccessTracerProxy(object): +class AttributeAccessTracerProxy(object): def __init__(self, delegate, **kwds): r""" - Proxy object which prints all property and method access to an object. + Proxy object which prints all attribute and method access to an object. - The implementation is kept lean since all access to properties of + The implementation is kept lean since all access to attributes of the proxy itself requires complicated syntax. - For this reason, the actual handling of property access - is delegated to a :class:`PropertyAccessTracerHelper`. + For this reason, the actual handling of attribute access + is delegated to a :class:`AttributeAccessTracerHelper`. INPUT: @@ -229,8 +229,8 @@ def __init__(self, delegate, **kwds): ....: return self.x*self.x ....: sage: foo = Foo() - sage: from sage.doctest.fixtures import PropertyAccessTracerProxy - sage: pat = PropertyAccessTracerProxy(foo) + sage: from sage.doctest.fixtures import AttributeAccessTracerProxy + sage: pat = AttributeAccessTracerProxy(foo) sage: pat.x = 2 write x = 2 sage: pat.x @@ -243,7 +243,7 @@ def __init__(self, delegate, **kwds): .. automethod:: __getattribute__ .. automethod:: __setattr__ """ - helper = PropertyAccessTracerHelper(delegate, **kwds) + helper = AttributeAccessTracerHelper(delegate, **kwds) object.__setattr__(self, "helper", helper) def __getattribute__(self, name): @@ -254,7 +254,7 @@ def __getattribute__(self, name): contained in the dictionary of the object itself but instead inherited from some class) then it is replaced by a wrapper function to report arguments and return value. - Otherwise a property read access is reported. + Otherwise a attribute read access is reported. EXAMPLE:: @@ -264,8 +264,8 @@ def __getattribute__(self, name): ....: sage: foo = Foo() sage: foo.x = 2 - sage: from sage.doctest.fixtures import PropertyAccessTracerProxy - sage: pat = PropertyAccessTracerProxy(foo) + sage: from sage.doctest.fixtures import AttributeAccessTracerProxy + sage: pat = AttributeAccessTracerProxy(foo) sage: pat.x read x = 2 2 @@ -288,8 +288,8 @@ def __setattr__(self, name, val): ....: pass ....: sage: foo = Foo() - sage: from sage.doctest.fixtures import PropertyAccessTracerProxy - sage: pat = PropertyAccessTracerProxy(foo) + sage: from sage.doctest.fixtures import AttributeAccessTracerProxy + sage: pat = AttributeAccessTracerProxy(foo) sage: pat.x = 2 write x = 2 sage: foo.x @@ -339,7 +339,7 @@ def trace_method(obj, meth, **kwds): exit f -> None """ f = getattr(obj, meth).__func__ - t = PropertyAccessTracerProxy(obj, **kwds) + t = AttributeAccessTracerProxy(obj, **kwds) @wraps(f) def g(*args, **kwds): arglst = [fmt(arg) for arg in args] From cb1388758f0cf2872c6dbd9f548fd13a18aa4502 Mon Sep 17 00:00:00 2001 From: Martin von Gagern Date: Fri, 2 Jan 2015 22:56:54 +0100 Subject: [PATCH 101/217] Fix trivial spelling mistake. Note to self: verify result of search & replace in the future. --- src/sage/doctest/fixtures.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/doctest/fixtures.py b/src/sage/doctest/fixtures.py index c86539889c7..9057a82462e 100644 --- a/src/sage/doctest/fixtures.py +++ b/src/sage/doctest/fixtures.py @@ -139,7 +139,7 @@ def get(self, name): contained in the dictionary of the object itself but instead inherited from some class) then it is replaced by a wrapper function to report arguments and return value. - Otherwise a attribute read access is reported. + Otherwise an attribute read access is reported. EXAMPLE:: @@ -254,7 +254,7 @@ def __getattribute__(self, name): contained in the dictionary of the object itself but instead inherited from some class) then it is replaced by a wrapper function to report arguments and return value. - Otherwise a attribute read access is reported. + Otherwise an attribute read access is reported. EXAMPLE:: From df0fb69e1aa9ab205e35b98ad6d97d0c67b889af Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Mon, 29 Dec 2014 09:19:24 +0100 Subject: [PATCH 102/217] Declare types of _entries --- src/sage/modules/free_module_element.pxd | 4 +- src/sage/modules/free_module_element.pyx | 211 +++++++++++++---------- 2 files changed, 122 insertions(+), 93 deletions(-) diff --git a/src/sage/modules/free_module_element.pxd b/src/sage/modules/free_module_element.pxd index 1f3a63cc1da..345d55901ec 100644 --- a/src/sage/modules/free_module_element.pxd +++ b/src/sage/modules/free_module_element.pxd @@ -7,7 +7,7 @@ cdef class FreeModuleElement(Vector): cdef class FreeModuleElement_generic_dense(FreeModuleElement): # data - cdef object _entries + cdef list _entries # cdef'd methods cdef _new_c(self, object v) @@ -15,7 +15,7 @@ cdef class FreeModuleElement_generic_dense(FreeModuleElement): cdef class FreeModuleElement_generic_sparse(FreeModuleElement): # data - cdef object _entries + cdef dict _entries # cdef'd methods cdef _new_c(self, object v) diff --git a/src/sage/modules/free_module_element.pyx b/src/sage/modules/free_module_element.pyx index db178aece4c..26b314a45af 100644 --- a/src/sage/modules/free_module_element.pyx +++ b/src/sage/modules/free_module_element.pyx @@ -98,15 +98,17 @@ TESTS:: (168, 194, 110, 116, 102) """ +#***************************************************************************** +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** + import math -import operator -include 'sage/ext/cdefs.pxi' include 'sage/ext/stdsage.pxi' -from cpython.dict cimport * -from cpython.list cimport * -import sage.misc.misc as misc -import sage.misc.latex from sage.structure.sequence import Sequence @@ -122,9 +124,6 @@ from sage.rings.real_double import RDF from sage.rings.complex_double import CDF from sage.misc.derivative import multi_derivative - -# We use some cimports for very quick checking of Integer and Ring -# type to optimize vector(ring,n) for creating the zero vector. from sage.rings.ring cimport Ring from sage.rings.integer cimport Integer @@ -3127,7 +3126,7 @@ cdef class FreeModuleElement(element_Vector): # abstract base class sage: latex(w) \left[1.0,\,2.0,\,3.0\right\rangle """ - latex = sage.misc.latex.latex + from sage.misc.latex import latex vector_delimiters = latex.vector_delimiters() s = '\\left' + vector_delimiters[0] s += ',\,'.join([latex(a) for a in self.list()]) @@ -3556,18 +3555,6 @@ cdef class FreeModuleElement_generic_dense(FreeModuleElement): cdef bint is_sparse_c(self): return 0 - def _hash(self): - """ - Return hash of an immutable form of self (works even if self - is mutable). - - sage: v = vector([-1,0,3,pi]) - sage: type(v) - - sage: v._hash() # random output - """ - return hash(tuple(list(self))) - def __copy__(self): """ Return a copy of this generic dense vector. @@ -3595,12 +3582,48 @@ cdef class FreeModuleElement_generic_dense(FreeModuleElement): """ EXAMPLES:: - sage: type(vector([-1,0,3,pi])) # indirect doctest - + sage: type(vector(RR, [-1,0,2/3,pi,oo])) + + + We can initialize with lists, tuples and derived types:: + + sage: from sage.modules.free_module_element import FreeModuleElement_generic_dense + sage: FreeModuleElement_generic_dense(RR^5, [-1,0,2/3,pi,oo]) + (-1.00000000000000, 0.000000000000000, 0.666666666666667, 3.14159265358979, +infinity) + sage: FreeModuleElement_generic_dense(RR^5, (-1,0,2/3,pi,oo)) + (-1.00000000000000, 0.000000000000000, 0.666666666666667, 3.14159265358979, +infinity) + sage: FreeModuleElement_generic_dense(RR^5, Sequence([-1,0,2/3,pi,oo])) + (-1.00000000000000, 0.000000000000000, 0.666666666666667, 3.14159265358979, +infinity) + sage: FreeModuleElement_generic_dense(RR^0, 0) + () TESTS: - Check that #11751 is fixed:: + Disabling coercion can lead to illegal objects:: + + sage: FreeModuleElement_generic_dense(RR^5, [-1,0,2/3,pi,oo], coerce=False) + (-1, 0, 2/3, pi, +Infinity) + + We test the ``copy`` flag:: + + sage: from sage.modules.free_module_element import FreeModuleElement_generic_dense + sage: L = [RR(x) for x in (-1,0,2/3,pi,oo)] + sage: FreeModuleElement_generic_dense(RR^5, tuple(L), coerce=False, copy=False) + (-1.00000000000000, 0.000000000000000, 0.666666666666667, 3.14159265358979, +infinity) + sage: v = FreeModuleElement_generic_dense(RR^5, L, coerce=False, copy=False) + sage: L[4] = 42.0 + sage: v # last entry changed since we didn't copy + (-1.00000000000000, 0.000000000000000, 0.666666666666667, 3.14159265358979, 42.0000000000000) + + :: + + sage: L = [RR(x) for x in (-1,0,2/3,pi,oo)] + sage: v = FreeModuleElement_generic_dense(RR^5, L, coerce=False, copy=True) + sage: L[4] = 42.0 + sage: v # last entry did not change + (-1.00000000000000, 0.000000000000000, 0.666666666666667, 3.14159265358979, +infinity) + + Check that :trac:`11751` is fixed:: sage: K. = QQ[] sage: M = K^1 @@ -3626,26 +3649,25 @@ cdef class FreeModuleElement_generic_dense(FreeModuleElement): Univariate Polynomial Ring in x over Rational Field """ FreeModuleElement.__init__(self, parent) - R = self.parent().base_ring() - if entries == 0: - entries = [R(0)]*self.degree() + R = self.base_ring() + if not entries: + entries = [R.zero_element()]*self._degree else: - if not isinstance(entries, (list, tuple)): - raise TypeError("entries (=%s) must be a list"%(entries, )) + if type(entries) is not list: + if not isinstance(entries, (list, tuple)): + raise TypeError("entries must be a list or tuple, not %s" % type(entries)) + copy = True # ensure we have a true Python list - if len(entries) != self.degree(): - raise TypeError("entries must be a list of length %s"%\ - self.degree()) + if len(entries) != self._degree: + raise TypeError("entries must be a list of length %s" % self.degree()) if coerce: - if len(entries) != 0: - coefficient_ring = parent.basis()[0][0].parent() - try: - entries = [coefficient_ring(x) for x in entries] - except TypeError: - raise TypeError("Unable to coerce entries (=%s) to coefficients in %s"%(entries, coefficient_ring)) + coefficient_ring = parent.basis()[0][0].parent() + try: + entries = [coefficient_ring(x) for x in entries] + except TypeError: + raise TypeError("Unable to coerce entries (=%s) to coefficients in %s"%(entries, coefficient_ring)) elif copy: - # Make a copy - entries = list(entries) + entries = list(entries) # make a copy/convert to list self._entries = entries cpdef ModuleElement _add_(left, ModuleElement right): @@ -3659,7 +3681,7 @@ cdef class FreeModuleElement_generic_dense(FreeModuleElement): (1/3, pi^2 + 2/3, pi + 1) """ cdef Py_ssize_t i, n - n = PyList_Size(left._entries) + n = len(left._entries) v = [None]*n for i from 0 <= i < n: v[i] = (left._entries[i])._add_( @@ -3680,7 +3702,7 @@ cdef class FreeModuleElement_generic_dense(FreeModuleElement): (1, -1, 0, 0, 0) """ cdef Py_ssize_t i, n - n = PyList_Size(left._entries) + n = len(left._entries) v = [None]*n for i from 0 <= i < n: v[i] = (left._entries[i])._sub_( @@ -3749,7 +3771,7 @@ cdef class FreeModuleElement_generic_dense(FreeModuleElement): right = left.parent().ambient_module()(right) # Component wise vector * vector multiplication. cdef Py_ssize_t i, n - n = PyList_Size(left._entries) + n = len(left._entries) v = [None]*n for i from 0 <= i < n: v[i] = (left._entries[i])._mul_((right)._entries[i]) @@ -4040,6 +4062,20 @@ cdef class FreeModuleElement_generic_sparse(FreeModuleElement): sage: v.is_sparse() True + We can initialize with dicts, lists, tuples and derived types:: + + sage: from sage.modules.free_module_element import FreeModuleElement_generic_sparse + sage: FreeModuleElement_generic_sparse(RR^5, {0:-1, 2:2/3, 3:pi, 4:oo}) + (-1.00000000000000, 0.000000000000000, 0.666666666666667, 3.14159265358979, +infinity) + sage: FreeModuleElement_generic_sparse(RR^5, [-1,0,2/3,pi,oo]) + (-1.00000000000000, 0.000000000000000, 0.666666666666667, 3.14159265358979, +infinity) + sage: FreeModuleElement_generic_sparse(RR^5, (-1,0,2/3,pi,oo)) + (-1.00000000000000, 0.000000000000000, 0.666666666666667, 3.14159265358979, +infinity) + sage: FreeModuleElement_generic_sparse(RR^5, Sequence([-1,0,2/3,pi,oo])) + (-1.00000000000000, 0.000000000000000, 0.666666666666667, 3.14159265358979, +infinity) + sage: FreeModuleElement_generic_sparse(RR^0, 0) + () + TESTS: Test that :trac:`11751` is fixed:: @@ -4077,31 +4113,30 @@ cdef class FreeModuleElement_generic_sparse(FreeModuleElement): # 0 <= i < degree. FreeModuleElement.__init__(self, parent) R = self.base_ring() - if entries == 0: + cdef Py_ssize_t i + if not entries: entries = {} else: - if isinstance(entries, list): - if len(entries) != self.degree(): - raise TypeError("entries has the wrong length") - x = entries - entries = {} - for i in xrange(self.degree()): - if x[i]: - entries[i] = x[i] - copy = False - if not isinstance(entries, dict): - raise TypeError, "entries must be a dict" - if copy: - # Make a copy - entries = dict(entries) + if type(entries) is not dict: + if isinstance(entries, (list, tuple)): + if len(entries) != self._degree: + raise TypeError("entries has the wrong length") + x = entries + entries = {} + for i in range(self._degree): + if x[i]: + entries[i] = x[i] + else: + raise TypeError("entries must be a dict, list or tuple, not %s", type(entries)) + elif copy: + entries = dict(entries) # make a copy if coerce: - if len(entries) != 0: - coefficient_ring = parent.basis()[0][0].parent() - try: - for k, x in entries.iteritems(): - entries[k] = coefficient_ring(x) - except TypeError: - raise TypeError("Unable to coerce value (=%s) of entries dict (=%s) to %s"%(x, entries, coefficient_ring)) + coefficient_ring = parent.basis()[0][0].parent() + try: + for k, x in entries.iteritems(): + entries[k] = coefficient_ring(x) + except TypeError: + raise TypeError("Unable to coerce value (=%s) of entries dict (=%s) to %s"%(x, entries, coefficient_ring)) self._entries = entries cpdef ModuleElement _add_(left, ModuleElement right): @@ -4114,18 +4149,17 @@ cdef class FreeModuleElement_generic_sparse(FreeModuleElement): sage: v._add_(v) (2, 4/3, 2*pi) """ - cdef object v, e - e = dict((right)._entries) + cdef dict v = dict((right)._entries) for i, a in left._entries.iteritems(): - if i in e: - sum = (a)._add_( e[i]) + if i in v: + sum = (a)._add_( v[i]) if sum: - e[i] = sum + v[i] = sum else: - del e[i] + del v[i] elif a: - e[i] = a - return left._new_c(e) + v[i] = a + return left._new_c(v) cpdef ModuleElement _sub_(left, ModuleElement right): """ @@ -4135,18 +4169,17 @@ cdef class FreeModuleElement_generic_sparse(FreeModuleElement): sage: v._sub_(v) (0, 0, 0) """ - cdef object v, e - e = dict(left._entries) # dict to make a copy + cdef dict v = dict(left._entries) # dict to make a copy for i, a in (right)._entries.iteritems(): - if i in e: - diff = ( e[i])._sub_(a) + if i in v: + diff = ( v[i])._sub_(a) if diff: - e[i] = diff + v[i] = diff else: - del e[i] + del v[i] elif a: - e[i] = -a - return left._new_c(e) + v[i] = -a + return left._new_c(v) cpdef ModuleElement _lmul_(self, RingElement right): """ @@ -4156,8 +4189,7 @@ cdef class FreeModuleElement_generic_sparse(FreeModuleElement): sage: v._lmul_(SR(3)) (3, 2, 3*pi) """ - cdef object v - v = PyDict_New() + cdef dict v = {} if right: for i, a in self._entries.iteritems(): prod = (a)._mul_(right) @@ -4173,8 +4205,7 @@ cdef class FreeModuleElement_generic_sparse(FreeModuleElement): sage: v._rmul_(SR(3)) (3, 2, 3*pi) """ - cdef object v - v = PyDict_New() + cdef dict v = {} if left: for i, a in self._entries.iteritems(): prod = left._mul_(a) @@ -4194,8 +4225,7 @@ cdef class FreeModuleElement_generic_sparse(FreeModuleElement): sage: w * v 10 """ - cdef object v, e, z - e = dict((right)._entries) + cdef dict e = (right)._entries z = left.base_ring()(0) for i, a in left._entries.iteritems(): if i in e: @@ -4211,9 +4241,8 @@ cdef class FreeModuleElement_generic_sparse(FreeModuleElement): (-2/3, 2/3*pi^2, pi) """ # Component wise vector * vector multiplication. - cdef object v, e - e = dict((right)._entries) - v = PyDict_New() + cdef dict e = (right)._entries + cdef dict v = {} for i, a in left._entries.iteritems(): if i in e: prod = (a)._mul_( e[i]) From 106f5d41398794b13a6a683236cf219d49602312 Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Sat, 3 Jan 2015 11:04:51 +0100 Subject: [PATCH 103/217] Minor improvements in FreeModuleElement_generic_sparse.__init__ --- src/sage/modules/free_module_element.pyx | 53 +++++++++++++++++------- 1 file changed, 38 insertions(+), 15 deletions(-) diff --git a/src/sage/modules/free_module_element.pyx b/src/sage/modules/free_module_element.pyx index 26b314a45af..90ee47227a1 100644 --- a/src/sage/modules/free_module_element.pyx +++ b/src/sage/modules/free_module_element.pyx @@ -4065,16 +4065,23 @@ cdef class FreeModuleElement_generic_sparse(FreeModuleElement): We can initialize with dicts, lists, tuples and derived types:: sage: from sage.modules.free_module_element import FreeModuleElement_generic_sparse - sage: FreeModuleElement_generic_sparse(RR^5, {0:-1, 2:2/3, 3:pi, 4:oo}) + sage: def S(R,n): + ....: return FreeModule(R, n, sparse=True) + sage: FreeModuleElement_generic_sparse(S(RR,5), {0:-1, 2:2/3, 3:pi, 4:oo}) (-1.00000000000000, 0.000000000000000, 0.666666666666667, 3.14159265358979, +infinity) - sage: FreeModuleElement_generic_sparse(RR^5, [-1,0,2/3,pi,oo]) + sage: FreeModuleElement_generic_sparse(S(RR,5), [-1,0,2/3,pi,oo]) (-1.00000000000000, 0.000000000000000, 0.666666666666667, 3.14159265358979, +infinity) - sage: FreeModuleElement_generic_sparse(RR^5, (-1,0,2/3,pi,oo)) + sage: FreeModuleElement_generic_sparse(S(RR,5), (-1,0,2/3,pi,oo)) (-1.00000000000000, 0.000000000000000, 0.666666666666667, 3.14159265358979, +infinity) - sage: FreeModuleElement_generic_sparse(RR^5, Sequence([-1,0,2/3,pi,oo])) + sage: FreeModuleElement_generic_sparse(S(RR,5), Sequence([-1,0,2/3,pi,oo])) (-1.00000000000000, 0.000000000000000, 0.666666666666667, 3.14159265358979, +infinity) - sage: FreeModuleElement_generic_sparse(RR^0, 0) + sage: FreeModuleElement_generic_sparse(S(RR,0), 0) () + sage: from collections import defaultdict + sage: D = defaultdict(RR) + sage: D[0] = -1 + sage: FreeModuleElement_generic_sparse(S(RR,5), D) + (-1.00000000000000, 0.000000000000000, 0.000000000000000, 0.000000000000000, 0.000000000000000) TESTS: @@ -4108,9 +4115,16 @@ cdef class FreeModuleElement_generic_sparse(FreeModuleElement): sage: v = vector([RIF(-1, 1)], sparse=True) sage: v.is_zero() False + + We correctly initialize values which become 0 only after coercion:: + + sage: v = FreeModuleElement_generic_sparse(S(GF(3),6), [1,2,3,4,5,6]) + sage: v.nonzero_positions() + [0, 1, 3, 4] """ - #WARNING: In creation, we do not check that the i pairs satisfy - # 0 <= i < degree. + #WARNING: In creation, we do not check that the indices i satisfy + # 0 <= i < degree + # or even that the indices are integers. FreeModuleElement.__init__(self, parent) R = self.base_ring() cdef Py_ssize_t i @@ -4118,25 +4132,34 @@ cdef class FreeModuleElement_generic_sparse(FreeModuleElement): entries = {} else: if type(entries) is not dict: - if isinstance(entries, (list, tuple)): + if isinstance(entries, dict): + # Convert derived type to dict + copy = True + elif isinstance(entries, (list, tuple)): if len(entries) != self._degree: raise TypeError("entries has the wrong length") - x = entries + e = entries entries = {} for i in range(self._degree): - if x[i]: - entries[i] = x[i] + x = e[i] + if x: + entries[i] = x + copy = False else: raise TypeError("entries must be a dict, list or tuple, not %s", type(entries)) - elif copy: - entries = dict(entries) # make a copy if coerce: coefficient_ring = parent.basis()[0][0].parent() + e = entries + entries = {} try: - for k, x in entries.iteritems(): - entries[k] = coefficient_ring(x) + for k, x in e.iteritems(): + x = coefficient_ring(x) + if x: + entries[k] = x except TypeError: raise TypeError("Unable to coerce value (=%s) of entries dict (=%s) to %s"%(x, entries, coefficient_ring)) + elif copy: + entries = dict(entries) # make a copy/convert to dict self._entries = entries cpdef ModuleElement _add_(left, ModuleElement right): From f347d4ccaba191be973f241a0a6bf7f1f0a86fa5 Mon Sep 17 00:00:00 2001 From: Martin von Gagern Date: Mon, 15 Dec 2014 22:59:26 +0100 Subject: [PATCH 104/217] Doctests for each method of APngAssembler using tracing fixture. While I personally believe that there is not much to be gained from doctesting every tiny internal method, this lack of doctests is currently preventing a positive review, so here they are. I implemented doctests in the way that seemed the most reasonable to me: show the role of each method in a realistic run of the code, using a tracing fixture to exhibit the interaction of a given method with its caller (who passes its arguments and may receive some return value), the object state (via read and write access to attributes) and other methods (which are called during the execution of a given method). --- src/sage/plot/animate.py | 415 ++++++++++++++++++++++++++++++--------- 1 file changed, 321 insertions(+), 94 deletions(-) diff --git a/src/sage/plot/animate.py b/src/sage/plot/animate.py index 7ea432a0266..cfec5cf2116 100644 --- a/src/sage/plot/animate.py +++ b/src/sage/plot/animate.py @@ -957,7 +957,7 @@ def save(self, filename=None, show_path=False, use_ffmpeg=False): class APngAssembler(object): - """ + r""" Builds an APNG_ (Animated PNG) from a sequence of PNG files. This is used by the :meth:`sage.plot.animate.Animation.apng` method. @@ -1007,7 +1007,7 @@ class APngAssembler(object): def __init__(self, out, num_frames, num_plays=0, delay=200, delay_denominator=100): - """ + r""" Initialize for creation of an APNG file. """ self._last_seqno = -1 @@ -1040,54 +1040,15 @@ def add_frame(self, pngfile, delay=None, delay_denominator=None): sage: from sage.plot.animate import APngAssembler sage: from StringIO import StringIO - sage: def h2b(h): - ....: b = [] - ....: while h: - ....: if h[0] in ' \n': # ignore whitespace - ....: h = h[1:] - ....: elif h[0] in '0123456789abcdef': # hex byte - ....: b.append(int(h[:2], 16)) - ....: h = h[2:] - ....: elif h[0] == '.': # for chunk type - ....: b.extend(ord(h[i]) for i in range(1,5)) - ....: h = h[5:] - ....: else: # for PNG magic - ....: b.append(ord(h[0])) - ....: h = h[1:] - ....: return ''.join(map(chr,b)) - ....: - sage: fn = tmp_filename(ext='.png') sage: buf = StringIO() sage: apng = APngAssembler(buf, 2) - sage: with open(fn, 'wb') as f: f.write(h2b('89 PNG 0d0a1a0a' - ....: '0000000d.IHDR 00000003000000020800000000 b81f39c6' - ....: '00000004.gAMA 000186a0 31e8965f' - ....: '00000007.tIME 07de061b0b2624 1f307ad5' - ....: '00000008.IDAT 696d673164617461 ce8a4999' - ....: '00000000.IEND ae426082')) + sage: fn = APngAssembler._testData("input1", True) sage: apng.add_frame(fn, delay=0x567, delay_denominator=0x1234) - sage: with open(fn, 'wb') as f: f.write(h2b('89 PNG 0d0a1a0a' - ....: '0000000d.IHDR 00000003000000020800000000 b81f39c6' - ....: '00000004.gAMA 000186a0 31e8965f' - ....: '00000004.IDAT 696d6732 0e69ab1d' - ....: '00000004.IDAT 64617461 6694cb78' - ....: '00000000.IEND ae426082')) + sage: fn = APngAssembler._testData("input2", True) sage: apng.add_frame(fn) sage: len(buf.getvalue()) 217 - sage: expected = h2b('89 PNG 0d0a1a0a' - ....: '0000000d.IHDR 00000003000000020800000000 b81f39c6' - ....: '00000004.gAMA 000186a0 31e8965f' - ....: '00000008.acTL 0000000200000000 f38d9370' - ....: '0000001a.fcTL 000000000000000300000002' - ....: ' 0000000000000000056712340100 b4f729c9' - ....: '00000008.IDAT 696d673164617461 ce8a4999' - ....: '0000001a.fcTL 000000010000000300000002' - ....: ' 000000000000000000c800640100 1b92eb4d' - ....: '00000008.fdAT 00000002696d6732 9cfb89a3' - ....: '00000008.fdAT 0000000364617461 c966c076' - ....: '00000000.IEND ae426082') - sage: buf.getvalue() == expected + sage: buf.getvalue() == APngAssembler._testData("anim12", False) True sage: apng.add_frame(fn) Traceback (most recent call last): @@ -1129,58 +1090,21 @@ def set_default(self, pngfile): sage: from sage.plot.animate import APngAssembler sage: from StringIO import StringIO - sage: def h2b(h): - ....: b = [] - ....: while h: - ....: if h[0] in ' \n': # ignore whitespace - ....: h = h[1:] - ....: elif h[0] in '0123456789abcdef': # hex byte - ....: b.append(int(h[:2], 16)) - ....: h = h[2:] - ....: elif h[0] == '.': # for chunk type - ....: b.extend(ord(h[i]) for i in range(1,5)) - ....: h = h[5:] - ....: else: # for PNG magic - ....: b.append(ord(h[0])) - ....: h = h[1:] - ....: return ''.join(map(chr,b)) - ....: - sage: fn = tmp_filename(ext='.png') sage: buf = StringIO() sage: apng = APngAssembler(buf, 1) - sage: with open(fn, 'wb') as f: f.write(h2b('89 PNG 0d0a1a0a' - ....: '0000000d.IHDR 00000003000000020800000000 b81f39c6' - ....: '00000004.gAMA 000186a0 31e8965f' - ....: '00000007.tIME 07de061b0b2624 1f307ad5' - ....: '00000008.IDAT 696d673164617461 ce8a4999' - ....: '00000000.IEND ae426082')) + sage: fn = APngAssembler._testData("input1", True) sage: apng.set_default(fn) - sage: with open(fn, 'wb') as f: f.write(h2b('89 PNG 0d0a1a0a' - ....: '0000000d.IHDR 00000003000000020800000000 b81f39c6' - ....: '00000004.gAMA 000186a0 31e8965f' - ....: '00000004.IDAT 696d6732 0e69ab1d' - ....: '00000004.IDAT 64617461 6694cb78' - ....: '00000000.IEND ae426082')) + sage: fn = APngAssembler._testData("input2", True) sage: apng.add_frame(fn, delay=0x567, delay_denominator=0x1234) sage: len(buf.getvalue()) 179 - sage: expected = h2b('89 PNG 0d0a1a0a' - ....: '0000000d.IHDR 00000003000000020800000000 b81f39c6' - ....: '00000004.gAMA 000186a0 31e8965f' - ....: '00000008.acTL 0000000100000000 b42de9a0' - ....: '00000008.IDAT 696d673164617461 ce8a4999' - ....: '0000001a.fcTL 000000000000000300000002' - ....: ' 0000000000000000056712340100 b4f729c9' - ....: '00000008.fdAT 00000001696d6732 db5bf373' - ....: '00000008.fdAT 0000000264617461 f406e9c6' - ....: '00000000.IEND ae426082') - sage: buf.getvalue() == expected + sage: buf.getvalue() == APngAssembler._testData("still1anim2", False) True sage: apng.add_frame(fn) Traceback (most recent call last): ... RuntimeError: Already reached the declared number of frames - + """ if self._idx != 0: raise RuntimeError("Default image must precede all animation frames") @@ -1189,7 +1113,36 @@ def set_default(self, pngfile): self._add_png(pngfile) def _add_png(self, pngfile): - """Add data from one PNG still image.""" + r""" + Add data from one PNG still image. + + TESTS:: + + sage: from sage.plot.animate import APngAssembler + sage: APngAssembler._testCase1("_add_png", reads=False) + enter _add_png('...png') + write _current_chunk = ('\x00\x00\x00\r', 'IHDR', '\x00\x00\x00\x03\x00\x00\x00\x02\x08\x00\x00\x00\x00', '\xb8\x1f9\xc6') + call _copy() -> None + call _first_IHDR('\x00\x00\x00\x03\x00\x00\x00\x02\x08\x00\x00\x00\x00') -> None + write _current_chunk = ('\x00\x00\x00\x04', 'gAMA', '\x00\x01\x86\xa0', '1\xe8\x96_') + call _copy() -> None + write _current_chunk = ('\x00\x00\x00\x07', 'tIME', '\x07\xde\x06\x1b\x0b&$', '\x1f0z\xd5') + write _current_chunk = ('\x00\x00\x00\x08', 'IDAT', 'img1data', '\xce\x8aI\x99') + call _first_IDAT('img1data') -> None + write _current_chunk = ('\x00\x00\x00\x00', 'IEND', '', '\xaeB`\x82') + write _first = False + exit _add_png -> None + enter _add_png('...png') + write _current_chunk = ('\x00\x00\x00\r', 'IHDR', '\x00\x00\x00\x03\x00\x00\x00\x02\x08\x00\x00\x00\x00', '\xb8\x1f9\xc6') + write _current_chunk = ('\x00\x00\x00\x04', 'gAMA', '\x00\x01\x86\xa0', '1\xe8\x96_') + write _current_chunk = ('\x00\x00\x00\x04', 'IDAT', 'img2', '\x0ei\xab\x1d') + call _next_IDAT('img2') -> None + write _current_chunk = ('\x00\x00\x00\x04', 'IDAT', 'data', 'f\x94\xcbx') + call _next_IDAT('data') -> None + write _current_chunk = ('\x00\x00\x00\x00', 'IEND', '', '\xaeB`\x82') + write _first = False + exit _add_png -> None + """ with open(pngfile, 'rb') as png: if png.read(8) != self.magic: raise ValueError("{} is not a PNG file".format(pngfile)) @@ -1220,24 +1173,79 @@ def _add_png(self, pngfile): self._first = False def _seqno(self): - """Generate next sequence number.""" + r""" + Generate next sequence number. + + TESTS:: + + sage: from sage.plot.animate import APngAssembler + sage: from StringIO import StringIO + sage: buf = StringIO() + sage: apng = APngAssembler(buf, 1) + sage: apng._seqno() + '\x00\x00\x00\x00' + sage: apng._seqno() + '\x00\x00\x00\x01' + sage: apng._seqno() + '\x00\x00\x00\x02' + """ self._last_seqno += 1 return struct.pack(">L", self._last_seqno) def _first_IHDR(self, data): - """Remember image size.""" + r""" + Remember image size. + + TESTS:: + + sage: from sage.plot.animate import APngAssembler + sage: APngAssembler._testCase1("_first_IHDR") + enter _first_IHDR('\x00\x00\x00\x03\x00\x00\x00\x02\x08\x00\x00\x00\x00') + write width = 3 + write height = 2 + exit _first_IHDR -> None + """ w, h, d, ctype, comp, filt, ilace = struct.unpack(">2L5B", data) self.width = w self.height = h def _first_IDAT(self, data): - """Write acTL and fcTL, then copy as IDAT.""" + r""" + Write acTL and fcTL, then copy as IDAT. + + TESTS:: + + sage: from sage.plot.animate import APngAssembler + sage: APngAssembler._testCase1("_first_IDAT") + enter _first_IDAT('img1data') + call _actl() -> None + call _fctl() -> None + call _copy() -> None + exit _first_IDAT -> None + """ self._actl() self._fctl() self._copy() def _next_IDAT(self, data): - """write fcTL, then convert to fdAT.""" + r""" + Write fcTL, then convert to fdAT. + + TESTS:: + + sage: from sage.plot.animate import APngAssembler + sage: APngAssembler._testCase1("_next_IDAT") + enter _next_IDAT('img2') + call _fctl() -> None + call _seqno() -> '\x00\x00\x00\x02' + call _chunk('fdAT', '\x00\x00\x00\x02img2') -> None + exit _next_IDAT -> None + enter _next_IDAT('data') + call _fctl() -> None + call _seqno() -> '\x00\x00\x00\x03' + call _chunk('fdAT', '\x00\x00\x00\x03data') -> None + exit _next_IDAT -> None + """ self._fctl() maxlen = 0x7ffffffb while len(data) > maxlen: @@ -1246,12 +1254,46 @@ def _next_IDAT(self, data): self._chunk(b"fdAT", self._seqno() + data) def _copy(self): - """Copy an existing chunk without modification.""" + r""" + Copy an existing chunk without modification. + + TESTS:: + + sage: from sage.plot.animate import APngAssembler + sage: APngAssembler._testCase1("_copy") + enter _copy() + read _current_chunk = ('\x00\x00\x00\r', 'IHDR', '\x00\x00\x00\x03\x00\x00\x00\x02\x08\x00\x00\x00\x00', '\xb8\x1f9\xc6') + read out = None + enter _copy() + read _current_chunk = ('\x00\x00\x00\x04', 'gAMA', '\x00\x01\x86\xa0', '1\xe8\x96_') + ... + read _current_chunk = ('\x00\x00\x00\x08', 'IDAT', 'img1data', '\xce\x8aI\x99') + ... + exit _copy -> None + """ for d in self._current_chunk: self.out.write(d) def _actl(self): - """Write animation control data (acTL).""" + r""" + Write animation control data (acTL). + + TESTS:: + + sage: from sage.plot.animate import APngAssembler + sage: APngAssembler._testCase1("_actl") + enter _actl() + read _actl_written = False + read num_frames = 2 + read num_plays = 0 + call _chunk('acTL', '\x00\x00\x00\x02\x00\x00\x00\x00') -> None + write _actl_written = True + exit _actl -> None + """ if self._actl_written: return data = struct.pack(">2L", self.num_frames, self.num_plays) @@ -1259,7 +1301,37 @@ def _actl(self): self._actl_written = True def _fctl(self): - """Write frame control data (fcTL).""" + r""" + Write frame control data (fcTL). + + TESTS:: + + sage: from sage.plot.animate import APngAssembler + sage: APngAssembler._testCase1("_fctl") + enter _fctl() + read _fctl_written = False + read width = 3 + read height = 2 + read delay_numerator = 1383 + read delay_denominator = 4660 + call _seqno() -> '\x00\x00\x00\x00' + call _chunk('fcTL', '\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x05g\x124\x01\x00') -> None + write _fctl_written = True + exit _fctl -> None + enter _fctl() + read _fctl_written = False + read width = 3 + read height = 2 + read delay_numerator = 200 + read delay_denominator = 100 + call _seqno() -> '\x00\x00\x00\x01' + call _chunk('fcTL', '\x00\x00\x00\x01\x00\x00\x00\x03\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\xc8\x00d\x01\x00') -> None + write _fctl_written = True + exit _fctl -> None + enter _fctl() + read _fctl_written = True + exit _fctl -> None + """ if self._fctl_written: return data = struct.pack( @@ -1271,8 +1343,163 @@ def _fctl(self): self._fctl_written = True def _chunk(self, ctype, cdata): - """Write a new (or modified) chunk of data""" + r""" + Write a new (or modified) chunk of data + + TESTS:: + + sage: from sage.plot.animate import APngAssembler + sage: from StringIO import StringIO + sage: buf = StringIO() + sage: apng = APngAssembler(buf, 1) + sage: buf.getvalue() + '\x89PNG\r\n\x1a\n' + sage: apng._chunk("abcd", "efgh") + sage: buf.getvalue() + '\x89PNG\r\n\x1a\n\x00\x00\x00\x04abcdefgh\xae\xef*P' + """ ccrc = struct.pack(">L", zlib.crc32(ctype + cdata) & 0xffffffff) clen = struct.pack(">L", len(cdata)) for d in [clen, ctype, cdata, ccrc]: self.out.write(d) + + @classmethod + def _hex2bin(cls, h): + r""" + Convert hex data to binary. + + This is a helper method used for testing. + Most data is given as lower-case hex digits, + possibly intermixed with whitespace. + A dot causes the next four bytes to be copied verbatim + even if they look like hex digits. This is used for chunk types. + Other characters which are not hex digits are passed verbatim. + + EXAMPLE:: + + sage: from sage.plot.animate import APngAssembler + sage: h2b = APngAssembler._hex2bin + sage: h2b("0123") == b"\x01\x23" + True + sage: h2b(" 01 \n 23 ") == b"\x01\x23" + True + sage: h2b(".abcdef") == b"abcd\xef" + True + sage: h2b("PNG") == b"PNG" + True + """ + b = [] + while h: + if h[0] in ' \n': # ignore whitespace + h = h[1:] + elif h[0] in '0123456789abcdef': # hex byte + b.append(int(h[:2], 16)) + h = h[2:] + elif h[0] == '.': # for chunk type + b.extend(ord(h[i]) for i in range(1,5)) + h = h[5:] + else: # for PNG magic + b.append(ord(h[0])) + h = h[1:] + return ''.join(map(chr,b)) + + @classmethod + def _testData(cls, name, asFile): + r""" + Retrieve data for test cases. + + INPUT: + + - ``name``: The name of the file content. + + - ``asFile``: Whether to return a binary string of the named data + or the path of a file containing that data. + + EXAMPLE:: + + sage: from sage.plot.animate import APngAssembler + sage: APngAssembler._testData("input1", False) + '\x89PNG\r\n\x1a\n\x00...' + sage: APngAssembler._testData("input2", True) + '...png' + """ + data = { + + # Input 1: one PNG image, except the data makes no real sense + "input1": """89 PNG 0d0a1a0a + 0000000d.IHDR 00000003000000020800000000 b81f39c6 + 00000004.gAMA 000186a0 31e8965f + 00000007.tIME 07de061b0b2624 1f307ad5 + 00000008.IDAT 696d673164617461 ce8a4999 + 00000000.IEND ae426082""", + + # Input 2: slightly different, data in two chunks + "input2": """89 PNG 0d0a1a0a + 0000000d.IHDR 00000003000000020800000000 b81f39c6 + 00000004.gAMA 000186a0 31e8965f + 00000004.IDAT 696d6732 0e69ab1d + 00000004.IDAT 64617461 6694cb78 + 00000000.IEND ae426082""", + + # Expected output 1: both images as frames of an animation + "anim12": """89 PNG 0d0a1a0a + 0000000d.IHDR 00000003000000020800000000 b81f39c6 + 00000004.gAMA 000186a0 31e8965f + 00000008.acTL 0000000200000000 f38d9370 + 0000001a.fcTL 000000000000000300000002 + 0000000000000000056712340100 b4f729c9 + 00000008.IDAT 696d673164617461 ce8a4999 + 0000001a.fcTL 000000010000000300000002 + 000000000000000000c800640100 1b92eb4d + 00000008.fdAT 00000002696d6732 9cfb89a3 + 00000008.fdAT 0000000364617461 c966c076 + 00000000.IEND ae426082""", + + # Expected output 2: first image as fallback, second as animation + "still1anim2": """89 PNG 0d0a1a0a + 0000000d.IHDR 00000003000000020800000000 b81f39c6 + 00000004.gAMA 000186a0 31e8965f + 00000008.acTL 0000000100000000 b42de9a0 + 00000008.IDAT 696d673164617461 ce8a4999 + 0000001a.fcTL 000000000000000300000002 + 0000000000000000056712340100 b4f729c9 + 00000008.fdAT 00000001696d6732 db5bf373 + 00000008.fdAT 0000000264617461 f406e9c6 + 00000000.IEND ae426082""", + + } + d = cls._hex2bin(data[name]) + if asFile: + from sage.misc.temporary_file import tmp_filename + fn = tmp_filename(ext=".png") + with open(fn, 'wb') as f: + f.write(d) + return fn + return d + + @classmethod + def _testCase1(cls, methodToTrace=None, **kwds): + r""" + Run common test case. + + This test case is one animation of two frames. + The named method (if not None) will be traced during execution. + This will demonstrate the role of each method in the doctests. + + TESTS:: + + sage: from sage.plot.animate import APngAssembler + sage: APngAssembler._testCase1() + """ + from sage.doctest.fixtures import trace_method + from StringIO import StringIO + buf = StringIO() + apng = cls(buf, 2) + if methodToTrace is not None: + trace_method(apng, methodToTrace, **kwds) + apng.add_frame(cls._testData("input1", True), + delay=0x567, delay_denominator=0x1234) + apng.add_frame(cls._testData("input2", True)) + out = buf.getvalue() + assert len(out) == 217 + assert out == cls._testData("anim12", False) From 7d2531d65b2a86a1506dab9c141c65aca6f3c751 Mon Sep 17 00:00:00 2001 From: Martin von Gagern Date: Sat, 3 Jan 2015 18:11:58 +0100 Subject: [PATCH 105/217] Details on my APNG contribution in module documentation. --- src/sage/plot/animate.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/plot/animate.py b/src/sage/plot/animate.py index cfec5cf2116..75db15e1ef4 100644 --- a/src/sage/plot/animate.py +++ b/src/sage/plot/animate.py @@ -94,7 +94,7 @@ - William Stein - John Palmieri - Niles Johnson (2013-12): Expand to animate more graphics objects -- Martin von Gagern +- Martin von Gagern (2014-12): Added APNG support .. REFERENCES (not rendered as a section, but linked inline): From 1235bad6ae59f20d6ecb160c2126a9c6b134d7bb Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Sun, 4 Jan 2015 11:52:56 +0530 Subject: [PATCH 106/217] trac #17581: Resolvable BIBD --- src/doc/en/reference/combinat/module_list.rst | 1 + src/sage/combinat/designs/__init__.py | 1 + src/sage/combinat/designs/bibd.py | 13 +- src/sage/combinat/designs/database.py | 6 +- src/sage/combinat/designs/design_catalog.py | 3 + .../orthogonal_arrays_find_recursive.pyx | 1 - src/sage/combinat/designs/resolvable_bibd.py | 401 ++++++++++++++++++ 7 files changed, 418 insertions(+), 8 deletions(-) create mode 100644 src/sage/combinat/designs/resolvable_bibd.py diff --git a/src/doc/en/reference/combinat/module_list.rst b/src/doc/en/reference/combinat/module_list.rst index e84dc84733e..826406e19db 100644 --- a/src/doc/en/reference/combinat/module_list.rst +++ b/src/doc/en/reference/combinat/module_list.rst @@ -75,6 +75,7 @@ Comprehensive Module list sage/combinat/designs/__init__ sage/combinat/designs/all sage/combinat/designs/bibd + sage/combinat/designs/resolvable_bibd sage/combinat/designs/block_design sage/combinat/designs/covering_design sage/combinat/designs/database diff --git a/src/sage/combinat/designs/__init__.py b/src/sage/combinat/designs/__init__.py index 9d965629391..54f963b59b9 100644 --- a/src/sage/combinat/designs/__init__.py +++ b/src/sage/combinat/designs/__init__.py @@ -16,6 +16,7 @@ - :ref:`sage.combinat.designs.block_design` - :ref:`sage.combinat.designs.bibd` +- :ref:`sage.combinat.designs.resolvable_bibd` - :ref:`sage.combinat.designs.latin_squares` - :ref:`sage.combinat.designs.orthogonal_arrays` - :ref:`sage.combinat.designs.orthogonal_arrays_build_recursive` diff --git a/src/sage/combinat/designs/bibd.py b/src/sage/combinat/designs/bibd.py index 914c054a0db..7293e832f8b 100644 --- a/src/sage/combinat/designs/bibd.py +++ b/src/sage/combinat/designs/bibd.py @@ -1,5 +1,5 @@ r""" -Balanced Incomplete Block Designs (BIBD) +(BIBD) Balanced Incomplete Block Designs This module gathers everything related to Balanced Incomplete Block Designs. One can build a BIBD (or check that it can be built) with :func:`balanced_incomplete_block_design`:: @@ -158,6 +158,8 @@ def balanced_incomplete_block_design(v, k, existence=False, use_LJCR=False): False """ lmbd = 1 + + # Trivial BIBD if v == 1: if existence: return True @@ -168,12 +170,15 @@ def balanced_incomplete_block_design(v, k, existence=False, use_LJCR=False): return True return BalancedIncompleteBlockDesign(v, [range(v)], check=False, copy=False) + # Non-existence of BIBD if (v < k or k < 2 or (v-1) % (k-1) != 0 or (v*(v-1)) % (k*(k-1)) != 0 or - # non-existence results from the Handbook of combinatorial designs. With - # lambda>1 other exceptions are (15,5,2),(21,6,2),(22,7,2),(22,8,4) + # From the Handbook of combinatorial designs: + # + # With lambda>1 other exceptions are + # (15,5,2),(21,6,2),(22,7,2),(22,8,4). (k==6 and v in [36,46]) or (k==7 and v == 43) or # Fisher's inequality @@ -186,7 +191,7 @@ def balanced_incomplete_block_design(v, k, existence=False, use_LJCR=False): if existence: return True from itertools import combinations - return BalancedIncompleteBlockDesign(v, combinations(range(v),2), check=False, copy=False) + return BalancedIncompleteBlockDesign(v, combinations(range(v),2), check=False, copy=True) if k == 3: if existence: return v%6 == 1 or v%6 == 3 diff --git a/src/sage/combinat/designs/database.py b/src/sage/combinat/designs/database.py index 17c6bdf519c..ab28fe893a9 100644 --- a/src/sage/combinat/designs/database.py +++ b/src/sage/combinat/designs/database.py @@ -1722,14 +1722,14 @@ def OA_520_plus_x(x): Only a row `[p,p,...]` is missing from the `OA(10+x,520+x)` This construction is used in :func:`OA(10,520) `, - :func:`OA(12,522) `, and :func:`OA(14,524) `. + :func:`OA(12,522) `, and :func:`OA(14,524) `. EXAMPLE:: sage: from sage.combinat.designs.designs_pyx import is_orthogonal_array sage: from sage.combinat.designs.database import OA_520_plus_x - sage: OA = OA_520_plus_x(0) # not tested (already tested in OA_10_520 - sage: print is_orthogonal_array(OA,10,520,2) # not tested (already tested in OA_10_520 + sage: OA = OA_520_plus_x(0) # not tested (already tested in OA_10_520) + sage: print is_orthogonal_array(OA,10,520,2) # not tested (already tested in OA_10_520) True """ diff --git a/src/sage/combinat/designs/design_catalog.py b/src/sage/combinat/designs/design_catalog.py index 88638d2f5f2..a3d33edd7a3 100644 --- a/src/sage/combinat/designs/design_catalog.py +++ b/src/sage/combinat/designs/design_catalog.py @@ -40,6 +40,8 @@ :meth:`~sage.combinat.designs.block_design.ProjectiveGeometryDesign` :meth:`~sage.combinat.designs.block_design.DesarguesianProjectivePlaneDesign` :meth:`~sage.combinat.designs.bibd.balanced_incomplete_block_design` + :meth:`~sage.combinat.designs.resolvable_bibd.resolvable_balanced_incomplete_block_design` + :meth:`~sage.combinat.designs.resolvable_bibd.kirkman_triple_system` :meth:`~sage.combinat.designs.block_design.AffineGeometryDesign` :meth:`~sage.combinat.designs.block_design.WittDesign` :meth:`~sage.combinat.designs.block_design.HadamardDesign` @@ -91,6 +93,7 @@ from sage.combinat.designs.incidence_structures import IncidenceStructure BlockDesign = IncidenceStructure # just an alias from sage.combinat.designs.bibd import balanced_incomplete_block_design, steiner_triple_system +from sage.combinat.designs.resolvable_bibd import resolvable_balanced_incomplete_block_design, kirkman_triple_system # deprecated in june 2014 (#16446) from sage.misc.superseded import deprecated_function_alias, deprecated_callable_import diff --git a/src/sage/combinat/designs/orthogonal_arrays_find_recursive.pyx b/src/sage/combinat/designs/orthogonal_arrays_find_recursive.pyx index e85e0d02e41..2d8102f4011 100644 --- a/src/sage/combinat/designs/orthogonal_arrays_find_recursive.pyx +++ b/src/sage/combinat/designs/orthogonal_arrays_find_recursive.pyx @@ -67,7 +67,6 @@ def find_recursive_construction(k, n): - :func:`~sage.combinat.designs.orthogonal_arrays_build_recursive.thwart_lemma_4_1` - :func:`~sage.combinat.designs.orthogonal_arrays_build_recursive.three_factor_product` - :func:`~sage.combinat.designs.orthogonal_arrays_build_recursive.brouwer_separable_design` - - :func:`~sage.combinat.designs.orthogonal_arrays_build_recursive.find_brouwer_van_rees_with_one_truncated_column` INPUT: diff --git a/src/sage/combinat/designs/resolvable_bibd.py b/src/sage/combinat/designs/resolvable_bibd.py new file mode 100644 index 00000000000..6e484ef76a2 --- /dev/null +++ b/src/sage/combinat/designs/resolvable_bibd.py @@ -0,0 +1,401 @@ +r""" +(RBIBD) Resolvable Balanced Incomplete Block Design + +This module contains everything related to resolvable Balanced Incomplete Block +Designs. The constructions implemented here can be obtained through the +``designs.`` object:: + + designs.resolvable_balanced_incomplete_block_design(15,3) + +A BIBD is said to be *resolvable* if its blocks can be partitionned into +parallel classes, i.e. partitions of its ground set. + +The main function of this module is +:func:`resolvable_balanced_incomplete_block_design`, which calls all others. + +.. csv-table:: + :class: contentstable + :widths: 30, 70 + :delim: | + + :func:`resolvable_balanced_incomplete_block_design` | Return a resolvable BIBD of parameters `v,k`. + :func:`kirkman_triple_system` | Return a Kirkman Triple System on `v` points. + :func:`v_4_1_rbibd` | Return a `(v,4,1)`-RBIBD + +References: + +.. [Stinson91] D.R. Stinson, + A survey of Kirkman triple systems and related designs, + Volume 92, Issues 1-3, 17 November 1991, Pages 371-393, + Discrete Mathematics, + http://dx.doi.org/10.1016/0012-365X(91)90294-C. + +.. [RCW71] D. K. Ray-Chaudhuri, R. M. Wilson, + Solution of Kirkman's schoolgirl problem, + Volume 19, Pages 187-203, + Proceedings of Symposia in Pure Mathematics + +.. [BJL99] T. Beth, D. Jungnickel, H. Lenz, + Design Theory 2ed. + Cambridge University Press + 1999 + +Functions +--------- +""" +from sage.rings.arith import is_prime_power +from sage.combinat.designs.bibd import BalancedIncompleteBlockDesign +from sage.categories.sets_cat import EmptySetError + +def resolvable_balanced_incomplete_block_design(v,k,existence=False): + r""" + Return a resolvable BIBD of parameters `v,k`. + + A BIBD is said to be *resolvable* if its blocks can be partitionned into + parallel classes, i.e. partitions of the ground set. + + INPUT: + + - ``v,k`` (integers) + + - ``existence`` (boolean) -- instead of building the design, return: + + - ``True`` -- meaning that Sage knows how to build the design + + - ``Unknown`` -- meaning that Sage does not know how to build the + design, but that the design may exist (see :mod:`sage.misc.unknown`). + + - ``False`` -- meaning that the design does not exist. + + .. SEEALSO:: + + - :meth:`IncidenceStructure.is_resolvable` + - :func:`~sage.combinat.designs.bibd.balanced_incomplete_block_design` + + EXAMPLE:: + + sage: KTS15 = designs.resolvable_balanced_incomplete_block_design(15,3); KTS15 + (15,3,1)-Balanced Incomplete Block Design + sage: KTS15.is_resolvable() + True + """ + # Trivial cases + if v==1 or k==v: + return balanced_incomplete_block_design(v,k,existence=existence) + + # Non-existence of resolvable BIBD + if (v < k or + k < 2 or + v%k != 0 or + (v-1) % (k-1) != 0 or + (v*(v-1)) % (k*(k-1)) != 0 or + # From the Handbook of combinatorial designs: + # + # With lambda>1 the other exceptions is + # (15,5,2) + (k==6 and v == 36) or + # Fisher's inequality + (v*(v-1))/(k*(k-1)) < v): + if existence: + return False + raise EmptySetError("There exists no ({},{},{})-RBIBD".format(v,k,1)) + + if k==2: + if existence: + return True + classes = [[[(c+i)%(v-1),(c+v-i)%(v-1)] for i in range(1,v//2)] + for c in range(v-1)] + for i,classs in enumerate(classes): + classs.append([v-1,c]) + + B = BalancedIncompleteBlockDesign(v, + classes, + check=True, + copy=False) + B._classes = classes + return B + elif k==3: + return kirkman_triple_system(v,existence=existence) + elif k==4: + return v_4_1_rbibd(v,existence=existence) + else: + if existence: + return Unknown + raise NotImplementedError("I don't know how to build a ({},{},1)-RBIBD!".format(v,3)) + +def kirkman_triple_system(v,existence=False): + r""" + Return a Kirkman Triple System on `v` points. + + A Kirkman Triple System `KTS(v)` is a resolvable Steiner Triple System. It + exists if and only if `v\equiv 3\pmod{6}`. + + .. NOTE:: + + In its current state, this function is **not** able to build a `KTS(v)` + for all `v` such that `v\equiv 3\pmod{6}`. It implements Theorems 5 and + 6 from [RCW71]_, i.e. constructions 1.1 and 1.2 from [Stinson91]_. + + INPUT: + + - `n` (integer) + + - ``existence`` (boolean; ``False`` by default) -- whether to build the + `KTS(n)` or only answer whether it exists. + + .. SEEALSO:: + + :meth:`IncidenceStructure.is_resolvable` + + EXAMPLES: + + A solution to Kirkmman's original problem:: + + sage: kts = designs.kirkman_triple_system(15) + sage: names = map(str,[0,1,2,3,4,5,6,7,8,9,'a','b','c','d','e']) + sage: classes = kts.is_resolvable(1)[1] + sage: for i,classs in enumerate(classes): + ....: print "Day",i+1 + ....: for row in classs: + ....: print " ",names[row[0]]+names[row[1]]+names[row[2]] + ....: print "" + Day 1 + 07e + 139 + 26b + 458 + acd + + Day 2 + 18e + 24a + 03c + 569 + 7bd + + Day 3 + 29e + 35b + 14d + 06a + 78c + + Day 4 + 3ae + 46c + 257 + 01b + 89d + + Day 5 + 4be + 05d + 368 + 12c + 79a + + Day 6 + 5ce + 167 + 049 + 23d + 8ab + + Day 7 + 6de + 028 + 15a + 347 + 9bc + + + TESTS: + + Construction 1.1 from [Stinson91]_:: + + sage: for q in prime_powers(2,40): + ....: if q%6 == 1: + ....: _ = designs.kirkman_triple_system(2*q+1) + + Construction 1.2 from [Stinson91]_:: + + sage: for q in prime_powers(2,40): + ....: if q%6 == 1: + ....: _ = designs.kirkman_triple_system(3*q) + """ + if v%6 != 3: + if existence: + return False + raise ValueError("There is no KTS({}) as v!=3 mod(6)".format(v)) + + elif v == 3: + if existence: + return True + return BalancedIncompleteBlockDesign(3,[[0,1,2]],k=3,lambd=1) + + elif v == 9: + if existence: + return True + classes = [[[0, 1, 5], [2, 6, 7], [3, 4, 8]], + [[1, 6, 8], [3, 5, 7], [0, 2, 4]], + [[1, 4, 7], [0, 3, 6], [2, 5, 8]], + [[4, 5, 6], [0, 7, 8], [1, 2, 3]]] + KTS = BalancedIncompleteBlockDesign(v,[tr for cl in classes for tr in cl],k=3,lambd=1,copy=False) + KTS._classes = classes + return KTS + + # Construction 1.1 from [Stinson91] (originally Theorem 6 from [RCW71]) + # + # For all prime powers q=1 mod 6, there exists a KTS(2q+1) + elif ((v-1)//2)%6 == 1 and is_prime_power((v-1)//2): + if existence: + return True + from sage.rings.finite_rings.constructor import FiniteField as GF + q = (v-1)//2 + K = GF(q,'x') + a = K.primitive_element() + t = (q-1)/6 + + # m is the solution of a^m=(a^t+1)/2 + m = 0 + rhs = (a**t+1)/2 + while rhs != 1: + rhs = rhs/a + m += 1 + assert 2*a**m==a**t+1 + + # First parallel class + first_class = [[(0,1),(0,2),'inf']] + first_class.extend([[(a**i,1),(a**(i+t),1),(a**(i+m),2)] + for i in range(t)+range(2*t,3*t)+range(4*t,5*t)]) + first_class.extend([[(a**(i+m+t),2),(a**(i+m+3*t),2),(a**(i+m+5*t),2)] + for i in range(t)]) + + # Action of K on the points + action = lambda v,x : (v+x[0],x[1]) if len(x) == 2 else x + + # relabel to integer + relabel = {(p,x): i+(x-1)*q + for i,p in enumerate(K) + for x in [1,2]} + relabel['inf'] = 2*q + + classes = [[[relabel[action(p,x)] for x in tr] for tr in first_class] + for p in K] + + KTS = BalancedIncompleteBlockDesign(v,[tr for cl in classes for tr in cl],k=3,lambd=1,copy=False) + + KTS._classes = classes + return KTS + + # Construction 1.2 from [Stinson91] (originally Theorem 5 from [RCW71]) + # + # For all prime powers q=1 mod 6, there exists a KTS(3q) + elif (v//3)%6 == 1 and is_prime_power(v//3): + if existence: + return True + from sage.rings.finite_rings.constructor import FiniteField as GF + q = v//3 + K = GF(q,'x') + a = K.primitive_element() + t = (q-1)/6 + A0 = [(0,0),(0,1),(0,2)] + B = [[(a**i,j),(a**(i+2*t),j),(a**(i+4*t),j)] for j in range(3) + for i in range(t)] + A = [[(a**i,0),(a**(i+2*t),1),(a**(i+4*t),2)] for i in range(6*t)] + + # Action of K on the points + action = lambda v,x: (v+x[0],x[1]) + + # relabel to integer + relabel = {(p,j): i+j*q + for i,p in enumerate(K) + for j in range(3)} + + B0 = [A0] + B + A[t:2*t] + A[3*t:4*t] + A[5*t:6*t] + + # Classes + classes = [[[relabel[action(p,x)] for x in tr] for tr in B0] + for p in K] + + for i in range(t)+range(2*t,3*t)+range(4*t,5*t): + classes.append([[relabel[action(p,x)] for x in A[i]] for p in K]) + + KTS = BalancedIncompleteBlockDesign(v,[tr for cl in classes for tr in cl],k=3,lambd=1,copy=False) + KTS._classes = classes + return KTS + + else: + if existence: + return Unknown + raise NotImplementedError("I don't know how to build a ({},{},1)-BIBD!".format(v,3)) + +def v_4_1_rbibd(v,existence=False): + r""" + Return a `(v,4,1)`-RBIBD. + + INPUT: + + - `n` (integer) + + - ``existence`` (boolean; ``False`` by default) -- whether to build the + design or only answer whether it exists. + + .. SEEALSO:: + + - :meth:`IncidenceStructure.is_resolvable` + - :func:`resolvable_balanced_incomplete_block_design` + + .. NOTE:: + + A resolvable `(v,4,1)`-BIBD exists whenever `1\equiv 4\pmod(12)`. This + function, however, only implements a construction of `(v,4,1)`-BIBD such + that `v=3q+1\equiv 1\pmod{3}` where `q` is a prime power (see VII.7.5.a + from [BJL99]_). + + EXAMPLE:: + + sage: rBIBD = designs.resolvable_balanced_incomplete_block_design(28,4) + sage: rBIBD.is_resolvable() + True + sage: rBIBD.is_t_design(return_parameters=True) + (True, (2, 28, 4, 1)) + + TESTS:: + + sage: for q in prime_powers(2,30): + ....: if (3*q+1)%12 == 4: + ....: _ = designs.resolvable_balanced_incomplete_block_design(3*q+1,4) # indirect doctest + """ + # Volume 1, VII.7.5.a from [BJL99]_ + if v%3 != 1 or not is_prime_power((v-1)//3): + if existence: + return Unknown + raise NotImplementedError("I don't know how to build a ({},{},1)-RBIBD!".format(v,4)) + from sage.rings.finite_rings.constructor import FiniteField as GF + q = (v-1)//3 + nn = (q-1)//4 + G = GF(q,'x') + w = G.primitive_element() + e = w**(nn) + assert e**2 == -1 + + first_class = [[(w**i,j),(-w**i,j),(e*w**i,j+1),(-e*w**i,j+1)] + for i in range(nn) for j in range(3)] + + first_class.append([(0,0),(0,1),(0,2),'inf']) + + label = {p:i for i,p in enumerate(G)} + + classes = [[[v-1 if x=='inf' else (x[1]%3)*q+label[x[0]+g] for x in S] + for S in first_class] + for g in G] + + BIBD = BalancedIncompleteBlockDesign(v, + blocks = sum(classes,[]), + k=4, + check=True, + copy=False) + BIBD._classes = classes + assert BIBD.is_resolvable() + return BIBD From f6c51cfe94eb0f01eba6600082bea267f6049dd9 Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Sun, 4 Jan 2015 13:17:49 +0530 Subject: [PATCH 107/217] trac #17545: review --- src/doc/en/developer/trac.rst | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/doc/en/developer/trac.rst b/src/doc/en/developer/trac.rst index f8327eb0e43..e981323394b 100644 --- a/src/doc/en/developer/trac.rst +++ b/src/doc/en/developer/trac.rst @@ -4,7 +4,7 @@ The Sage Trac Server ==================== -All changes to Sage source code have to go through the `Sage trac +All changes to Sage source code have to go through the `Sage Trac development server `_. The purpose of the Sage trac server is to @@ -176,8 +176,6 @@ issuing some basic gitolite commands, for example:: .. _trac-bug-report: -.. _section-trac-bug-report: - Reporting Bugs ============== @@ -243,7 +241,7 @@ describing the idea. no metric to measure this properly and it is highly subjective. - For bug reports: the ticket's description should contain the information - described at :ref:`section-trac-bug-report`. + described at :ref:`trac-bug-report`. - If appropriate, provide URLs to background information or sage-devel conversation relevant to the problem you are reporting. From f684012d92548a3eb596e80e4ecabd3f6912b902 Mon Sep 17 00:00:00 2001 From: vince Date: Sun, 21 Dec 2014 07:16:09 +0000 Subject: [PATCH 108/217] Fixing incorrect optional tag --- src/sage/game_theory/normal_form_game.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/game_theory/normal_form_game.py b/src/sage/game_theory/normal_form_game.py index 47d601b84a6..338a0ad8c97 100644 --- a/src/sage/game_theory/normal_form_game.py +++ b/src/sage/game_theory/normal_form_game.py @@ -1170,7 +1170,7 @@ def obtain_nash(self, algorithm=False, maximization=True): [[(1, 0, 0, 0, 0), (0, 1, 0, 0, 0)]] sage: fivegame.obtain_nash(algorithm='lrs') # optional - lrs [[(1, 0, 0, 0, 0), (0, 1, 0, 0, 0)]] - sage: fivegame.obtain_nash(algorithm='LCP') # optional - LCP + sage: fivegame.obtain_nash(algorithm='LCP') # optional - gambit [[(1.0, 0.0, 0.0, 0.0, 0.0), (0.0, 1.0, 0.0, 0.0, 0.0)]] Here is an example of a 3 by 2 game with 3 Nash equilibrium:: From 93bd1d6de52e3d4e9581d41f2e7792f550e7d035 Mon Sep 17 00:00:00 2001 From: vince Date: Sun, 21 Dec 2014 07:18:13 +0000 Subject: [PATCH 109/217] Fixing wording for LCP algorithm differences Merge conflict --- src/sage/game_theory/gambit_docs.py | 4 ++-- src/sage/game_theory/normal_form_game.py | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/sage/game_theory/gambit_docs.py b/src/sage/game_theory/gambit_docs.py index 0ecc1c5d805..90401e732e8 100644 --- a/src/sage/game_theory/gambit_docs.py +++ b/src/sage/game_theory/gambit_docs.py @@ -9,8 +9,8 @@ $ ./sage -i gambit The `python API documentation for gambit -_` shows various examples -that can be run easily in Ipython. To run the Ipython packaged with Sage run +`_ shows various examples +that can be run easily in ipython. To run the ipython packaged with Sage run (from root of Sage):: $ ./sage -ipython diff --git a/src/sage/game_theory/normal_form_game.py b/src/sage/game_theory/normal_form_game.py index 338a0ad8c97..c4a6dab4263 100644 --- a/src/sage/game_theory/normal_form_game.py +++ b/src/sage/game_theory/normal_form_game.py @@ -1054,7 +1054,8 @@ def obtain_nash(self, algorithm=False, maximization=True): * ``"LCP"`` - This algorithm is only suited for 2 player games. See the gambit web site (http://gambit.sourceforge.net/). Note - that the output differs to the other algorithms: floats are returned. + that the output differs from the other algorithms: floats are + returned. * ``'enumeration'`` - This is a very inefficient algorithm (in essence a brute force approach). From 4e2afbd5d7b2b6bdb40e65463ad51d7f3f19641f Mon Sep 17 00:00:00 2001 From: vince Date: Fri, 2 Jan 2015 10:19:11 +0000 Subject: [PATCH 110/217] Fixing syntaxing --- src/sage/game_theory/gambit_docs.py | 2 +- src/sage/game_theory/normal_form_game.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/game_theory/gambit_docs.py b/src/sage/game_theory/gambit_docs.py index 90401e732e8..92b1fbf31e8 100644 --- a/src/sage/game_theory/gambit_docs.py +++ b/src/sage/game_theory/gambit_docs.py @@ -100,7 +100,7 @@ In [12]: solver.solve(g) Out[12]: [] -If we solve this with the `LCP` solver we get the expected Nash equilibrium:: +If we solve this with the ``LCP`` solver we get the expected Nash equilibrium:: In [13]: solver = gambit.nash.ExternalLCPSolver() In [14]: solver.solve(g) diff --git a/src/sage/game_theory/normal_form_game.py b/src/sage/game_theory/normal_form_game.py index c4a6dab4263..f43168ee8f5 100644 --- a/src/sage/game_theory/normal_form_game.py +++ b/src/sage/game_theory/normal_form_game.py @@ -206,7 +206,7 @@ algorithm uses the optional 'lrs' package. To install it type ``sage -i lrs`` at the command line. For more information see [A2000]_. -* ``LCP``: Linear complementarity program algorithm for 2 player games. +* ``'LCP'``: Linear complementarity program algorithm for 2 player games. This algorithm uses the open source game theory package: `Gambit `_ [MMAT2014]_. At present this is the only gambit algorithm available in sage but further development will @@ -1052,7 +1052,7 @@ def obtain_nash(self, algorithm=False, maximization=True): * ``'lrs'`` - This algorithm is only suited for 2 player games. See the lrs web site (http://cgm.cs.mcgill.ca/~avis/C/lrs.html). - * ``"LCP"`` - This algorithm is only suited for 2 player games. + * ``'LCP'`` - This algorithm is only suited for 2 player games. See the gambit web site (http://gambit.sourceforge.net/). Note that the output differs from the other algorithms: floats are returned. From b0142f51755a7a441a0b787c4c06949ad09a38ea Mon Sep 17 00:00:00 2001 From: vince Date: Sun, 4 Jan 2015 08:08:00 +0000 Subject: [PATCH 111/217] Have fixed Ipython capitalisation --- src/sage/game_theory/gambit_docs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/game_theory/gambit_docs.py b/src/sage/game_theory/gambit_docs.py index 92b1fbf31e8..4047224dcb0 100644 --- a/src/sage/game_theory/gambit_docs.py +++ b/src/sage/game_theory/gambit_docs.py @@ -10,7 +10,7 @@ The `python API documentation for gambit `_ shows various examples -that can be run easily in ipython. To run the ipython packaged with Sage run +that can be run easily in IPython. To run the IPython packaged with Sage run (from root of Sage):: $ ./sage -ipython From 3a0b97e0f98ac1f00aae3541b7a5be9a46489dee Mon Sep 17 00:00:00 2001 From: vince Date: Sun, 4 Jan 2015 08:11:45 +0000 Subject: [PATCH 112/217] Adding correct backticks --- src/sage/game_theory/gambit_docs.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/game_theory/gambit_docs.py b/src/sage/game_theory/gambit_docs.py index 4047224dcb0..b8da63740df 100644 --- a/src/sage/game_theory/gambit_docs.py +++ b/src/sage/game_theory/gambit_docs.py @@ -76,7 +76,7 @@ - ExternalIteratedPolymatrixSolver - ExternalLogitSolver -Here is how to use the `ExternalEnumPureSolver`:: +Here is how to use the ``ExternalEnumPureSolver``:: In [21]: solver = gambit.nash.ExternalEnumPureSolver() In [22]: solver.solve(g) @@ -111,7 +111,7 @@ as extensive form games: for more details see http://www.gambit-project.org/. If one really wants to use gambit directly in Sage (without using the -`NormalFormGame` class as a wrapper) then integers must first be +``NormalFormGame`` class as a wrapper) then integers must first be converted to Python integers (due to the preparser). Here is an example showing the Battle of the Sexes:: From 067e3256e38168f98b772a7308b79021e0f8dfd8 Mon Sep 17 00:00:00 2001 From: Ben Hutz Date: Sun, 4 Jan 2015 10:21:00 -0500 Subject: [PATCH 113/217] 17427: fixed 32-bit doctests --- src/sage/schemes/projective/projective_point.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/sage/schemes/projective/projective_point.py b/src/sage/schemes/projective/projective_point.py index e373c7d8da4..91cebf1813a 100644 --- a/src/sage/schemes/projective/projective_point.py +++ b/src/sage/schemes/projective/projective_point.py @@ -355,8 +355,10 @@ def __hash__(self): sage: P. = ProjectiveSpace(ZZ, 1) sage: hash(P([1,1])) + 1265304440 # 32-bit 7316841028997809016 # 64-bit sage: hash(P.point([2,2], False)) + 1265304440 # 32-bit 7316841028997809016 # 64-bit :: @@ -366,14 +368,17 @@ def __hash__(self): sage: O = K.maximal_order() sage: P. = ProjectiveSpace(O, 1) sage: hash(P([1+w, 2])) + -609701421 # 32-bit 4801154424156762579 # 64-bit sage: hash(P([2, 1-w])) + -609701421 # 32-bit 4801154424156762579 # 64-bit :: sage: P. = ProjectiveSpace(Zmod(10), 1) sage: hash(P([2,5])) + -479010389 # 32-bit 4677413289753502123 # 64-bit """ R = self.codomain().base_ring() @@ -1113,8 +1118,10 @@ def __hash__(self): sage: P. = ProjectiveSpace(QQ, 1) sage: hash(P([1/2, 1])) + -1741117121 # 32-bit 3714374126286711103 # 64-bit sage: hash(P.point([1, 2], False)) + -1741117121 # 32-bit 3714374126286711103 # 64-bit """ P = copy(self) From b3cb45774f2f91f75ea070ffac22a826c7cff396 Mon Sep 17 00:00:00 2001 From: Ralf Stephan Date: Sun, 4 Jan 2015 17:58:10 +0100 Subject: [PATCH 114/217] 16201: change keyword default; add doctests; add misc.defaults to doctree and point to it --- src/doc/en/reference/misc/index.rst | 1 + src/sage/misc/defaults.py | 6 +++++- src/sage/rings/laurent_series_ring.py | 4 ++++ src/sage/rings/power_series_ring.py | 3 +++ src/sage/symbolic/expression.pyx | 11 +++++++++-- 5 files changed, 22 insertions(+), 3 deletions(-) diff --git a/src/doc/en/reference/misc/index.rst b/src/doc/en/reference/misc/index.rst index 5ae0a6a5151..4742f5cecb8 100644 --- a/src/doc/en/reference/misc/index.rst +++ b/src/doc/en/reference/misc/index.rst @@ -49,6 +49,7 @@ Miscellaneous sage/misc/messaging sage/misc/viewer sage/misc/session + sage/misc/defaults Profiling and Performance Testing diff --git a/src/sage/misc/defaults.py b/src/sage/misc/defaults.py index e822a46bd2a..a842a1982db 100644 --- a/src/sage/misc/defaults.py +++ b/src/sage/misc/defaults.py @@ -1,5 +1,5 @@ """ -Defaults +Default Settings AUTHORS: William Stein and David Kohel """ @@ -54,6 +54,10 @@ def set_default_variable_name(name, separator=''): series_prec = 20 def series_precision(): + """ + Return the Sage-wide precision for series (symbolic, + power series, Laurent series). + """ return series_prec def set_series_precision(prec): diff --git a/src/sage/rings/laurent_series_ring.py b/src/sage/rings/laurent_series_ring.py index efacd3eb3ac..d4ced6964c4 100644 --- a/src/sage/rings/laurent_series_ring.py +++ b/src/sage/rings/laurent_series_ring.py @@ -12,6 +12,10 @@ Finite Field of size 17 sage: S.base_ring() Univariate Polynomial Ring in x over Finite Field of size 17 + +.. SEEALSO:: + + * :func:`sage.misc.defaults.set_series_precision` """ import weakref diff --git a/src/sage/rings/power_series_ring.py b/src/sage/rings/power_series_ring.py index 9f2e58a4507..fbbbf71b78c 100644 --- a/src/sage/rings/power_series_ring.py +++ b/src/sage/rings/power_series_ring.py @@ -323,6 +323,9 @@ def PowerSeriesRing(base_ring, name=None, arg2=None, names=None, Category of commutative rings sage: TestSuite(M).run() + .. SEEALSO:: + + * :func:`sage.misc.defaults.set_series_precision` """ #multivariate case: # examples for first case: diff --git a/src/sage/symbolic/expression.pyx b/src/sage/symbolic/expression.pyx index 0eaf1786861..03c5d87052a 100644 --- a/src/sage/symbolic/expression.pyx +++ b/src/sage/symbolic/expression.pyx @@ -3492,7 +3492,7 @@ cdef class Expression(CommutativeRingElement): for g in self.gradient()]) - def series(self, symbol, int order=-1): + def series(self, symbol, int order=-65535): r""" Return the power series expansion of self in terms of the given variable to the given order. @@ -3539,6 +3539,8 @@ cdef class Expression(CommutativeRingElement): sage: f = sin(x)/x^2 sage: f.series(x,7) 1*x^(-1) + (-1/6)*x + 1/120*x^3 + (-1/5040)*x^5 + Order(x^7) + sage: f.series(x) + 1*x^(-1) + (-1/6)*x + ... + Order(x^20) sage: f.series(x==1,3) (sin(1)) + (cos(1) - 2*sin(1))*(x - 1) + (-2*cos(1) + 5/2*sin(1))*(x - 1)^2 + Order((x - 1)^3) sage: f.series(x==1,3).truncate().expand() @@ -3563,10 +3565,15 @@ cdef class Expression(CommutativeRingElement): sage: ((1+arctan(x))**(1/x)).series(x==0, 3) (e) + (-1/2*e)*x + (1/8*e)*x^2 + Order(x^3) + + Order may be negative:: + + sage: f = sin(x)^(-2); f.series(x, -1) + 1*x^(-2) + Order(1/x) """ cdef Expression symbol0 = self.coerce_in(symbol) cdef GEx x - if order < 0: + if order == -65535: from sage.misc.defaults import series_precision order = series_precision() sig_on() From df12b0e059f074b7d39c09434d61b8d74a3dd009 Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Sun, 4 Jan 2015 22:43:20 +0530 Subject: [PATCH 115/217] trac #17582: Bandwidth of a graph --- src/doc/en/reference/graphs/index.rst | 1 + src/module_list.py | 3 + src/sage/graphs/distances_all_pairs.pxd | 5 + .../graphs/graph_decompositions/__init__.py | 1 + .../graphs/graph_decompositions/bandwidth.pyx | 369 ++++++++++++++++++ 5 files changed, 379 insertions(+) create mode 100644 src/sage/graphs/graph_decompositions/bandwidth.pyx diff --git a/src/doc/en/reference/graphs/index.rst b/src/doc/en/reference/graphs/index.rst index 0a6995e6147..8da1ab2a4ca 100644 --- a/src/doc/en/reference/graphs/index.rst +++ b/src/doc/en/reference/graphs/index.rst @@ -75,6 +75,7 @@ Libraries of algorithms sage/graphs/graph_plot_js sage/graphs/graph_decompositions/vertex_separation sage/graphs/graph_decompositions/rankwidth + sage/graphs/graph_decompositions/bandwidth sage/graphs/graph_decompositions/graph_products sage/graphs/modular_decomposition/modular_decomposition sage/graphs/convexity_properties diff --git a/src/module_list.py b/src/module_list.py index 0684b954b7c..37311ff262b 100755 --- a/src/module_list.py +++ b/src/module_list.py @@ -505,6 +505,9 @@ def uname_specific(name, value, alternative): sources = ['sage/graphs/graph_decompositions/rankwidth.pyx', 'sage/graphs/graph_decompositions/rankwidth_c/rw.c']), + Extension('sage.graphs.graph_decompositions.bandwidth', + sources = ['sage/graphs/graph_decompositions/bandwidth.pyx']), + Extension('sage.graphs.spanning_tree', sources = ['sage/graphs/spanning_tree.pyx']), diff --git a/src/sage/graphs/distances_all_pairs.pxd b/src/sage/graphs/distances_all_pairs.pxd index 4cf4bb46e43..9928ab9f689 100644 --- a/src/sage/graphs/distances_all_pairs.pxd +++ b/src/sage/graphs/distances_all_pairs.pxd @@ -1,3 +1,8 @@ cdef unsigned short * c_shortest_path_all_pairs(G) except NULL cdef unsigned short * c_distances_all_pairs(G) +cdef all_pairs_shortest_path_BFS(gg, + unsigned short * predecessors, + unsigned short * distances, + int * eccentricity) + cdef int * c_eccentricity(G) except NULL diff --git a/src/sage/graphs/graph_decompositions/__init__.py b/src/sage/graphs/graph_decompositions/__init__.py index 31707e78d3b..5eaac2c2db4 100644 --- a/src/sage/graphs/graph_decompositions/__init__.py +++ b/src/sage/graphs/graph_decompositions/__init__.py @@ -1,3 +1,4 @@ # This file is not empty ! import sage.graphs.graph_decompositions.vertex_separation import sage.graphs.graph_decompositions.rankwidth +import sage.graphs.graph_decompositions.bandwidth diff --git a/src/sage/graphs/graph_decompositions/bandwidth.pyx b/src/sage/graphs/graph_decompositions/bandwidth.pyx new file mode 100644 index 00000000000..c5c704a776e --- /dev/null +++ b/src/sage/graphs/graph_decompositions/bandwidth.pyx @@ -0,0 +1,369 @@ +r""" +Bandwidth + +Definition +---------- + +The bandwidth `bw(M)` of a matrix `M` is the smallest integer `k` such that all +non-zero entries of `M` are at distance `k` from the diagonal. The bandwidth +`bw(G)` of a graph `G` is the minimum bandwidth of the adjacency matrix of `G`, +over all possible relabellings of its vertices. + +**Path spanner:** alternatively, the bandwidth measures how tightly a path +represents the distance of a graph `G`. Indeed, if the vertices of `G` can be +ordered as `v_1,...,v_n` in such a way that `k \times d_G(v_i,v_j) \geq |i-j|` then +`bw(G)\leq k`. + + **Proof:** for all `v_i \sim v_j` (i.e. `d_G(v_i,v_j)=1`), the constraint + ensures that `k\geq |i-j|`, meaning that adjacent vertices are at distance + at most `k` in the path ordering. That alone is sufficient to ensure that + `bw(G)\leq k`. + + As a byproduct, we obtain that `k \times d_G(v_i,v_j) \geq |i-j|` in + general: let `v_{s_0},...,v_{s_i}` be the vertices of a shortest + `(v_i,v_j)`-path. We have: + + .. MATH:: + + k \times d_G(v_i,v_j) &= k\times d_G(v_i,v_{s_0}) + k\times d_G(v_{s_0},v_{s_1}) + ... + k\times d_G(v_{s_{i-1}},v_{s_i}) + k\times d_G(v_{s_i},v_j)\\ + &\geq |v_i-v_{s_0}| + |v_{s_0}-v_{s_1}| + ... + |v_{s_{i-1}}-v_{s_i}| + |v_{s_i}-v_j|\\ + &\geq |v_i-v_j|\\ + +Satisfiability of a partial assignment +-------------------------------------- + +Let us suppose that the first `i` vertices `v_1,...,v_i` of `G` have already +been assigned positions `p_1,...,p_i` in an ordering of `V(G)` of bandwidth +`\leq k`. Where can `v_{i+1}` appear ? + +Because of the previous definition, `p_{i+1}` must be at distance at most +`k\times d_G(v_1,v_{i+1})` from `p_1`, and in general at distance at most +`k\times d_G(v_j,v_{i+1})` from `p_j`. Each range is an interval of +`\{1,...,n\}\backslash \{p_1,...,p_i\}`, and because the intersection of two +intervals is again an interval we deduce that in order to satisfy all these +constraints simultaneously `p_j` must belong to an interval defined from this +partial assignment. + +Applying this rule to all non-assigned vertices, we deduce that each of them +must be assigned to a given interval of `\{1,...,n\}`. Note that this can also +be extended to the already assigned vertices, by saying that `v_j` with `j +# +# Distributed under the terms of the GNU General Public License (GPL) +# http://www.gnu.org/licenses/ +#***************************************************************************** + +from libc.stdint cimport uint16_t, uint32_t, uint64_t +from libc.stdlib cimport malloc, free +from sage.graphs.distances_all_pairs cimport all_pairs_shortest_path_BFS + +ctypedef uint32_t index_t + +ctypedef struct range_t: + index_t m + index_t M + +def bandwidth(G, k=None): + r""" + Compute the bandwidth of a graph. + + For a definition of the bandwidth of a graph, see the documentation of the + :mod:`~sage.graphs.graph_decompositions.bandwidth` module. + + INPUT: + + - ``G`` (a graph) + + - ``k`` -- set to an integer value to test whether `bw(G)\leq k`, or to + ``None`` (default) to compute `bw(G)`. + + OUTPUT: + + When `k` is an integer value, the function returns either ``False`` or a + pair ``(ordering, adjacency_matrix)``. + + When `k` is equal to ``None``, the function returns a triple ``(bw, + ordering, adjacency_matrix)``. + + EXAMPLES:: + + sage: from sage.graphs.graph_decompositions.bandwidth import bandwidth + sage: bandwidth(graphs.PetersenGraph(),3) + False + sage: bandwidth(graphs.PetersenGraph()) + ( + [0 1 1 0 1 0 0 0 0 0] + [1 0 0 0 0 1 1 0 0 0] + [1 0 0 1 0 0 0 1 0 0] + [0 0 1 0 0 0 1 0 1 0] + [1 0 0 0 0 0 0 0 1 1] + [0 1 0 0 0 0 0 1 1 0] + [0 1 0 1 0 0 0 0 0 1] + [0 0 1 0 0 1 0 0 0 1] + [0 0 0 1 1 1 0 0 0 0] + 5, [0, 4, 5, 8, 1, 9, 3, 7, 6, 2], [0 0 0 0 1 0 1 1 0 0] + ) + sage: bandwidth(graphs.ChvatalGraph()) + ( + [0 0 1 1 0 1 1 0 0 0 0 0] + [0 0 0 1 1 1 0 1 0 0 0 0] + [1 0 0 0 1 0 0 1 1 0 0 0] + [1 1 0 0 0 0 0 0 1 1 0 0] + [0 1 1 0 0 0 1 0 0 1 0 0] + [1 1 0 0 0 0 0 0 0 0 1 1] + [1 0 0 0 1 0 0 1 0 0 0 1] + [0 1 1 0 0 0 1 0 0 0 1 0] + [0 0 1 1 0 0 0 0 0 0 1 1] + [0 0 0 1 1 0 0 0 0 0 1 1] + [0 0 0 0 0 1 0 1 1 1 0 0] + 6, [0, 5, 9, 4, 10, 1, 6, 11, 3, 8, 7, 2], [0 0 0 0 0 1 1 0 1 1 0 0] + ) + """ + # All that this function does is allocate/free the memory for function + # bandwidth_C + + G = G.relabel(inplace=False) # int-labelled + + cdef int n = G.order() + + cdef unsigned short ** d = malloc( n * sizeof(unsigned short *)) + cdef unsigned short * distances = malloc( n*n * sizeof(unsigned short )) + cdef index_t * current = malloc( n * sizeof(index_t)) + cdef index_t * ordering = malloc( n * sizeof(index_t)) + cdef index_t * left_to_order = malloc( n * sizeof(index_t)) + cdef index_t * index_array_tmp = malloc( n * sizeof(index_t)) + cdef range_t * range_arrays = malloc( n*n * sizeof(range_t)) + cdef range_t ** ith_range_array = malloc( n * sizeof(range_t *)) + cdef range_t * range_array_tmp = malloc( n * sizeof(range_t)) + + if (d is NULL or + distances is NULL or + current is NULL or + ordering is NULL or + left_to_order is NULL or + index_array_tmp is NULL or + ith_range_array is NULL or + range_arrays is NULL or + range_array_tmp is NULL): + + free(d) + free(distances) + free(current) + free(ordering) + free(left_to_order) + free(index_array_tmp) + free(range_arrays) + free(ith_range_array) + free(range_array_tmp) + + cdef int i,j + all_pairs_shortest_path_BFS(G,NULL,distances,NULL) # compute the distance matrix + + # fill d so that d[i][j] works + for i in range(n): + d[i] = distances + i*n + + # ith_range_array + for i in range(n): + ith_range_array[i] = range_arrays + i*n + + # initialize left_to_order + for i in range(n): + left_to_order[i] = i + + if k is None: + for k in range((n-1)//G.diameter(),n): + if bandwidth_C(n,k,d,current,ordering,left_to_order,index_array_tmp,ith_range_array,range_array_tmp): + from sage.matrix.constructor import Matrix + order = [ordering[i] for i in range(n)] + M = Matrix([[int(G.has_edge(order[i],order[j])) for i in range(n)] for j in range(n)]) + assert all(abs(i-j)<= k for i,j in M.dict()) + ans = k, order, M + break + else: + ans = bool(bandwidth_C(n,k,d,current,ordering,left_to_order,index_array_tmp,ith_range_array,range_array_tmp)) + if ans: + from sage.matrix.constructor import Matrix + order = [ordering[i] for i in range(n)] + M = Matrix([[int(G.has_edge(order[i],order[j])) for i in range(n)] for j in range(n)]) + assert all(abs(i-j)<= k for i,j in M.dict()) + ans = k, order, M + + free(d) + free(distances) + free(current) + free(ordering) + free(left_to_order) + free(index_array_tmp) + free(range_arrays) + free(ith_range_array) + free(range_array_tmp) + return ans + +cdef bint bandwidth_C(int n, int k, + unsigned short ** d, + index_t * current, # choice of vertex for the current position + index_t * ordering, # the actual ordering of vertices + index_t * left_to_order, # begins with the assigned vertices, ends with the others + index_t * index_array_tmp, # tmp space + range_t ** ith_range_array, # array of ranges, for every step of the algorithm + range_t * range_array_tmp):# tmp space + + cdef int i,v + cdef int pi # the position for which a vertex is being chosen + cdef int vi # the vertex being tested at position pi + cdef int radius + current[0] = -1 + + # At first any vertex can be anywhere + for v in range(n): + ith_range_array[0][v].m = 0 + ith_range_array[0][v].M = n-1 + + i = 0 + while True: + #print i + current[i] += 1 + + if current[i] == n: # All choices for this position have been exhausted + if i == 0: + return 0 + i = i-1 + left_to_order[i], left_to_order[current[i]] = left_to_order[current[i]], left_to_order[i] + continue + + pi = (n-1-i//2) if (i%2) else (i//2) # 0, n-1,1,n-2,2,n-3,3, ... that's an ugly 'if' + vi = left_to_order[current[i]] + + # Wrong choice + if (ith_range_array[i][vi].m > pi or + ith_range_array[i][vi].M < pi): + continue + + # swap + left_to_order[i], left_to_order[current[i]] = left_to_order[current[i]], left_to_order[i] + ordering[pi] = vi + + if i == n-1: # We did it ! + return 1 + + # build the range array of depth i+1 knowing that vertex vi is at + # position pi. + # + # k*d[v][vi] >= |p_v-p_{vi}| + for v in range(n): + radius = k*d[v][vi] + ith_range_array[i+1][v].m = max( ith_range_array[i][v].m,pi-radius) + ith_range_array[i+1][v].M = min( ith_range_array[i][v].M,pi+radius) + + # check feasibility at depth i+1 + if is_matching_feasible(n,ith_range_array[i+1],range_array_tmp, index_array_tmp): + i += 1 + current[i] = i-1 + else: + # swap back + left_to_order[i], left_to_order[current[i]] = left_to_order[current[i]], left_to_order[i] + +cdef bint is_matching_feasible(int n, range_t * range_array, range_t * range_array_tmp, index_t * index_array_tmp): + r""" + Test if the matching is feasible + + INPUT: + + - ``n`` (integer) -- number of points + + - ``range_array`` -- associates to every point an interval in which the + point must be given a position. + + - ``range_array_tmp`` -- temporary spaces with the same characteristics as + ``range_array`` + + - ``index_array_tmp`` -- temporary space to associate an integer to every + point. + + OUTPUT: + + The function must return a boolean, and does not change the content of + ``range_array``. + """ + # Heuristic: check if some vertex has an empty range, that's an easy 'no'. + cdef int v,M,m,j + for v in range(n): + if range_array[v].M < range_array[v].m: + #print range_array[v].m, range_array[v].M + return 0 + index_array_tmp[v] = 0 + + # Sort the guys according to increasing value of M in O(n). + # + # Step 1: count the occurrences of each M + for v in range(n): + index_array_tmp[range_array[v].M] += 1 + + # Step 2: sorted table + for v in range(1,n): + index_array_tmp[v] += index_array_tmp[v-1] + + for v in range(n): + M = range_array[v].M + m = range_array[v].m + index_array_tmp[M] -= 1 + range_array_tmp[index_array_tmp[M]].M = M + range_array_tmp[index_array_tmp[M]].m = m + + # Satisfiability. We use index_array_tmp as a bitset, and mark every + # assigned position. + for v in range(n): + index_array_tmp[v] = 0 + + for v in range(n): + for j in range(range_array_tmp[v].m, range_array_tmp[v].M+1): + if not index_array_tmp[j]: + index_array_tmp[j] = 1 + break + else: + return 0 + return 1 From 19da4149e16a3a32bd68e354f01e0a50e1af3441 Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Mon, 5 Jan 2015 09:35:54 +0530 Subject: [PATCH 116/217] trac #17582: Review --- src/sage/graphs/distances_all_pairs.pyx | 2 -- .../graphs/graph_decompositions/bandwidth.pyx | 31 +++++++++---------- 2 files changed, 14 insertions(+), 19 deletions(-) diff --git a/src/sage/graphs/distances_all_pairs.pyx b/src/sage/graphs/distances_all_pairs.pyx index ec1cbfbbd5d..3754b237bbb 100644 --- a/src/sage/graphs/distances_all_pairs.pyx +++ b/src/sage/graphs/distances_all_pairs.pyx @@ -163,8 +163,6 @@ cdef inline all_pairs_shortest_path_BFS(gg, from sage.rings.infinity import Infinity - cdef CGraph cg = gg._backend._cg - cdef list int_to_vertex = gg.vertices() cdef int i diff --git a/src/sage/graphs/graph_decompositions/bandwidth.pyx b/src/sage/graphs/graph_decompositions/bandwidth.pyx index c5c704a776e..2dae6203d0a 100644 --- a/src/sage/graphs/graph_decompositions/bandwidth.pyx +++ b/src/sage/graphs/graph_decompositions/bandwidth.pyx @@ -165,8 +165,6 @@ def bandwidth(G, k=None): # All that this function does is allocate/free the memory for function # bandwidth_C - G = G.relabel(inplace=False) # int-labelled - cdef int n = G.order() cdef unsigned short ** d = malloc( n * sizeof(unsigned short *)) @@ -198,9 +196,11 @@ def bandwidth(G, k=None): free(range_arrays) free(ith_range_array) free(range_array_tmp) + raise MemoryError - cdef int i,j + cdef int i,j,kk all_pairs_shortest_path_BFS(G,NULL,distances,NULL) # compute the distance matrix + cdef list int_to_vertex = G.vertices() # fill d so that d[i][j] works for i in range(n): @@ -215,22 +215,20 @@ def bandwidth(G, k=None): left_to_order[i] = i if k is None: - for k in range((n-1)//G.diameter(),n): - if bandwidth_C(n,k,d,current,ordering,left_to_order,index_array_tmp,ith_range_array,range_array_tmp): - from sage.matrix.constructor import Matrix - order = [ordering[i] for i in range(n)] - M = Matrix([[int(G.has_edge(order[i],order[j])) for i in range(n)] for j in range(n)]) - assert all(abs(i-j)<= k for i,j in M.dict()) - ans = k, order, M + for kk in range((n-1)//G.diameter(),n): + if bandwidth_C(n,kk,d,current,ordering,left_to_order,index_array_tmp,ith_range_array,range_array_tmp): + ans = True break else: ans = bool(bandwidth_C(n,k,d,current,ordering,left_to_order,index_array_tmp,ith_range_array,range_array_tmp)) - if ans: - from sage.matrix.constructor import Matrix - order = [ordering[i] for i in range(n)] - M = Matrix([[int(G.has_edge(order[i],order[j])) for i in range(n)] for j in range(n)]) - assert all(abs(i-j)<= k for i,j in M.dict()) - ans = k, order, M + + if ans: + from sage.matrix.constructor import Matrix + order = [int_to_vertex[ordering[i]] for i in range(n)] + M = Matrix([[int(G.has_edge(u,v)) for u in order] for v in order]) + assert all(abs(i-j)<= (kk if k is None else k) + for i,j in M.dict()) + ans = (kk, order, M) if k is None else (order,M) free(d) free(distances) @@ -265,7 +263,6 @@ cdef bint bandwidth_C(int n, int k, i = 0 while True: - #print i current[i] += 1 if current[i] == n: # All choices for this position have been exhausted From 5c37a78c3b5b1491bf4de7a5ca447364c076f09c Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Mon, 5 Jan 2015 16:13:39 +0530 Subject: [PATCH 117/217] trac #17555: remove mention of the dev scripts --- src/doc/en/developer/git_trac.rst | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/doc/en/developer/git_trac.rst b/src/doc/en/developer/git_trac.rst index 773d5ab143e..be4efb8b57d 100644 --- a/src/doc/en/developer/git_trac.rst +++ b/src/doc/en/developer/git_trac.rst @@ -14,11 +14,8 @@ public setting on `the Sage Trac server `_ (the Sage bug and enhancement tracker). One can use ``git`` :ref:`the hard way ` for this, -but this section presumes use of the helper ``git trac`` command, which +but this section explains how to use the helper ``git trac`` command, which simplifies many of the most common actions in collaboration on Sage. -Sage itself has a more limited set of actions built in to work with -(see :ref:`chapter-devscript`), but the recommended path is using -this section of the manual to get started. Most of the commands in the following section will not work unless you have an account on Trac. If you want to contribute to Sage, it From 07b0bab5742b10152c28705c3d01eb4b3c5ba6e6 Mon Sep 17 00:00:00 2001 From: vince Date: Mon, 5 Jan 2015 16:29:05 +0000 Subject: [PATCH 118/217] Have corrected optional test --- src/sage/game_theory/normal_form_game.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/game_theory/normal_form_game.py b/src/sage/game_theory/normal_form_game.py index 80ffa11a6c6..0418679ef38 100644 --- a/src/sage/game_theory/normal_form_game.py +++ b/src/sage/game_theory/normal_form_game.py @@ -1203,7 +1203,7 @@ def obtain_nash(self, algorithm=False, maximization=True): True sage: lrs_eqs == sorted(lrs_eqs) # optional - lrs True - sage: LCP_eqs == sorted(LCP_eqs) # optional - LCP + sage: LCP_eqs == sorted(LCP_eqs) # optional - gambit True sage: lrs_eqs == enumeration_eqs # optional - lrs True From 36241e3e3706ed269ea9617c5094aa54e097dd4a Mon Sep 17 00:00:00 2001 From: Martin von Gagern Date: Mon, 5 Jan 2015 19:15:19 +0100 Subject: [PATCH 119/217] Clearer documentation for reproducible_repr. --- src/sage/doctest/fixtures.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/sage/doctest/fixtures.py b/src/sage/doctest/fixtures.py index 9057a82462e..6db5cb4358f 100644 --- a/src/sage/doctest/fixtures.py +++ b/src/sage/doctest/fixtures.py @@ -54,9 +54,12 @@ def reproducible_repr(val): One example is the order of elements in a hash-based structure. For most objects, this is simply the ``repr`` of the object. - All types which require special handling are covered by the - examples below. If a doctest requires special handling for - additional types, this function may be extended apropriately. + All types for which special handling had been implemented are + covered by the examples below. If a doctest requires special + handling for additional types, this function may be extended + apropriately. It is an error if an argument to this function has + a non-reproducible ``repr`` implementation and is not explicitely + mentioned in an example case below. INPUT: From e3093ed9167903328b40ccfa5f0d41729d07021d Mon Sep 17 00:00:00 2001 From: Martin von Gagern Date: Mon, 5 Jan 2015 23:12:16 +0100 Subject: [PATCH 120/217] Add reproducible_repr for list and dict. The special handling of lists is needed in order to perform recursive calls of this function for each element of a list. Also reduce reliance on comparison operators, by comparing representations instead of (or at least in preference to) actual values. This has the drawback that we don't sort numerically, which might seem strange. But it has the benefit that we can establish a total order even for containers of mixed and mutually not comparable types, as long as distinct elements yield distinct representations. --- src/sage/doctest/fixtures.py | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/src/sage/doctest/fixtures.py b/src/sage/doctest/fixtures.py index 6db5cb4358f..fd70913f9af 100644 --- a/src/sage/doctest/fixtures.py +++ b/src/sage/doctest/fixtures.py @@ -78,17 +78,37 @@ def reproducible_repr(val): set(['a', 'b', 'c', 'd']) sage: print(reproducible_repr(frozenset(["a", "c", "b", "d"]))) frozenset(['a', 'b', 'c', 'd']) + sage: print(reproducible_repr([1, frozenset("cab"), set("bar"), 0])) + [1, frozenset(['a', 'b', 'c']), set(['a', 'b', 'r']), 0] + sage: print(reproducible_repr({3.0:"three","2":"two",1:"one"})) + {'2': 'two', 1: 'one', 3.00000000000000: 'three'} sage: print(reproducible_repr("foo\nbar")) # demonstrate default case 'foo\nbar' """ + + def sorted_pairs(iterable, pairs=False): + # We don't know whether container data structures will have + # homogeneous types, and if not, whether comparisons will work + # in a sane way. For this reason, we sort by representation first. + res = sorted((reproducible_repr(item), item) for item in iterable) + if not pairs: + res = [r for r, i in res] + return res + if isinstance(val, frozenset): - return ("frozenset([{}])".format - (", ".join(map(reproducible_repr, sorted(val))))) + itms = sorted_pairs(val) + return "frozenset([{}])".format(", ".join(itms)) if isinstance(val, set): - return ("set([{}])".format - (", ".join(map(reproducible_repr, sorted(val))))) - r = repr(val) - return r + itms = sorted_pairs(val) + return "set([{}])".format(", ".join(itms)) + if isinstance(val, dict): + keys = sorted_pairs(val.keys(), True) + itms = ["{}: {}".format(r, reproducible_repr(val[k])) for r, k in keys] + return ("{{{}}}".format(", ".join(itms))) + if isinstance(val, list): + itms = map(reproducible_repr, val) + return ("[{}]".format(", ".join(itms))) + return repr(val) class AttributeAccessTracerHelper(object): From 97f59dc2046e358f9380310883d78cb97e0f78d9 Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Tue, 6 Jan 2015 09:29:41 +0530 Subject: [PATCH 121/217] trac #17582: Review --- .../graphs/graph_decompositions/bandwidth.pyx | 86 +++++++++++++++---- 1 file changed, 68 insertions(+), 18 deletions(-) diff --git a/src/sage/graphs/graph_decompositions/bandwidth.pyx b/src/sage/graphs/graph_decompositions/bandwidth.pyx index 2dae6203d0a..726ae18c9df 100644 --- a/src/sage/graphs/graph_decompositions/bandwidth.pyx +++ b/src/sage/graphs/graph_decompositions/bandwidth.pyx @@ -95,6 +95,7 @@ Functions # Distributed under the terms of the GNU General Public License (GPL) # http://www.gnu.org/licenses/ #***************************************************************************** +include 'sage/ext/interrupt.pxi' from libc.stdint cimport uint16_t, uint32_t, uint64_t from libc.stdlib cimport malloc, free @@ -161,11 +162,53 @@ def bandwidth(G, k=None): [0 0 0 0 0 1 0 1 1 1 0 0] 6, [0, 5, 9, 4, 10, 1, 6, 11, 3, 8, 7, 2], [0 0 0 0 0 1 1 0 1 1 0 0] ) + + TESTS:: + + sage: bandwidth(2*graphs.PetersenGraph()) + (5, + [0, 4, 5, 8, 1, 9, 3, 7, 6, 2, 10, 14, 15, 18, 11, 19, 13, 17, 16, 12], + 20 x 20 dense matrix over Integer Ring) + sage: bandwidth(Graph()) + (0, [], []) + sage: bandwidth(Graph(1)) + (0, [0], [0]) + sage: bandwidth(Graph(3)) + ( + [0 0 0] + [0 0 0] + 0, [0, 1, 2], [0 0 0] + ) """ + # Trivial cases + if G.order() <= 1: + from sage.matrix.constructor import Matrix + if k is None: + return (0,G.vertices(),Matrix([[0]*G.order()])) + else: + return (G.vertices(),Matrix([[0]*G.order()])) + + if not G.is_connected(): + from sage.matrix.constructor import block_diagonal_matrix + max_k = 0 if k is None else k + order = [] + mat = [] + for GG in G.connected_components_subgraphs(): + ans = bandwidth(GG,k=k) + if not ans: + return False + if k is None: + max_k = max(max_k, ans[0]) + ans = ans[1:] + order.extend(ans[0]) + mat.append(ans[1]) + return (max_k, order, block_diagonal_matrix(*mat,subdivide=False)) + # All that this function does is allocate/free the memory for function # bandwidth_C cdef int n = G.order() + cdef list int_to_vertex = G.vertices() cdef unsigned short ** d = malloc( n * sizeof(unsigned short *)) cdef unsigned short * distances = malloc( n*n * sizeof(unsigned short )) @@ -200,7 +243,6 @@ def bandwidth(G, k=None): cdef int i,j,kk all_pairs_shortest_path_BFS(G,NULL,distances,NULL) # compute the distance matrix - cdef list int_to_vertex = G.vertices() # fill d so that d[i][j] works for i in range(n): @@ -214,31 +256,39 @@ def bandwidth(G, k=None): for i in range(n): left_to_order[i] = i - if k is None: - for kk in range((n-1)//G.diameter(),n): - if bandwidth_C(n,kk,d,current,ordering,left_to_order,index_array_tmp,ith_range_array,range_array_tmp): - ans = True - break - else: - ans = bool(bandwidth_C(n,k,d,current,ordering,left_to_order,index_array_tmp,ith_range_array,range_array_tmp)) + try: + sig_on() + if k is None: + for kk in range((n-1)//G.diameter(),n): + if bandwidth_C(n,kk,d,current,ordering,left_to_order,index_array_tmp,ith_range_array,range_array_tmp): + ans = True + break + else: + ans = bool(bandwidth_C(n,k,d,current,ordering,left_to_order,index_array_tmp,ith_range_array,range_array_tmp)) + + if ans: + order = [int_to_vertex[ordering[i]] for i in range(n)] + + sig_off() + + finally: + free(d) + free(distances) + free(current) + free(ordering) + free(left_to_order) + free(index_array_tmp) + free(range_arrays) + free(ith_range_array) + free(range_array_tmp) if ans: from sage.matrix.constructor import Matrix - order = [int_to_vertex[ordering[i]] for i in range(n)] M = Matrix([[int(G.has_edge(u,v)) for u in order] for v in order]) assert all(abs(i-j)<= (kk if k is None else k) for i,j in M.dict()) ans = (kk, order, M) if k is None else (order,M) - free(d) - free(distances) - free(current) - free(ordering) - free(left_to_order) - free(index_array_tmp) - free(range_arrays) - free(ith_range_array) - free(range_array_tmp) return ans cdef bint bandwidth_C(int n, int k, From dd4636a9706689dee80d16e7e11a62763ef3fe56 Mon Sep 17 00:00:00 2001 From: Mario Pernici Date: Tue, 6 Jan 2015 11:26:15 +0100 Subject: [PATCH 122/217] fixed bug in ``permanental_minor_poly``; fixed bugs in ``rook_vector`` in the "Godsil" case; added tests. --- src/sage/matrix/matrix2.pyx | 8 ++++++-- src/sage/matrix/matrix_misc.py | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/sage/matrix/matrix2.pyx b/src/sage/matrix/matrix2.pyx index 7ccce6b6371..4cc72daf7fe 100644 --- a/src/sage/matrix/matrix2.pyx +++ b/src/sage/matrix/matrix2.pyx @@ -1049,6 +1049,10 @@ cdef class Matrix(matrix1.Matrix): [1, 0, 0] sage: matrix([[0,0],[0,0]]).rook_vector(algorithm="Godsil") [1, 0, 0] + sage: matrix.ones(4, 2).rook_vector("Ryser") + [1, 8, 12] + sage: matrix.ones(4, 2).rook_vector("Godsil") + [1, 8, 12] AUTHORS: @@ -1079,9 +1083,9 @@ cdef class Matrix(matrix1.Matrix): elif algorithm == "Godsil": from sage.graphs.bipartite_graph import BipartiteGraph g = BipartiteGraph(self) - p = g.matching_polynomial() + p = g.matching_polynomial(complement=False) d = p.degree() - return [p[i]*(-1)**((d - i)/2) for i in range(d,-1,-2)] + return [p[i]*(-1)**((d - i)/2) for i in range(d, d-2*mn-1, -2)] else: raise ValueError('algorithm must be one of "Ryser", "ButeraPernici" or "Godsil".') diff --git a/src/sage/matrix/matrix_misc.py b/src/sage/matrix/matrix_misc.py index 253ae26d70f..91576596c99 100644 --- a/src/sage/matrix/matrix_misc.py +++ b/src/sage/matrix/matrix_misc.py @@ -443,7 +443,7 @@ def permanental_minor_polynomial(A, permanent_only=False, var='t', prec=None): """ if permanent_only: prec = None - else: + elif prec is not None: prec = int(prec) if prec == 0: raise ValueError('the argument `prec` must be a positive integer') From 5212473c776529dca597c2bcc38efe15cbdb14b4 Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Tue, 6 Jan 2015 18:20:09 +0530 Subject: [PATCH 123/217] trac #17589: Small changes in the developer's manual table of contents --- src/doc/en/developer/coding_basics.rst | 41 ++++++++------------ src/doc/en/developer/git_trac.rst | 8 ++-- src/doc/en/developer/index.rst | 43 +++++++++++---------- src/doc/en/developer/reviewer_checklist.rst | 16 ++++---- src/doc/en/developer/sage_manuals.rst | 4 +- 5 files changed, 54 insertions(+), 58 deletions(-) diff --git a/src/doc/en/developer/coding_basics.rst b/src/doc/en/developer/coding_basics.rst index 60b6fa8f9b4..1f94ed4d092 100644 --- a/src/doc/en/developer/coding_basics.rst +++ b/src/doc/en/developer/coding_basics.rst @@ -152,8 +152,8 @@ Then in the file ``SAGE_ROOT/src/sage/all.py``, add a line :: from sage.measure_theory.all import * -An Example Is Worth a Thousand Words -==================================== +Learn by copy/paste +=================== For all of the conventions discussed here, you can find many examples in the Sage library. Browsing through the code is helpful, but so is @@ -492,8 +492,6 @@ there is not one already. That is, you can do the following:: sage: sage_getdoc(foo.__init__) # constructor docstring 'Construct a Foo.\n' - - .. _section-latex-typeset: LaTeX Typesetting @@ -585,6 +583,7 @@ which will appear interactively as "ZZ" while being typeset as See the file ``$SAGE_ROOT/src/sage/misc/latex_macros.py`` for a full list and for details about how to add more macros. +.. _section-doctest-writing: Writing Testable Examples ------------------------- @@ -643,6 +642,19 @@ When writing documentation, keep the following points in mind: sage: n.is_prime() False +**Randomized Testing and TestSuites** + +In addition to all the examples in your docstrings, which serve as +both demonstrations and tests of your code, you should consider +creating a test suite. Think of this as a program that will run for a +while and "tries" to crash your code using randomly generated +input. Your test code should define a class ``Test`` with a +``random()`` method that runs random tests. These are all assembled +together later, and each test is run for a certain amount of time on a +regular basis. + +For an example, see the file +``SAGE_ROOT/src/sage/modular/modsym/tests.py``. .. _section-further_conventions: @@ -664,8 +676,7 @@ framework. Here is a comprehensive list: However, most functions generating pseudorandom output do not need this tag since the doctesting framework guarantees the state of the pseudorandom number - generators (PRNGs) used in Sage for a given doctest (see - :ref:`chapter-randomtesting`). + generators (PRNGs) used in Sage for a given doctest. When possible, avoid the problem, e.g.: rather than checking the value of the hash in a doctest, one could illustrate successfully using it as a key in a @@ -923,24 +934,6 @@ see :func:`sage.structure.sage_object.register_unpickle_override` pickle jar should first be discussed on sage-devel. -.. _chapter-randomtesting: - -Randomized Testing -================== - -In addition to all the examples in your docstrings, which serve as -both demonstrations and tests of your code, you should consider -creating a test suite. Think of this as a program that will run for a -while and "tries" to crash your code using randomly generated -input. Your test code should define a class ``Test`` with a -``random()`` method that runs random tests. These are all assembled -together later, and each test is run for a certain amount of time on a -regular basis. - -For an example, see the file -``SAGE_ROOT/src/sage/modular/modsym/tests.py``. - - Global Options ============== diff --git a/src/doc/en/developer/git_trac.rst b/src/doc/en/developer/git_trac.rst index 7b7d4180927..5043f55c0de 100644 --- a/src/doc/en/developer/git_trac.rst +++ b/src/doc/en/developer/git_trac.rst @@ -126,8 +126,8 @@ then you can set up the remote by hand as described in the section on :ref:`section-git-trac`. -Trac Tickets to Local Branches -============================== +Trac Tickets and Git Branches +============================= Now let's start adding code to Sage! @@ -353,8 +353,8 @@ you need a new feature or if your branch conflicts. .. _section-git_trac-collaborate: -Collaboration -============= +Collaboration and conflict resolution +===================================== Exchanging Branches ------------------- diff --git a/src/doc/en/developer/index.rst b/src/doc/en/developer/index.rst index 9bc2381e7f8..7c4ba692803 100644 --- a/src/doc/en/developer/index.rst +++ b/src/doc/en/developer/index.rst @@ -1,4 +1,3 @@ - ====================================== Welcome to the Sage Developer's Guide! ====================================== @@ -55,8 +54,8 @@ development! - :ref:`How to install it ? ` - :ref:`How to configure it for use with Trac ? ` -Git and Trac -============ +Git for Sage development +======================== First Steps with Git -------------------- @@ -69,24 +68,13 @@ Sage uses git for version control. git_setup walk_through -Sage Trac and tickets ---------------------- - -All changes to Sage source code require a ticket on the -`Sage trac server `_. - -.. toctree:: - :maxdepth: 2 - - trac - The git-trac command -------------------- Putting your local changes on a Trac ticket. .. toctree:: - :maxdepth: 3 + :maxdepth: 2 git_trac @@ -98,29 +86,44 @@ Git Tricks & Tips When ``git trac`` is not enough. .. toctree:: - :maxdepth: 3 + :maxdepth: 2 manual_git git_background advanced_git workflows +Sage Trac and tickets +===================== + +All changes to Sage source code require a ticket on the +`Sage trac server `_. + +.. toctree:: + :maxdepth: 2 + + trac + .. _section-writing-code-for-sage: Writing Code for Sage ===================== -Basics of Writing and Testing Sage Code ---------------------------------------- - .. toctree:: :maxdepth: 3 coding_basics - doctesting reviewer_checklist +Running Sage's tests +-------------------- + +.. toctree:: + :maxdepth: 3 + + doctesting + Contributing to Manuals and Tutorials ------------------------------------- diff --git a/src/doc/en/developer/reviewer_checklist.rst b/src/doc/en/developer/reviewer_checklist.rst index e3d9925d22f..89d2feb1420 100644 --- a/src/doc/en/developer/reviewer_checklist.rst +++ b/src/doc/en/developer/reviewer_checklist.rst @@ -54,9 +54,9 @@ The following should generally be checked while reading and testing the code: `? :ref:`Python's convention `? :ref:`Cython's convention `? -- **Doctest coverage**: All code in Sage must have doctests. Use - ``sage -coverage `` to check that all functions of a file have - doctests. +- **Doctest coverage**: Do all functions contain doctests ? Use ``sage -coverage + `` to check it. Are all aspects of the new/modified methods and classes + tested (see :ref:`section-doctest-writing`) ? - **Bugfixes**: If the ticket contains a bugfix, does it add a doctest illustrating that the bug has been fixed? This new doctest should contain the @@ -69,11 +69,11 @@ The following should generally be checked while reading and testing the code: - **Manuals**: Does the reference manual build without errors (check both html and pdf)? See :ref:`chapter-sage_manuals` to learn how to build the manuals. -- **Doctests**: Do all doctests pass without errors? Unrelated components of - Sage may be affected by the change. Check all tests in the whole library, - including "long" doctests (this can be done with ``make ptestlong``) and - any optional doctests related to the functionality. See - :ref:`chapter-doctesting` for more information. +- **Run the tests**: Do all doctests pass without errors? Unrelated components + of Sage may be affected by the change. Check all tests in the whole library, + including "long" doctests (this can be done with ``make ptestlong``) and any + optional doctests related to the functionality. See :ref:`chapter-doctesting` + for more information. You are now ready to change the ticket's status (see :ref:`section-trac-ticket-status`): diff --git a/src/doc/en/developer/sage_manuals.rst b/src/doc/en/developer/sage_manuals.rst index f7efed22ae9..d4742d6b8e1 100644 --- a/src/doc/en/developer/sage_manuals.rst +++ b/src/doc/en/developer/sage_manuals.rst @@ -41,8 +41,8 @@ Here is a list of the Sage manuals and the corresponding files to edit: ``SAGE_ROOT/src/doc/fr/tutorial`` -Editing the Manuals -=================== +Building the Manuals +==================== If, for example, you want to change the Sage tutorial, then you should start by modifying the files in From 216eb972fcb5c224243db440fa0a43c1ec85498c Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Tue, 6 Jan 2015 20:41:42 +0530 Subject: [PATCH 124/217] trac #17592: rephrase "LaTeX formating" and "Writing doctests" --- src/doc/en/developer/coding_basics.rst | 203 +++++++++--------- .../vertex_separation.pyx | 1 - 2 files changed, 102 insertions(+), 102 deletions(-) diff --git a/src/doc/en/developer/coding_basics.rst b/src/doc/en/developer/coding_basics.rst index 1f94ed4d092..4797e982bba 100644 --- a/src/doc/en/developer/coding_basics.rst +++ b/src/doc/en/developer/coding_basics.rst @@ -213,8 +213,9 @@ compatible, that is, less restrictive license (e.g. the BSD license). Documentation Strings ===================== +.. _section-docstring-function: -Docstring Markup With ReST and Sphinx +The docstring of a function: content ------------------------------------- **Every** function must have a docstring that includes the following @@ -497,13 +498,14 @@ there is not one already. That is, you can do the following:: LaTeX Typesetting ----------------- -In ReST documentation, you use backticks \` to mark LaTeX code to be -typeset. In Sage docstrings, you may also use dollar signs instead. -Thus ```x^2 + y^2 = 1``` and ``$x^2 + y^2 = 1$`` should produce -identical output. If you use TeX commands containing backslashes in -docstrings, then either use double backslashes or place an "r" right -before the first triple opening quote. For example, both of the -following are valid:: +In ReST documentation LaTeX code is allowed, and is marked with **backticks or +dollar signs**: + + ```x^2 + y^2 = 1``` and ``$x^2 + y^2 = 1$`` both yield `x^2 + y^2 = 1` + +**Backslashes:** For LaTeX commands containing backslashes, either use double +backslashes or begin the docstring with a ``r"""`` instead of ``"""``. Both of +the following are valid:: def cos(x): """ @@ -515,8 +517,7 @@ following are valid:: Return $\sin(x)$. """ -You can also use the MATH block to format complicated mathematical -expressions:: +**MATH block:** It is similar to LaTeX' syntax ``$$$$``. For instance:: .. MATH:: @@ -524,9 +525,13 @@ expressions:: \leq e \sum_{i=1}^{\infty} a_i -Note that the MATH block is automatically wrapped in a latex math -environment (i.e. in ``\[ \]`` or ``$$``, etc.). To use aligned equations, -use the **aligned** environment:: +.. MATH:: + + \sum_{i=1}^{\infty} (a_1 a_2 \cdots a_i)^{1/i} + \leq + e \sum_{i=1}^{\infty} a_i + +The **aligned** environment works as it does in LaTeX:: .. MATH:: @@ -535,126 +540,122 @@ use the **aligned** environment:: g(x) & = x^x - f(x - 2) \end{aligned} -If you wish to explicitly not wrap the MATH block, make the first line of -the indented block ``:nowrap:``:: +.. MATH:: - .. MATH:: - :nowrap: + \begin{aligned} + f(x) & = x^2 - 1 \\ + g(x) & = x^x - f(x - 2) + \end{aligned} - This is now plain text so I can do things like $x = 5$. +**Readability balance:** in the interactive console, LaTeX formulas contained in +the documentation are represented by their LaTeX code (stripped from +backslashes). In this situation ``\\frac{a}{b}`` is less readable than ``a/b`` +or ``a b^{-1}`` (some users may not even know LaTeX code). Make it pleasant for +everybody as much as you can manage. -.. WARNING:: +**Commons rings** `(\Bold{Z},\Bold{N},...)`: The Sage LaTeX style is to typeset +standard rings and fields using the locally-defined macro ``\\Bold`` +(e.g. ``\\Bold{Z}`` gives `\Bold{Z}`). - With or without ``:nowrap:``, the *html* documentation output - currently will work if you use environments such as **align** - which wrap their contents in math mode. However, ``:nowrap:`` - is necessary for the *pdf* documentation to build correctly. - -The Sage LaTeX style is to typeset standard rings and fields like the -integers and the real numbers using the locally-defined macro -``\\Bold``, as in ``\\Bold{Z}`` for the integers. This macro is -defined to be ordinary bold-face ``\\mathbf`` by default, but users -can switch to blackboard-bold ``\\mathbb`` and back on-the-fly by -using ``latex.blackboard_bold(True)`` and -``latex.blackboard_bold(False)``. - -The docstring will be available interactively (for the "def point..." -example above, by typing "point?" at the "sage:" prompt) and also in -the reference manual. When viewed interactively, LaTeX code has the -backslashes stripped from it, so "\\cos" will appear as "cos". - -Because of the dual role of the docstring, you need to strike a -balance between readability (for interactive help) and using perfect -LaTeX code (for the reference manual). For instance, instead of using -"\\frac{a}{b}", use "a/b" or maybe "a b^{-1}". Also keep in mind that -some users of Sage are not familiar with LaTeX; this is another reason -to avoid complicated LaTeX expressions in docstrings, if at all -possible: "\\frac{a}{b}" will be obscure to someone who doesn't know -any LaTeX. - -Finally, a few non-standard LaTeX macros are available to help achieve -this balance, including "\\ZZ", "\\RR", "\\CC", and "\\QQ". These are -names of Sage rings, and they are typeset using a single boldface -character; they allow the use of "\\ZZ" in a docstring, for example, -which will appear interactively as "ZZ" while being typeset as -"\\Bold{Z}" in the reference manual. Other examples are "\\GF" and -"\\Zmod", each of which takes an argument: "\\GF{q}" is typeset as -"\\Bold{F}_{q}" and "\\Zmod{n}" is typeset as "\\Bold{Z}/n\\Bold{Z}". -See the file ``$SAGE_ROOT/src/sage/misc/latex_macros.py`` for a -full list and for details about how to add more macros. +**Shortcuts** are available which preserve readability, e.g. ``\\Z`` (`\\Z`), +``\\R`` (`\\R`), ``\\C`` (`\\C`), and ``\\Q`` (`\\Q`). They appear as +LaTeX-formatted ``\\Bold{Z}`` in the html manual, and as ``Z`` in the +interactive help. Other examples: ``\\GF_{q}`` (`\\GF_{q}`) and ``\\Zmod{p}`` +(`\Zmod{p}`). + +See the file ``$SAGE_ROOT/src/sage/misc/latex_macros.py`` for a full list and +for details about how to add more macros. .. _section-doctest-writing: Writing Testable Examples ------------------------- -The code in the examples should pass automatic testing. This means -that if the above code is in the file ``f.py`` (or ``f.sage``), then -``sage -t f.py`` should not give any error messages. Testing occurs -with full Sage preparsing of input within the standard Sage shell -environment, as described in :ref:`section-preparsing`. **Important:** -The file ``f.py`` is not imported when running tests unless you have -arranged that it be imported into your Sage environment, i.e. unless -its functions are available when you start Sage using the ``sage`` -command. For example, the function ``AA()`` in the file -``SAGE_ROOT/src/sage/algebras/steenrod/steenrod_algebra.py`` includes -an EXAMPLES block containing the following:: +The examples from Sage's documentation have a double purpose: + +- They provide **illustrations** of the code's usage to the users + +- They are **tests** that are checked before each release, helping us avoid the + apparition of new bugs. + +All new doctests added to Sage should **pass all tests** (see +:ref:`chapter-doctesting`), i.e. running ``sage -t your_file.py`` should not +give any error messages. Below are instructions about how doctests should be +written. + +.. centered:: **1) What doctests should test** + +- **Interesting examples** of what the function can do. This is what will be the + most meaningful to a lost user. It is also the occasion to check famous + theorems (just in case):: + + sage: is_prime(6) # 6 is not prime + False + sage: 2 * 3 # and here is a proof + 6 + +- All **meaningful combinations** of input arguments. For example a function may + accept an ``algorithm="B"`` argument, and doctests should involve both + ``algorithm="A"`` and ``algorithm="B"``. + +- **Corner cases:** the code should be able to handle a 0 input, or an empty + set, or a null matrix, or a null function, ... All corner cases should be + checked, as they are the most likely to be broken, now or in the future. This + probably belongs to the TESTS block (see :ref:`section-docstring-function`). + +- **Random of systematic tests** of all instances of small size, or of some + random instances if possible. + + .. NOTE:: + + Note that **TestSuites** are an automatic way to generate some of these + tests in specific situations. + ``SAGE_ROOT/src/sage/modular/modsym/tests.py``. + +.. centered:: **2) The syntax** + +- **Environment:** doctests should work if you copy/paste them in Sage's + interactive console. For example, the function ``AA()`` in the file + ``SAGE_ROOT/src/sage/algebras/steenrod/steenrod_algebra.py`` includes an + EXAMPLES block containing the following:: sage: from sage.algebras.steenrod.steenrod_algebra import AA as A sage: A() mod 2 Steenrod algebra, milnor basis -Sage does not know about the function ``AA()`` by default, so -it needs to be imported before it is tested. Hence the first line in -the example. + Sage does not know about the function ``AA()`` by default, so it needs to be + imported before it is tested. Hence the first line in the example. -When writing documentation, keep the following points in mind: +- **Preparsing:** As in Sage's console, `4/3` return `4/3` and not `1` as in + Python. Testing occurs with full Sage preparsing of input within the standard + Sage shell environment, as described in :ref:`section-preparsing`. -- All input is preparsed before being passed to Python, e.g. ``2/3`` - is replaced by ``Integer(2)/Integer(3)``, which evaluates to ``2/3`` - as a rational instead of the Python int ``0``. For more information - on preparsing, see :ref:`section-preparsing`. - -- If a test outputs to a file, the file should be a temporary file. - Use :func:`tmp_filename` to get a temporary filename, or - :func:`tmp_dir` to get a temporary directory. For example (taken - from the file ``SAGE_ROOT/src/sage/plot/graphics.py``):: +- **Writing files:** If a test outputs to a file, the file should be a temporary + file. Use :func:`tmp_filename` to get a temporary filename, or + :func:`tmp_dir` to get a temporary directory. An example from + ``SAGE_ROOT/src/sage/plot/graphics.py``):: sage: plot(x^2 - 5, (x, 0, 5), ymin=0).save(tmp_filename(ext='.png')) -- You may write tests that span multiple lines. The best way to do so - is to use the line continuation marker ``....:`` :: +- **Multiline doctests:** You may write tests that span multiple lines, using + the line continuation marker ``....:`` :: sage: for n in srange(1,10): ....: if n.is_prime(): ....: print n, 2 3 5 7 - If you have a long line of code, you may want to consider adding a - backslash to the end of the line, which tells the doctesting - framework to join that current line with the next. This syntax is - non-standard so may be removed in a future version of Sage, but in - the mean time it can be useful for breaking up large integers across - multiple lines:: +- **Split long lines:** You may want to split long lines of code with a + backslash. Note: this syntax is non-standard and may be removed in the + future:: sage: n = 123456789123456789123456789\ ....: 123456789123456789123456789 sage: n.is_prime() False -**Randomized Testing and TestSuites** - -In addition to all the examples in your docstrings, which serve as -both demonstrations and tests of your code, you should consider -creating a test suite. Think of this as a program that will run for a -while and "tries" to crash your code using randomly generated -input. Your test code should define a class ``Test`` with a -``random()`` method that runs random tests. These are all assembled -together later, and each test is run for a certain amount of time on a -regular basis. - -For an example, see the file -``SAGE_ROOT/src/sage/modular/modsym/tests.py``. +- **Doctests flags:** flags are available to change the behaviour of doctests: + see :ref:`section-further_conventions`. .. _section-further_conventions: diff --git a/src/sage/graphs/graph_decompositions/vertex_separation.pyx b/src/sage/graphs/graph_decompositions/vertex_separation.pyx index d1efc0ecbbd..3a91a62a135 100644 --- a/src/sage/graphs/graph_decompositions/vertex_separation.pyx +++ b/src/sage/graphs/graph_decompositions/vertex_separation.pyx @@ -146,7 +146,6 @@ sets such that an ordering `v_1, ..., v_n` of the vertices correspond to **MILP formulation:** .. MATH:: - :nowrap: \begin{alignat}{2} \text{Minimize:} From 2c40f5eb5d5ff3e10c415fbc33778fcb8002a47b Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Tue, 6 Jan 2015 17:20:08 +0100 Subject: [PATCH 125/217] Remove sqlalchemy --- COPYING.txt | 1 - build/deps | 4 -- build/install | 1 - build/pkgs/sagenb/SPKG.txt | 1 - build/pkgs/sqlalchemy/SPKG.txt | 51 ----------------------- build/pkgs/sqlalchemy/checksums.ini | 4 -- build/pkgs/sqlalchemy/package-version.txt | 1 - build/pkgs/sqlalchemy/spkg-check | 16 ------- build/pkgs/sqlalchemy/spkg-install | 14 ------- src/sage/databases/sql_db.py | 1 - src/sage/tests/cmdline.py | 10 +++-- 11 files changed, 6 insertions(+), 98 deletions(-) delete mode 100644 build/pkgs/sqlalchemy/SPKG.txt delete mode 100644 build/pkgs/sqlalchemy/checksums.ini delete mode 100644 build/pkgs/sqlalchemy/package-version.txt delete mode 100755 build/pkgs/sqlalchemy/spkg-check delete mode 100755 build/pkgs/sqlalchemy/spkg-install diff --git a/COPYING.txt b/COPYING.txt index 66c2e7fb438..c74fb76301b 100644 --- a/COPYING.txt +++ b/COPYING.txt @@ -117,7 +117,6 @@ setuptools Python License singular GPLv2 or GPLv3 (see below) six MIT License sphinx Modified BSD -sqlalchemy MIT License sqlite Public Domain (see below) symmetrica MIT-like License (see below) sympow Modified BSD diff --git a/build/deps b/build/deps index afb6f3c9947..2f8ec730c06 100644 --- a/build/deps +++ b/build/deps @@ -104,7 +104,6 @@ all-sage: \ $(INST)/$(SINGULAR) \ $(INST)/$(SIX) \ $(INST)/$(SPHINX) \ - $(INST)/$(SQLALCHEMY) \ $(INST)/$(SQLITE) \ $(INST)/$(SYMMETRICA) \ $(INST)/$(SYMPOW) \ @@ -447,9 +446,6 @@ $(INST)/$(SAGENB): $(INST)/$(PYTHON) $(INST)/$(SETUPTOOLS) $(INST)/$(PEXPECT) \ $(INST)/$(JINJA2) $(INST)/$(SPHINX) $(INST)/$(DOCUTILS) +$(PIPE) "$(SAGE_SPKG) $(SAGENB) 2>&1" "tee -a $(SAGE_LOGS)/$(SAGENB).log" -$(INST)/$(SQLALCHEMY): $(INST)/$(PYTHON) $(INST)/$(SETUPTOOLS) - +$(PIPE) "$(SAGE_SPKG) $(SQLALCHEMY) 2>&1" "tee -a $(SAGE_LOGS)/$(SQLALCHEMY).log" - $(INST)/$(SPHINX): $(INST)/$(PYTHON) $(INST)/$(SETUPTOOLS) $(INST)/$(DOCUTILS) \ $(INST)/$(JINJA2) $(INST)/$(PYGMENTS) +$(PIPE) "$(SAGE_SPKG) $(SPHINX) 2>&1" "tee -a $(SAGE_LOGS)/$(SPHINX).log" diff --git a/build/install b/build/install index 03be7506189..546f21fcea4 100755 --- a/build/install +++ b/build/install @@ -347,7 +347,6 @@ SETUPTOOLS=`newest_version setuptools` SINGULAR=`newest_version singular` SIX=`newest_version six` SPHINX=`newest_version sphinx` -SQLALCHEMY=`newest_version sqlalchemy` SQLITE=`newest_version sqlite` SYMMETRICA=`newest_version symmetrica` SYMPOW=`newest_version sympow` diff --git a/build/pkgs/sagenb/SPKG.txt b/build/pkgs/sagenb/SPKG.txt index 6b85f8cada8..4402326bcb2 100644 --- a/build/pkgs/sagenb/SPKG.txt +++ b/build/pkgs/sagenb/SPKG.txt @@ -47,7 +47,6 @@ Sage: * Python * setuptools - * SQLAlchemy * pexpect * jinja2 * sphinx diff --git a/build/pkgs/sqlalchemy/SPKG.txt b/build/pkgs/sqlalchemy/SPKG.txt deleted file mode 100644 index 1c251cfecb9..00000000000 --- a/build/pkgs/sqlalchemy/SPKG.txt +++ /dev/null @@ -1,51 +0,0 @@ -= SQLAlchemy = - -== Description == - -SQLAlchemy is the Python SQL toolkit and Object Relational Mapper that gives application developers the full power and flexibility of SQL. - -It provides a full suite of well known enterprise-level persistence patterns, designed for efficient and high-performing database access, adapted into a simple and Pythonic domain language. - -Website: http://www.sqlalchemy.org/ - -== License == - -MIT - -== SPKG Maintainers == - - * Mike Hansen - * Mitesh Patel - -== Upstream Contact == - - * http://www.sqlalchemy.org/community.html - -== Dependencies == - * python - * setuptools - -== Build Notes == - -Use the standard sources. No changes from the official sources. - -== Changelog == - -=== SQLAlchemy-0.5.8.p0 (Mitesh Patel, March 4, 2010) === - * Upgrade to 0.5.8. - * Tweak removal of previous versions. - * Disable tests in spkg-check, since they now require nose. - -=== SQLAlchemy-0.4.6.p0 (Yi Qiang, June 25, 2008) === - * version bump - * make sure to delete the old SQLAlchemy install directories (Michael Abshoff) - -=== SQLAlchemy-0.4.3.p1 (Michael Abshoff, March 14th, 2008) === - * add hg repo - * add .hgignore - * make sure SAGE_LOCAL is installed (#633) - * use /usr/bin/env bash shebang (#1638) - -=== SQLAlchemy-0.4.3.p0 (Yi Qiang) === - * Initial version - diff --git a/build/pkgs/sqlalchemy/checksums.ini b/build/pkgs/sqlalchemy/checksums.ini deleted file mode 100644 index 291b1c5a95c..00000000000 --- a/build/pkgs/sqlalchemy/checksums.ini +++ /dev/null @@ -1,4 +0,0 @@ -tarball=sqlalchemy-VERSION.tar.bz2 -sha1=dcc1b0b997be96342817f108e45005c260e940e7 -md5=d646dae454133c232d448c73cebd9a2b -cksum=33626186 diff --git a/build/pkgs/sqlalchemy/package-version.txt b/build/pkgs/sqlalchemy/package-version.txt deleted file mode 100644 index 659914ae941..00000000000 --- a/build/pkgs/sqlalchemy/package-version.txt +++ /dev/null @@ -1 +0,0 @@ -0.5.8 diff --git a/build/pkgs/sqlalchemy/spkg-check b/build/pkgs/sqlalchemy/spkg-check deleted file mode 100755 index 90af2a30d11..00000000000 --- a/build/pkgs/sqlalchemy/spkg-check +++ /dev/null @@ -1,16 +0,0 @@ -#!/usr/bin/env bash - -if [ "$SAGE_LOCAL" = "" ]; then - echo "SAGE_LOCAL undefined ... exiting"; - echo "Maybe run 'sage -sh'?" - exit 1 -fi - -# Run the unittests. The tests now require nose. -#cd src -#python test/alltests.py - -if [ $? -ne 0 ]; then - echo "There was a problem during the SQLAlchemy unit tests" - exit 1 -fi diff --git a/build/pkgs/sqlalchemy/spkg-install b/build/pkgs/sqlalchemy/spkg-install deleted file mode 100755 index 9aed0fba70d..00000000000 --- a/build/pkgs/sqlalchemy/spkg-install +++ /dev/null @@ -1,14 +0,0 @@ -#!/usr/bin/env bash - -if [ "$SAGE_LOCAL" = "" ]; then - echo "SAGE_LOCAL undefined ... exiting"; - echo "Maybe run 'sage -sh'?" - exit 1 -fi - -echo "Deleting old SQLAlchemy installs" -rm -rf "$SAGE_LOCAL/lib/python/site-packages/SQLAlchemy*" - -# Install SQLAlchemy -cd src -python setup.py install diff --git a/src/sage/databases/sql_db.py b/src/sage/databases/sql_db.py index 3b1e4eaa809..601ea22e92e 100644 --- a/src/sage/databases/sql_db.py +++ b/src/sage/databases/sql_db.py @@ -63,7 +63,6 @@ # - order by clause in query strings # - delete from query containing joins # - add data by column -# - wrap sqlalchemy # - create query interface (with interact) # - allow kwds arguments to SQLQuery (like GraphQuery) diff --git a/src/sage/tests/cmdline.py b/src/sage/tests/cmdline.py index 6c7f9fa9372..8ecb0c00fe6 100644 --- a/src/sage/tests/cmdline.py +++ b/src/sage/tests/cmdline.py @@ -202,12 +202,14 @@ def test_executable(args, input="", timeout=100.0, **kwds): Test ``sage --info [packages]``, unless this is a binary (bdist) distribution which doesn't ship spkgs:: - sage: out, err, ret = test_executable(["sage", "--info", "sqlalchemy"]) + sage: out, err, ret = test_executable(["sage", "--info", "sqlite"]) sage: print out - Found local metadata for sqlalchemy-... - = SQLAlchemy = + Found local metadata for sqlite-... + = SQLite = + ... + SQLite is a software library that implements a self-contained, + serverless, zero-configuration, transactional SQL database engine. ... - SQLAlchemy is the Python SQL toolkit... sage: err '' sage: ret From 3b682e2a637b58323979f8fca2f97e470a4c2436 Mon Sep 17 00:00:00 2001 From: Ralf Stephan Date: Tue, 6 Jan 2015 17:32:21 +0100 Subject: [PATCH 126/217] 16201: misc.defauts doctests; deprecate LaurentSeriesRing.set_default_prec; cosmetics --- src/sage/misc/defaults.py | 12 ++++++++++++ src/sage/rings/laurent_series_ring.py | 15 ++++++++++----- src/sage/symbolic/expression.pyx | 9 ++++++--- 3 files changed, 28 insertions(+), 8 deletions(-) diff --git a/src/sage/misc/defaults.py b/src/sage/misc/defaults.py index a842a1982db..ec48cd612fd 100644 --- a/src/sage/misc/defaults.py +++ b/src/sage/misc/defaults.py @@ -57,6 +57,11 @@ def series_precision(): """ Return the Sage-wide precision for series (symbolic, power series, Laurent series). + + EXAMPLES:: + + sage: series_precision() + 20 """ return series_prec @@ -64,6 +69,13 @@ def set_series_precision(prec): """ Change the Sage-wide precision for series (symbolic, power series, Laurent series). + + EXAMPLES:: + + sage: set_series_precision(5) + sage: series_precision() + 5 + sage: set_series_precision(20) """ global series_prec series_prec = prec \ No newline at end of file diff --git a/src/sage/rings/laurent_series_ring.py b/src/sage/rings/laurent_series_ring.py index d4ced6964c4..f9c7a56ab00 100644 --- a/src/sage/rings/laurent_series_ring.py +++ b/src/sage/rings/laurent_series_ring.py @@ -307,6 +307,8 @@ def _element_constructor_(self, x, n=0): sage: L. = LaurentSeriesRing(QQ) sage: L.set_default_prec(10) + doctest:...: DeprecationWarning: This method is deprecated. + See http://trac.sagemath.org/16201 for details. sage: L(pari('1/x')) q^-1 sage: L(pari('poltchebi(5)')) @@ -510,24 +512,27 @@ def residue_field(self): def set_default_prec(self, n): """ - Sets the default precision. + Set the default precision. - This operation should be discouraged: parents should be - immutable and this function may be deprecated in the future. + This method is deprecated. TESTS:: sage: R. = LaurentSeriesRing(QQ) sage: R.set_default_prec(3) + doctest:...: DeprecationWarning: This method is deprecated. + See http://trac.sagemath.org/16201 for details. sage: 1/(x^5-x^7) x^-5 + x^-3 + O(x^-2) """ + from sage.misc.superseded import deprecation + deprecation(16201, "This method is deprecated.") self.power_series_ring().set_default_prec(n) def default_prec(self): """ - Sets the precision to which exact elements are truncated when - necessary (most frequently when inverting) + Get the precision to which exact elements are truncated when + necessary (most frequently when inverting). EXAMPLES:: diff --git a/src/sage/symbolic/expression.pyx b/src/sage/symbolic/expression.pyx index 03c5d87052a..e52acf44dfd 100644 --- a/src/sage/symbolic/expression.pyx +++ b/src/sage/symbolic/expression.pyx @@ -3492,7 +3492,7 @@ cdef class Expression(CommutativeRingElement): for g in self.gradient()]) - def series(self, symbol, int order=-65535): + def series(self, symbol, order=None): r""" Return the power series expansion of self in terms of the given variable to the given order. @@ -3573,9 +3573,12 @@ cdef class Expression(CommutativeRingElement): """ cdef Expression symbol0 = self.coerce_in(symbol) cdef GEx x - if order == -65535: + cdef int prec + if order is None: from sage.misc.defaults import series_precision - order = series_precision() + prec = series_precision() + else: + prec = order sig_on() try: x = self._gobj.series(symbol0._gobj, order, 0) From 467d02db83994ac80d4edfebfbece9c9aae2bcdc Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Tue, 6 Jan 2015 22:08:07 +0530 Subject: [PATCH 127/217] trac #17592: Review --- src/doc/en/developer/coding_basics.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/doc/en/developer/coding_basics.rst b/src/doc/en/developer/coding_basics.rst index 4797e982bba..4328c8e5386 100644 --- a/src/doc/en/developer/coding_basics.rst +++ b/src/doc/en/developer/coding_basics.rst @@ -498,7 +498,7 @@ there is not one already. That is, you can do the following:: LaTeX Typesetting ----------------- -In ReST documentation LaTeX code is allowed, and is marked with **backticks or +In Sage's documentation LaTeX code is allowed, and is marked with **backticks or dollar signs**: ```x^2 + y^2 = 1``` and ``$x^2 + y^2 = 1$`` both yield `x^2 + y^2 = 1` @@ -517,7 +517,8 @@ the following are valid:: Return $\sin(x)$. """ -**MATH block:** It is similar to LaTeX' syntax ``$$$$``. For instance:: +**MATH block:** It is similar to LaTeX' syntax ``\[\]`` (or +``$$$$``). For instance:: .. MATH:: From 6a2a4194483f2bdbd370ec441a3fc87a20a29695 Mon Sep 17 00:00:00 2001 From: "John H. Palmieri" Date: Tue, 6 Jan 2015 12:13:53 -0800 Subject: [PATCH 128/217] #17589: trivial fixes --- src/doc/en/developer/coding_basics.rst | 2 +- src/doc/en/developer/index.rst | 6 +++--- src/doc/en/developer/reviewer_checklist.rst | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/doc/en/developer/coding_basics.rst b/src/doc/en/developer/coding_basics.rst index 1f94ed4d092..122aa8f84c8 100644 --- a/src/doc/en/developer/coding_basics.rst +++ b/src/doc/en/developer/coding_basics.rst @@ -654,7 +654,7 @@ together later, and each test is run for a certain amount of time on a regular basis. For an example, see the file -``SAGE_ROOT/src/sage/modular/modsym/tests.py``. +:file:`SAGE_ROOT/src/sage/modular/modsym/tests.py`. .. _section-further_conventions: diff --git a/src/doc/en/developer/index.rst b/src/doc/en/developer/index.rst index 7c4ba692803..500f47528b6 100644 --- a/src/doc/en/developer/index.rst +++ b/src/doc/en/developer/index.rst @@ -50,9 +50,9 @@ development! purpose. - :ref:`Here is ` an overview of our development flow. - - :ref:`Unfamiliar with Git or revision control ? ` - - :ref:`How to install it ? ` - - :ref:`How to configure it for use with Trac ? ` + - :ref:`Unfamiliar with Git or revision control? ` + - :ref:`How to install it? ` + - :ref:`How to configure it for use with Trac? ` Git for Sage development ======================== diff --git a/src/doc/en/developer/reviewer_checklist.rst b/src/doc/en/developer/reviewer_checklist.rst index 89d2feb1420..8c77cc16483 100644 --- a/src/doc/en/developer/reviewer_checklist.rst +++ b/src/doc/en/developer/reviewer_checklist.rst @@ -54,9 +54,9 @@ The following should generally be checked while reading and testing the code: `? :ref:`Python's convention `? :ref:`Cython's convention `? -- **Doctest coverage**: Do all functions contain doctests ? Use ``sage -coverage +- **Doctest coverage**: Do all functions contain doctests? Use ``sage -coverage `` to check it. Are all aspects of the new/modified methods and classes - tested (see :ref:`section-doctest-writing`) ? + tested (see :ref:`section-doctest-writing`)? - **Bugfixes**: If the ticket contains a bugfix, does it add a doctest illustrating that the bug has been fixed? This new doctest should contain the From 84a8b233a6602a644eb63541d4846aa832e2d22b Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Wed, 7 Jan 2015 09:16:41 +0530 Subject: [PATCH 129/217] trac #17592: :nowrap: again --- src/doc/en/developer/coding_basics.rst | 19 +++++++++++++++++++ .../vertex_separation.pyx | 1 + 2 files changed, 20 insertions(+) diff --git a/src/doc/en/developer/coding_basics.rst b/src/doc/en/developer/coding_basics.rst index 4328c8e5386..abeb127a286 100644 --- a/src/doc/en/developer/coding_basics.rst +++ b/src/doc/en/developer/coding_basics.rst @@ -548,6 +548,25 @@ The **aligned** environment works as it does in LaTeX:: g(x) & = x^x - f(x - 2) \end{aligned} +For **non-math** LaTeX environments (like ``align``), the pdf documentation +will not compile unless you add a **:nowrap:** flag to the MATH mode:: + + .. MATH:: + :nowrap: + + \begin{align} + 1+...+n &= n(n+1)/2\\ + &= O(n^2)\\ + \end{tabular} + +.. MATH:: + :nowrap: + + \begin{align} + 1+...+n &= n(n+1)/2\\ + &= O(n^2)\\ + \end{align} + **Readability balance:** in the interactive console, LaTeX formulas contained in the documentation are represented by their LaTeX code (stripped from backslashes). In this situation ``\\frac{a}{b}`` is less readable than ``a/b`` diff --git a/src/sage/graphs/graph_decompositions/vertex_separation.pyx b/src/sage/graphs/graph_decompositions/vertex_separation.pyx index 3a91a62a135..d1efc0ecbbd 100644 --- a/src/sage/graphs/graph_decompositions/vertex_separation.pyx +++ b/src/sage/graphs/graph_decompositions/vertex_separation.pyx @@ -146,6 +146,7 @@ sets such that an ordering `v_1, ..., v_n` of the vertices correspond to **MILP formulation:** .. MATH:: + :nowrap: \begin{alignat}{2} \text{Minimize:} From e3d7bbd479838b2cbc8433509034da0ab864d456 Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Tue, 6 Jan 2015 15:53:06 +0100 Subject: [PATCH 130/217] Refactor stacking, use coercion --- src/sage/matrix/matrix1.pxd | 2 +- src/sage/matrix/matrix1.pyx | 168 ++++++++++++++++------- src/sage/matrix/matrix_integer_dense.pyx | 84 +++--------- src/sage/matrix/matrix_mod2_dense.pyx | 30 ++-- src/sage/matrix/matrix_sparse.pyx | 55 +++----- src/sage/modules/diamond_cutting.py | 2 +- src/sage/modules/fg_pid/fgp_morphism.py | 1 + 7 files changed, 159 insertions(+), 183 deletions(-) diff --git a/src/sage/matrix/matrix1.pxd b/src/sage/matrix/matrix1.pxd index a74cae197ef..8d0545f47f7 100644 --- a/src/sage/matrix/matrix1.pxd +++ b/src/sage/matrix/matrix1.pxd @@ -1,4 +1,4 @@ cimport matrix0 cdef class Matrix(matrix0.Matrix): - pass + cdef _stack_impl(self, bottom) diff --git a/src/sage/matrix/matrix1.pyx b/src/sage/matrix/matrix1.pyx index eb42a18cc9e..1ae2d24c103 100644 --- a/src/sage/matrix/matrix1.pyx +++ b/src/sage/matrix/matrix1.pyx @@ -23,6 +23,7 @@ include "sage/ext/python.pxi" import sage.modules.free_module + cdef class Matrix(matrix0.Matrix): ################################################### # Coercion to Various Systems @@ -1116,8 +1117,10 @@ cdef class Matrix(matrix0.Matrix): ########################################################################### def stack(self, bottom, subdivide=False): r""" - Returns a new matrix formed by appending the matrix - (or vector) ``bottom`` beneath ``self``. + Return the matrix ``self`` on top of ``bottom``:: + + [ self ] + [ bottom ] INPUT: @@ -1142,6 +1145,7 @@ cdef class Matrix(matrix0.Matrix): in the result. .. warning:: + If ``subdivide`` is ``True`` then unequal column subdivisions will be discarded, since it would be ambiguous how to interpret them. If the subdivision behavior is not what you need, @@ -1187,13 +1191,13 @@ cdef class Matrix(matrix0.Matrix): sage: A.stack(B) Traceback (most recent call last): ... - TypeError: number of columns must be the same, 2 != 3 + TypeError: number of columns must be the same, not 2 and 3 sage: v = vector(RR, [100, 200, 300]) sage: A.stack(v) Traceback (most recent call last): ... - TypeError: number of columns must be the same, 2 != 3 + TypeError: number of columns must be the same, not 2 and 3 Setting ``subdivide`` to ``True`` will, in its simplest form, add a subdivision between ``self`` and ``bottom``. :: @@ -1251,16 +1255,17 @@ cdef class Matrix(matrix0.Matrix): [ 5 6 7 8 9] [10 11 12 13 14] - The result retains the base ring of ``self`` by coercing - the elements of ``bottom`` into the base ring of ``self``. :: + The base ring of the result is the common parent for the base + rings of ``self`` and ``bottom``. In particular, the parent for + ``A.stack(B)`` and ``B.stack(A)`` should be equal:: sage: A = matrix(QQ, 1, 2, [1,2]) sage: B = matrix(RR, 1, 2, [sin(1.1), sin(2.2)]) sage: C = A.stack(B); C - [ 1 2] - [183017397/205358938 106580492/131825561] + [ 1.00000000000000 2.00000000000000] + [0.891207360061435 0.808496403819590] sage: C.parent() - Full MatrixSpace of 2 by 2 dense matrices over Rational Field + Full MatrixSpace of 2 by 2 dense matrices over Real Field with 53 bits of precision sage: D = B.stack(A); D [0.891207360061435 0.808496403819590] @@ -1268,32 +1273,52 @@ cdef class Matrix(matrix0.Matrix): sage: D.parent() Full MatrixSpace of 2 by 2 dense matrices over Real Field with 53 bits of precision - Sometimes it is not possible to coerce into the base ring - of ``self``. A solution is to change the base ring of ``self`` - to a more expansive ring. Here we mix the rationals with - a ring of polynomials with rational coefficients. :: + :: - sage: R = PolynomialRing(QQ, 'y') - sage: A = matrix(QQ, 1, 2, [1,2]) - sage: B = matrix(R, 1, 2, ['y', 'y^2']) + sage: R. = PolynomialRing(ZZ) + sage: A = matrix(QQ, 1, 2, [1, 2/3]) + sage: B = matrix(R, 1, 2, [y, y^2]) - sage: C = B.stack(A); C + sage: C = A.stack(B); C + [ 1 2/3] [ y y^2] - [ 1 2] sage: C.parent() Full MatrixSpace of 2 by 2 dense matrices over Univariate Polynomial Ring in y over Rational Field - sage: D = A.stack(B) - Traceback (most recent call last): - ... - TypeError: not a constant polynomial + Stacking a dense matrix atop a sparse one returns a sparse + matrix:: - sage: E = A.change_ring(R) - sage: F = E.stack(B); F - [ 1 2] - [ y y^2] - sage: F.parent() - Full MatrixSpace of 2 by 2 dense matrices over Univariate Polynomial Ring in y over Rational Field + sage: M = Matrix(ZZ, 2, 3, range(6), sparse=False) + sage: N = diagonal_matrix([10,11,12], sparse=True) + sage: P = M.stack(N); P + [ 0 1 2] + [ 3 4 5] + [10 0 0] + [ 0 11 0] + [ 0 0 12] + sage: P.is_sparse() + True + sage: P = N.stack(M); P + [10 0 0] + [ 0 11 0] + [ 0 0 12] + [ 0 1 2] + [ 3 4 5] + sage: P.is_sparse() + True + + One can stack matrices over different rings (:trac:`16399`). :: + + sage: M = Matrix(ZZ, 2, 3, range(6)) + sage: N = Matrix(QQ, 1, 3, [10,11,12]) + sage: M.stack(N) + [ 0 1 2] + [ 3 4 5] + [10 11 12] + sage: N.stack(M) + [10 11 12] + [ 0 1 2] + [ 3 4 5] TESTS: @@ -1306,43 +1331,80 @@ cdef class Matrix(matrix0.Matrix): [ 3 4 5] [10 11 12] - AUTHOR: + Non-matrices fail gracefully:: - - Rob Beezer (2011-03-19) - rewritten to mirror code for :meth:`augment` - """ - from sage.matrix.constructor import matrix + sage: M.stack(polygen(QQ)) + Traceback (most recent call last): + ... + TypeError: a matrix must be stacked with another matrix or a vector + + AUTHORS: - if hasattr(bottom, '_vector_'): - bottom = bottom.row() - if not isinstance(bottom, sage.matrix.matrix1.Matrix): - raise TypeError('a matrix must be stacked with another matrix, or ' - 'a vector') + - Rob Beezer (2011-03-19): rewritten to mirror code for :meth:`augment` + - Jeroen Demeyer (2015-01-06): refactor, see :trac:`16399`. + Put all boilerplate in one place (here) and put the actual + type-dependent implementation in ``_stack_impl``. + """ cdef Matrix other - other = bottom + if isinstance(bottom, Matrix): + other = bottom + else: + if hasattr(bottom, '_vector_'): + bottom = bottom.row() + else: + raise TypeError('a matrix must be stacked with ' + 'another matrix or a vector') + other = bottom if self._ncols != other._ncols: - raise TypeError('number of columns must be the same, ' - '{0} != {1}'.format(self._ncols, other._ncols)) - if not (self._base_ring is other.base_ring()): - other = other.change_ring(self._base_ring) + raise TypeError("number of columns must be the same, not %s and %s" % + (self.ncols(), bottom.ncols()) ) + + top_ring = self._base_ring + bottom_ring = other._base_ring + if top_ring is not bottom_ring: + from sage.structure.element import get_coercion_model + coercion_model = get_coercion_model() + R = coercion_model.common_parent(top_ring, bottom_ring) + if top_ring is not R: + self = self.change_ring(R) + if bottom_ring is not R: + other = other.change_ring(R) + + if type(self) is not type(other): + # If one of the matrices is sparse, return a sparse matrix + if self.is_sparse_c() and not other.is_sparse_c(): + other = other.sparse_matrix() + elif other.is_sparse_c() and not self.is_sparse_c(): + self = self.sparse_matrix() + + Z = self._stack_impl(other) + if subdivide: + Z._subdivide_on_stack(self, other) + return Z + cdef _stack_impl(self, bottom): + """ + Implementation of :meth:`stack`. + + Assume that ``self`` and ``other`` are compatible in the sense + that they have the same base ring and that both are either + dense or sparse. + """ + cdef Matrix other = bottom cdef Matrix Z - Z = self.new_matrix(nrows = self._nrows + other._nrows) + Z = self.new_matrix(nrows=self._nrows + other._nrows, ncols=self._ncols) cdef Py_ssize_t r, c - for r from 0 <= r < self._nrows: - for c from 0 <= c < self._ncols: - Z.set_unsafe(r,c, self.get_unsafe(r,c)) - nr = self.nrows() - - for r from 0 <= r < other._nrows: - for c from 0 <= c < other._ncols: + cdef Py_ssize_t nr = self._nrows + for r in range(self._nrows): + for c in range(self._ncols): + Z.set_unsafe(r, c, self.get_unsafe(r,c)) + for r in range(other._nrows): + for c in range(other._ncols): Z.set_unsafe(r+nr, c, other.get_unsafe(r,c)) - if subdivide: - Z._subdivide_on_stack(self, other) - return Z def augment(self, right, subdivide=False): diff --git a/src/sage/matrix/matrix_integer_dense.pyx b/src/sage/matrix/matrix_integer_dense.pyx index a352e96638d..c4d6104f2d0 100644 --- a/src/sage/matrix/matrix_integer_dense.pyx +++ b/src/sage/matrix/matrix_integer_dense.pyx @@ -95,7 +95,7 @@ from sage.rings.integer_ring cimport IntegerRing_class from sage.rings.finite_rings.integer_mod_ring import IntegerModRing from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing from sage.rings.polynomial.polynomial_integer_dense_flint cimport Polynomial_integer_dense_flint -from sage.structure.element cimport ModuleElement, RingElement, Element, Vector, CoercionModel +from sage.structure.element cimport ModuleElement, RingElement, Element, Vector from sage.structure.element import is_Vector from sage.structure.sequence import Sequence @@ -4679,9 +4679,9 @@ cdef class Matrix_integer_dense(matrix_dense.Matrix_dense): # dense or sparse ################################################################# # operations with matrices ################################################################# - def stack(self, bottom, subdivide=False): + cdef _stack_impl(self, bottom): r""" - Return the matrix ``self`` on top of ``bottom``: + Return the matrix ``self`` on top of ``bottom``:: [ self ] [ bottom ] @@ -4718,73 +4718,21 @@ cdef class Matrix_integer_dense(matrix_dense.Matrix_dense): # dense or sparse [-----------] [ 0 1 2 3] [ 4 5 6 7] + """ + cdef Matrix_integer_dense other = bottom + cdef Matrix_integer_dense Z + Z = self.new_matrix(nrows=self._nrows + other._nrows, ncols=self._ncols) - TESTS: - - Stacking a dense matrix atop a sparse one should work:: - - sage: M = Matrix(ZZ, 2, 3, range(6)) - sage: M.is_sparse() - False - sage: N = diagonal_matrix([10,11,12], sparse=True) - sage: N.is_sparse() - True - sage: P = M.stack(N); P - [ 0 1 2] - [ 3 4 5] - [10 0 0] - [ 0 11 0] - [ 0 0 12] - sage: P.is_sparse() - False - - One can stack matrices over different rings (:trac:`16399`). :: - - sage: M = Matrix(ZZ, 2, 3, range(6)) - sage: N = Matrix(QQ, 1, 3, [10,11,12]) - sage: M.stack(N) - [0 1 2 3 4] - [5 6 7 8 9] - [0 1 2 3 4] - sage: N.stack(M) - ? - sage: M2 = Matrix(ZZ['x'], 2, 3, range(6)) - sage: N.stack(M2) - ? - """ - if hasattr(bottom, '_vector_'): - bottom = bottom.row() - if self._ncols != bottom.ncols(): - raise TypeError("number of columns must be the same") - cdef CoercionModel coercion_model - - top_ring = self._base_ring - bottom_ring = bottom.base_ring() - if not (top_ring is bottom_ring): - if top_ring.has_coerce_map_from(bottom_ring): - bottom = bottom.change_ring(top_ring) - elif bottom_ring.has_coerce_map_from(top_ring): - new_top = self.change_ring(bottom_ring) - return new_top.stack(bottom, subdivide=subdivide): - else: - from sage.structure.element import get_coercion_model - coercion_model = get_coercion_model() - com_ring = coercion_model.common_parent(top_ring, bottom_ring) - self = self.change_ring(com_ring) # allowed ? - bottom = other.change_ring(com_ring) + cdef Py_ssize_t r, c + cdef Py_ssize_t nr = self._nrows + for r in range(self._nrows): + for c in range(self._ncols): + fmpz_set(fmpz_mat_entry(Z._matrix, r, c),fmpz_mat_entry(self._matrix, r, c)) + for r in range(other._nrows): + for c in range(other._ncols): + fmpz_set(fmpz_mat_entry(Z._matrix, r+nr, c),fmpz_mat_entry(other._matrix, r, c)) - cdef Matrix_integer_dense other = bottom.dense_matrix() - cdef Matrix_integer_dense M - M = self.new_matrix(nrows = self._nrows + other._nrows, ncols = self.ncols()) - cdef Py_ssize_t i, j, k - for j from 0 <= j < self._ncols: - for i from 0 <= i < self._nrows: - fmpz_set(fmpz_mat_entry(M._matrix,i,j),fmpz_mat_entry(self._matrix,i,j)) - for i from 0 <= i < other._nrows: - fmpz_set(fmpz_mat_entry(M._matrix,self._nrows + i,j),fmpz_mat_entry(other._matrix,i,j)) - if subdivide: - M._subdivide_on_stack(self, other) - return M + return Z def augment(self, right, subdivide=False): r""" diff --git a/src/sage/matrix/matrix_mod2_dense.pyx b/src/sage/matrix/matrix_mod2_dense.pyx index 445427394ce..b7934b51f9b 100644 --- a/src/sage/matrix/matrix_mod2_dense.pyx +++ b/src/sage/matrix/matrix_mod2_dense.pyx @@ -1575,7 +1575,7 @@ cdef class Matrix_mod2_dense(matrix_dense.Matrix_dense): # dense or sparse Z._subdivide_on_augment(self, other) return Z - def stack(self, bottom, subdivide=False): + cdef _stack_impl(self, bottom): r""" Stack ``self`` on top of ``bottom``. @@ -1641,25 +1641,11 @@ cdef class Matrix_mod2_dense(matrix_dense.Matrix_dense): # dense or sparse sage: M.stack(N) [] """ - if hasattr(bottom, '_vector_'): - bottom = bottom.row() - cdef Matrix_mod2_dense other = bottom - - if self._ncols != other._ncols: - raise TypeError("Both numbers of columns must match.") - - if self._nrows == 0: - return other.__copy__() - if other._nrows == 0: - return self.__copy__() - + cdef Matrix_mod2_dense other = bottom cdef Matrix_mod2_dense Z - Z = self.new_matrix(nrows = self._nrows + other._nrows) - if self._ncols == 0: - return Z - Z._entries = mzd_stack(Z._entries, self._entries, other._entries) - if subdivide: - Z._subdivide_on_stack(self, other) + Z = self.new_matrix(nrows=self._nrows + other._nrows, ncols=self._ncols) + if self._ncols > 0: + Z._entries = mzd_stack(Z._entries, self._entries, other._entries) return Z def submatrix(self, lowr, lowc, nrows , ncols): @@ -1763,7 +1749,8 @@ cdef class Matrix_mod2_dense(matrix_dense.Matrix_dense): # dense or sparse '1 0 1 1 1 0' """ cdef Py_ssize_t i, j, k, n - cdef char *s, *t + cdef char *s + cdef char *t if self._nrows == 0 or self._ncols == 0: data = '' @@ -1845,7 +1832,8 @@ cdef class Matrix_mod2_dense(matrix_dense.Matrix_dense): # dense or sparse if self._nrows == 0 or self._ncols == 0: return 0 cdef mzd_t *A = mzd_copy(NULL, self._entries) - cdef mzp_t *P, *Q + cdef mzp_t *P + cdef mzp_t *Q if algorithm == 'ple': P = mzp_init(self._entries.nrows) diff --git a/src/sage/matrix/matrix_sparse.pyx b/src/sage/matrix/matrix_sparse.pyx index ed5ee0747a6..8356904c614 100644 --- a/src/sage/matrix/matrix_sparse.pyx +++ b/src/sage/matrix/matrix_sparse.pyx @@ -4,11 +4,10 @@ Base class for sparse matrices cimport matrix cimport matrix0 -from sage.structure.element cimport (Element, RingElement, ModuleElement, Vector, CoercionModel) +from sage.structure.element cimport Element, RingElement, ModuleElement, Vector from sage.rings.ring import is_Ring from sage.misc.misc import verbose -include 'sage/ext/cdefs.pxi' include 'sage/ext/stdsage.pxi' include 'sage/ext/python.pxi' include 'sage/ext/interrupt.pxi' @@ -18,9 +17,9 @@ cdef extern from "Python.h": PyObject* PyList_GET_ITEM0 "PyList_GET_ITEM" (PyObject* list, Py_ssize_t i) Py_ssize_t PyNumber_AsSsize_t(PyObject* o, PyObject* exc) - import sage.matrix.matrix_space + cdef class Matrix_sparse(matrix.Matrix): cdef bint is_sparse_c(self): @@ -855,9 +854,9 @@ cdef class Matrix_sparse(matrix.Matrix): A.set_unsafe(new_row, new_col, entry) return A - def stack(self, bottom, subdivide=False): + cdef _stack_impl(self, bottom): r""" - Stack ``self`` on top of ``bottom``. + Stack ``self`` on top of ``bottom``:: [ self ] [ bottom ] @@ -902,50 +901,28 @@ cdef class Matrix_sparse(matrix.Matrix): sage: M = Matrix(ZZ, 2, 3, range(6), sparse=True) sage: N = Matrix(QQ, 1, 3, [10,11,12], sparse=True) sage: M.stack(N) - [0 1 2 3 4] - [5 6 7 8 9] - [0 1 2 3 4] + [ 0 1 2] + [ 3 4 5] + [10 11 12] sage: N.stack(M) - ? + [10 11 12] + [ 0 1 2] + [ 3 4 5] sage: M2 = Matrix(ZZ['x'], 2, 3, range(6), sparse=True) sage: N.stack(M2) - ? + [10 11 12] + [ 0 1 2] + [ 3 4 5] """ - if hasattr(bottom, '_vector_'): - bottom = bottom.row() - if not isinstance(bottom, matrix.Matrix): - raise TypeError("other must be a matrix") - - cdef Matrix_sparse other = bottom.sparse_matrix() - cdef CoercionModel coercion_model - - if self._ncols != other._ncols: - raise TypeError("number of columns must be the same") - - top_ring = self._base_ring - bottom_ring = other.base_ring() - if not (top_ring is bottom_ring): - if top_ring.has_coerce_map_from(bottom_ring): - other = other.change_ring(top_ring) - elif bottom_ring.has_coerce_map_from(top_ring): - self = self.change_ring(bottom_ring) # allowed ? - else: - from sage.structure.element import get_coercion_model - coercion_model = get_coercion_model() - com_ring = coercion_model.common_parent(top_ring, bottom_ring) - self = self.change_ring(com_ring) # allowed ? - other = other.change_ring(com_ring) - - + cdef Matrix_sparse other = bottom cdef Matrix_sparse Z - Z = self.new_matrix(nrows = self._nrows + other.nrows()) + Z = self.new_matrix(nrows=self._nrows + other._nrows, ncols=self._ncols) for i, j in self.nonzero_positions(copy=False): Z.set_unsafe(i, j, self.get_unsafe(i,j)) for i, j in other.nonzero_positions(copy=False): Z.set_unsafe(i + self._nrows, j, other.get_unsafe(i,j)) - if subdivide: - Z._subdivide_on_stack(self, other) + return Z def augment(self, right, subdivide=False): diff --git a/src/sage/modules/diamond_cutting.py b/src/sage/modules/diamond_cutting.py index c6098fa5838..ea98105c2c6 100644 --- a/src/sage/modules/diamond_cutting.py +++ b/src/sage/modules/diamond_cutting.py @@ -259,7 +259,7 @@ def calculate_voronoi_cell(basis, radius=None, verbose=False): artificial_length = None if dim[0] < dim[1]: # introduce "artificial" basis points (representing infinity) - artificial_length = ceil(max(abs(v) for v in basis)) * 2 + artificial_length = max(abs(v) for v in basis).ceil() * 2 additional_vectors = identity_matrix(dim[1]) * artificial_length basis = basis.stack(additional_vectors) # LLL-reduce to get quadratic matrix diff --git a/src/sage/modules/fg_pid/fgp_morphism.py b/src/sage/modules/fg_pid/fgp_morphism.py index 80fe02ba823..86bbe370955 100644 --- a/src/sage/modules/fg_pid/fgp_morphism.py +++ b/src/sage/modules/fg_pid/fgp_morphism.py @@ -423,6 +423,7 @@ def lift(self, x): # Stack it on top of the basis for W'. Wp = CD.V().coordinate_module(CD.W()).basis_matrix() + Wp = Wp.change_ring(A.base_ring()) B = A.stack(Wp) # Compute Hermite form of C with transformation From 0fcce825397b78e58a13b0fa5eaa930e7bd17960 Mon Sep 17 00:00:00 2001 From: Ralf Stephan Date: Wed, 7 Jan 2015 10:33:03 +0100 Subject: [PATCH 131/217] 16201: more doctests, fixes; remove deprecated usage --- src/sage/rings/laurent_series_ring.py | 10 ++++++++++ src/sage/rings/morphism.pyx | 3 +-- .../hyperelliptic_curves/hyperelliptic_generic.py | 3 +-- src/sage/symbolic/expression.pyx | 11 +++++++++-- 4 files changed, 21 insertions(+), 6 deletions(-) diff --git a/src/sage/rings/laurent_series_ring.py b/src/sage/rings/laurent_series_ring.py index f9c7a56ab00..70354f18726 100644 --- a/src/sage/rings/laurent_series_ring.py +++ b/src/sage/rings/laurent_series_ring.py @@ -81,6 +81,16 @@ def LaurentSeriesRing(base_ring, name=None, names=None, default_prec=None, spars sage: W. = LaurentSeriesRing(Qp(5,prec=199)) sage: W is T False + + TESTS: + + Check if changing global series precision does it right:: + + sage: set_series_precision(3) + sage: R. = LaurentSeriesRing(ZZ) + sage: 1/(1 - 2*x) + 1 + 2*x + 4*x^2 + O(x^3) + sage: set_series_precision(20) """ if not names is None: name = names if name is None: diff --git a/src/sage/rings/morphism.pyx b/src/sage/rings/morphism.pyx index d383eb30c0d..5c7c592f826 100644 --- a/src/sage/rings/morphism.pyx +++ b/src/sage/rings/morphism.pyx @@ -242,11 +242,10 @@ Frobenius on a power series ring over a finite field:: Homomorphism of Laurent series ring:: - sage: R. = LaurentSeriesRing(QQ) + sage: R. = LaurentSeriesRing(QQ, 10) sage: f = R.hom([t^3 + t]); f Ring endomorphism of Laurent Series Ring in t over Rational Field Defn: t |--> t + t^3 - sage: R.set_default_prec(10) sage: s = 2/t^2 + 1/(1 + t); s 2*t^-2 + 1 - t + t^2 - t^3 + t^4 - t^5 + t^6 - t^7 + t^8 - t^9 + O(t^10) sage: f(s) diff --git a/src/sage/schemes/hyperelliptic_curves/hyperelliptic_generic.py b/src/sage/schemes/hyperelliptic_curves/hyperelliptic_generic.py index 9c47d34ce29..79bac8cd36a 100644 --- a/src/sage/schemes/hyperelliptic_curves/hyperelliptic_generic.py +++ b/src/sage/schemes/hyperelliptic_curves/hyperelliptic_generic.py @@ -507,9 +507,8 @@ def local_coordinates_at_infinity(self, prec = 20, name = 't'): """ g = self.genus() pol = self.hyperelliptic_polynomials()[0] - K = LaurentSeriesRing(self.base_ring(), name) + K = LaurentSeriesRing(self.base_ring(), name, default_prec=prec+2) t = K.gen() - K.set_default_prec(prec+2) L = PolynomialRing(self.base_ring(),'x') x = L.gen() i = 0 diff --git a/src/sage/symbolic/expression.pyx b/src/sage/symbolic/expression.pyx index e52acf44dfd..6b8978eb34a 100644 --- a/src/sage/symbolic/expression.pyx +++ b/src/sage/symbolic/expression.pyx @@ -3505,7 +3505,7 @@ cdef class Expression(CommutativeRingElement): of the equality - ``order`` - an integer; if nothing given, it is set to the global default (``20``), which can be changed - using :meth:`set_series_precision` + using :func:`set_series_precision` OUTPUT: @@ -3570,6 +3570,13 @@ cdef class Expression(CommutativeRingElement): sage: f = sin(x)^(-2); f.series(x, -1) 1*x^(-2) + Order(1/x) + + Check if changing global series precision does it right:: + + sage: set_series_precision(3) + sage: (1/(1-2*x)).series(x) + 1 + 2*x + 4*x^2 + Order(x^3) + sage: set_series_precision(20) """ cdef Expression symbol0 = self.coerce_in(symbol) cdef GEx x @@ -3581,7 +3588,7 @@ cdef class Expression(CommutativeRingElement): prec = order sig_on() try: - x = self._gobj.series(symbol0._gobj, order, 0) + x = self._gobj.series(symbol0._gobj, prec, 0) finally: sig_off() return new_Expression_from_GEx(self._parent, x) From 0b9914f8b6cd4cb0f885f0550bfd375d80495a71 Mon Sep 17 00:00:00 2001 From: Ralf Stephan Date: Wed, 7 Jan 2015 10:55:00 +0100 Subject: [PATCH 132/217] 17399: remove whitespace changes --- src/sage/symbolic/expression.pyx | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/sage/symbolic/expression.pyx b/src/sage/symbolic/expression.pyx index 762a02924cc..8a6128b927a 100644 --- a/src/sage/symbolic/expression.pyx +++ b/src/sage/symbolic/expression.pyx @@ -5109,9 +5109,9 @@ cdef class Expression(CommutativeRingElement): Traceback (most recent call last): ... TypeError: n != 1 only allowed for s being a variable - + Using ``coeff()`` is now deprecated (:trac:`17438`):: - + sage: x.coeff(x) doctest:...: DeprecationWarning: coeff is deprecated. Please use coefficient instead. See http://trac.sagemath.org/17438 for details. @@ -5140,12 +5140,12 @@ cdef class Expression(CommutativeRingElement): - ``x`` -- optional variable. OUTPUT: - + Depending on the value of ``sparse``, - A list of pairs ``(expr, n)``, where ``expr`` is a symbolic expression and ``n`` is a power (``sparse=True``, default) - + - A list of expressions where the ``n``-th element is the coefficient of ``x^n`` when self is seen as polynomial in ``x`` (``sparse=False``). @@ -5173,7 +5173,7 @@ cdef class Expression(CommutativeRingElement): [[2*a^2 + 1, 0], [-2*sqrt(2)*a + 1, 1], [1, 2]] sage: p.coefficients(x, sparse=False) [2*a^2 + 1, -2*sqrt(2)*a + 1, 1] - + TESTS: The behaviour is undefined with noninteger or negative exponents:: @@ -5185,16 +5185,16 @@ cdef class Expression(CommutativeRingElement): Traceback (most recent call last): ... ValueError: Cannot return dense coefficient list with noninteger exponents. - + Using ``coeffs()`` is now deprecated (:trac:`17438`):: - + sage: x.coeffs() doctest:...: DeprecationWarning: coeffs is deprecated. Please use coefficients instead. See http://trac.sagemath.org/17438 for details. [[1, 1]] - + Series coefficients are now handled correctly (:trac:`17399`):: - + sage: s=(1/(1-x)).series(x,6); s 1 + 1*x + 1*x^2 + 1*x^3 + 1*x^4 + 1*x^5 + Order(x^6) sage: s.coefficients() @@ -5236,7 +5236,7 @@ cdef class Expression(CommutativeRingElement): return ret coeffs = deprecated_function_alias(17438, coefficients) - + def list(self, x=None): r""" Return the coefficients of this symbolic expression as a polynomial in x. From a60cc747d5b9270e5fb3d636ebb5fdd07d2d97d9 Mon Sep 17 00:00:00 2001 From: Mario Pernici Date: Wed, 7 Jan 2015 13:32:54 +0100 Subject: [PATCH 133/217] added argument `complement`, eliminated argument `check` in ``rook_vector``; added documentation and tests --- src/sage/matrix/matrix2.pyx | 98 +++++++++++++++++++++++++++++-------- 1 file changed, 78 insertions(+), 20 deletions(-) diff --git a/src/sage/matrix/matrix2.pyx b/src/sage/matrix/matrix2.pyx index 4cc72daf7fe..dd449a36aa6 100644 --- a/src/sage/matrix/matrix2.pyx +++ b/src/sage/matrix/matrix2.pyx @@ -755,7 +755,7 @@ cdef class Matrix(matrix1.Matrix): sage: A.permanent() 32 - A huge determinant that can not be reasonably computed with the Ryser + A huge permanent that can not be reasonably computed with the Ryser algorithm (a `50 \times 50` band matrix with width `5`):: sage: n, w = 50, 5 @@ -947,7 +947,7 @@ cdef class Matrix(matrix1.Matrix): pm = pm + self.matrix_from_rows_and_columns(rows, cols).permanent() return pm - def rook_vector(self, algorithm="ButeraPernici", check=False): + def rook_vector(self, algorithm="ButeraPernici", complement=True): r""" Return the rook vector of this matrix. @@ -958,8 +958,8 @@ cdef class Matrix(matrix1.Matrix): another. The *rook vector* of the matrix `A` is the list consisting of `r_0, - r_1, \ldots, r_m`. The *rook polynomial* is defined by - `r(x) = \sum_{k=0}^m r_k x^k`. + r_1, \ldots, r_h`, where `h = min(m,n)`. The *rook polynomial* is defined by + `r(x) = \sum_{k=0}^h r_k x^k`. The rook vector can be generalized to matrices defined over any rings using permanental minors. Among the available algorithms, only "Godsil" @@ -969,17 +969,20 @@ cdef class Matrix(matrix1.Matrix): method :meth:`permanental_minor` to compute individual permanental minor. - INPUT: + See also ``sage.matrix.matrix2.permanental_minor_polynomial`` + and the graph method ``matching_polynomial``. - - ``self`` -- an `m` by `n` (0,1)-matrix + INPUT: - - ``check`` -- Boolean (default: ``False``) determining whether - to check that ``self`` is a (0,1)-matrix. + - ``self`` -- an `m` by `n` matrix - ``algorithm`` - a string which must be either "Ryser" or "ButeraPernici" (default) or "Godsil"; Ryser one might be faster on simple and small instances. Godsil only accepts input in 0,1. + - ``complement`` -- Boolean (default: ``True``) whether to compute the + rook vector of a (0,1)-matrix from its complement. + EXAMPLES: The standard chessboard is an `8` by `8` grid in which any positions is @@ -996,6 +999,15 @@ cdef class Matrix(matrix1.Matrix): x^8 + 64*x^7 + 1568*x^6 + 18816*x^5 + 117600*x^4 + 376320*x^3 + 564480*x^2 + 322560*x + 40320 + The number of desarrangements of length `n` is the permanent + of a matrix with 0 on the diagonal and 1 elsewhere; + for `n=21` it is `18795307255050944540` (see OEIS A000166): + + sage: n = 21 + sage: A = matrix([[int(j != i) for i in range(n)] for j in range(n)]) + sage: A.rook_vector()[-1] + 18795307255050944540 + An other example that we convert into a rook polynomial:: sage: A = matrix(3,6, [1,1,1,1,0,0,0,1,1,1,1,0,0,0,1,1,1,1]) @@ -1020,6 +1032,18 @@ cdef class Matrix(matrix1.Matrix): sage: A.rook_vector(algorithm="Godsil") [1, 8, 20, 16, 4] + When the matrix `A` has more ones then zeroes it is usually faster + to compute the rook polynomial of the complementary matrix, with + zeroes and ones interchanged, and use the inclusion-exclusion theorem, + giving for a `m \times n` matrix `A` with complementary matrix `B` + + .. MATH:: + + r_k(A) = \sum_{j=0}^k (-1)^j \binom{m-j}{k-j} \binom{n-j}{k-j} (k-j)! r_j(B) + + see [Riordan] or the introductory text [Allenby]. + + An example with an exotic matrix (for which only Butera-Pernici and Ryser algorithms are available):: @@ -1032,9 +1056,7 @@ cdef class Matrix(matrix1.Matrix): sage: A.rook_vector(algorithm="Godsil") Traceback (most recent call last): ... - ValueError: coefficients must be zero or one, but we have 'x' in - position (0,1). - + ValueError: coefficients must be zero or one, but we have 'x' in position (0,1). sage: B = A.transpose() sage: B.rook_vector(algorithm="ButeraPernici") [1, x^2 + x*y + x + 2*y + 1, 2*x^2*y + x*y^2 + x^2 + y^2 + y] @@ -1053,6 +1075,18 @@ cdef class Matrix(matrix1.Matrix): [1, 8, 12] sage: matrix.ones(4, 2).rook_vector("Godsil") [1, 8, 12] + sage: m = matrix(8,9,[int(j != i) for i in range(9) for j in range(8)]) + sage: m.rook_vector() + [1, 64, 1568, 18816, 117600, 376320, 564480, 322560, 40320] + sage: m.rook_vector(complement=False) + [1, 64, 1568, 18816, 117600, 376320, 564480, 322560, 40320] + + REFERENCES: + + .. [Riordan] J. Riordan, "An Introduction to Combinatorial Analysis", + Dover Publ. (1958) + + .. [Allenby] R.B.J.T Allenby and A. Slomson, "How to count", CRC Press (2011) AUTHORS: @@ -1063,15 +1097,39 @@ cdef class Matrix(matrix1.Matrix): n = self._ncols mn = min(m,n) - if check or algorithm == "Godsil": - # verify that self[i, j] in {0, 1} - zero = self.base_ring().zero() - one = self.base_ring().one() - for i in range(m): - for j in range(n): - x = self.get_unsafe(i, j) - if x != zero and x != one: - raise ValueError("coefficients must be zero or one, but we have '{}' in position ({},{}).".format(x,i,j)) + # z2 flag for self[i, j] in {0, 1} + z2 = True + num_ones = 1 + zero = self.base_ring().zero() + one = self.base_ring().one() + for i in range(m): + for j in range(n): + x = self.get_unsafe(i, j) + if x != zero: + if x != one: + z2 = False + if algorithm == "Godsil": + raise ValueError("coefficients must be zero or one, but we have '{}' in position ({},{}).".format(x,i,j)) + else: + num_ones += 1 + + fill = 0.55 + if z2 and complement and num_ones > fill*m*n: + from sage.matrix.constructor import matrix + B = [[1-self.get_unsafe(i, j) for j in range(n)] for i in range(m)] + B = matrix(B) + b = B.rook_vector(algorithm=algorithm, complement=False) + a = [1] + c1 = 1 + for k in range(1, mn + 1): + c1 = c1*(m-k+1)*(n-k+1)/k + c = c1 + s = c*b[0] + (-1)**k*b[k] + for j in range(1, k): + c = -c*(k-j+1)/((m-j+1)*(n-j+1)) + s += c*b[j] + a.append(s) + return a if algorithm == "Ryser": return [self.permanental_minor(k,algorithm="Ryser") for k in range(mn+1)] From 810a889f723ae78ee0331cf366e22e2308ebf74e Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Wed, 7 Jan 2015 20:16:20 +0100 Subject: [PATCH 134/217] Rephrase documentation --- src/sage/matrix/matrix1.pyx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/matrix/matrix1.pyx b/src/sage/matrix/matrix1.pyx index 1ae2d24c103..94b8e1ee518 100644 --- a/src/sage/matrix/matrix1.pyx +++ b/src/sage/matrix/matrix1.pyx @@ -1117,7 +1117,8 @@ cdef class Matrix(matrix0.Matrix): ########################################################################### def stack(self, bottom, subdivide=False): r""" - Return the matrix ``self`` on top of ``bottom``:: + Return a new matrix formed by appending the matrix (or vector) + ``bottom`` below ``self``:: [ self ] [ bottom ] @@ -1158,7 +1159,6 @@ cdef class Matrix(matrix0.Matrix): :func:`~sage.matrix.constructor.block_diagonal_matrix` useful and simpler in some instances. - EXAMPLES: Stacking with a matrix. :: From 997528b08110b9acc1a1fe878553313993f3dbd3 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Thu, 8 Jan 2015 09:36:03 +0100 Subject: [PATCH 135/217] remove double "passed to" from docstring --- src/sage/structure/unique_representation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/structure/unique_representation.py b/src/sage/structure/unique_representation.py index dc49a9e4b5b..89201fef477 100644 --- a/src/sage/structure/unique_representation.py +++ b/src/sage/structure/unique_representation.py @@ -157,7 +157,7 @@ class will by default also be used as keys for the cache:: .. WARNING:: - If there is preprocessing, then the preprocessed arguments passed to + If there is preprocessing, then the preprocessed arguments passed to :meth:`CachedRepresentation.__classcall__` must be invariant under the preprocessing. That is to say, preprocessing the input arguments twice must have the same effect as preprocessing the input From e7ff9304dc91db662a608d1861e317cffd28966e Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Thu, 8 Jan 2015 15:31:31 +0530 Subject: [PATCH 136/217] trac #17577: Bug with labels --- src/sage/combinat/designs/incidence_structures.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/sage/combinat/designs/incidence_structures.py b/src/sage/combinat/designs/incidence_structures.py index 2e34893341d..c79d7bb351b 100644 --- a/src/sage/combinat/designs/incidence_structures.py +++ b/src/sage/combinat/designs/incidence_structures.py @@ -631,9 +631,10 @@ def induced_substructure(self,points): except KeyError: raise ValueError("{} is not a point of the incidence structure".format(x)) - points = set(points) + int_points = set(int_points) return IncidenceStructure(points, - [S for S in self._blocks if points.issuperset(S)]) + [[self._points[x] for x in S] for S in self._blocks + if int_points.issuperset(S)]) def ground_set(self): r""" From e4359bb3d2444375eed077c72274e4f6a4ed87bc Mon Sep 17 00:00:00 2001 From: Mario Pernici Date: Thu, 8 Jan 2015 12:20:42 +0100 Subject: [PATCH 137/217] added ``matching_polynomial`` method to ``BipartiteGraph`` class --- src/sage/graphs/bipartite_graph.py | 60 ++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/src/sage/graphs/bipartite_graph.py b/src/sage/graphs/bipartite_graph.py index 4ba7d1c6080..c6f0c428df3 100644 --- a/src/sage/graphs/bipartite_graph.py +++ b/src/sage/graphs/bipartite_graph.py @@ -894,6 +894,66 @@ def plot(self, *args, **kwds): kwds["pos"] = pos return Graph.plot(self, *args, **kwds) + def matching_polynomial(self, algorithm="Godsil", complement=True, name=None): + r""" + Computes the matching polynomial. + + INPUT: + + - ``algorithm`` - a string which must be either "Godsil" (default) + or "rook"; "rook" is usually faster for larger graphs. + + - ``complement`` - Boolean (default: ``True``) whether to compute the + rook vector from its complement; it is used in the "rook" algorithm. + + - ``name`` - optional string for the variable name in the polynomial. + + EXAMPLE:: + + sage: BipartiteGraph(graphs.CubeGraph(3)).matching_polynomial() + x^8 - 12*x^6 + 42*x^4 - 44*x^2 + 9 + + :: + + sage: x = polygen(ZZ) + sage: g = BipartiteGraph(graphs.CompleteBipartiteGraph(16, 16)) + sage: factorial(16)*laguerre(16,x^2) == g.matching_polynomial(algorithm='rook') + True + + Compute the matching polynomial of a line with `60` vertices:: + + sage: from sage.functions.orthogonal_polys import chebyshev_U + sage: g = graphs.trees(60).next() + sage: chebyshev_U(60, x/2) == BipartiteGraph(g).matching_polynomial(algorithm='rook') + True + + The matching polynomial of a tree graphs is equal to its characteristic + polynomial:: + + sage: g = graphs.RandomTree(20) + sage: p = g.characteristic_polynomial() + sage: p == BipartiteGraph(g).matching_polynomial(algorithm='rook') + True + """ + if algorithm == "Godsil": + return Graph.matching_polynomial(self, complement=False, name=name) + elif algorithm == "rook": + from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing + A = self.reduced_adjacency_matrix() + a = A.rook_vector(complement=complement) + m = A.nrows() + n = A.ncols() + b = [0]*(m + n + 1) + for i in range(min(m, n) + 1): + b[m + n - 2*i] = a[i]*(-1)**i + if name is None: + name = 'x' + K = PolynomialRing(A.base_ring(), name) + p = K(b) + return p + else: + raise ValueError('algorithm must be one of "Godsil" or "rook".') + def load_afile(self, fname): r""" Loads into the current object the bipartite graph specified in the From 344c088eb647c7bb5c67f9a25940af8f99555c71 Mon Sep 17 00:00:00 2001 From: Clemens Heuberger Date: Thu, 8 Jan 2015 14:46:07 +0100 Subject: [PATCH 138/217] Trac #17599: Fix ReSt Formatting errors in schemes.hyperelliptic_curves.hyperelliptic_generic --- .../hyperelliptic_generic.py | 74 ++++++++++--------- 1 file changed, 41 insertions(+), 33 deletions(-) diff --git a/src/sage/schemes/hyperelliptic_curves/hyperelliptic_generic.py b/src/sage/schemes/hyperelliptic_curves/hyperelliptic_generic.py index 9c47d34ce29..de9d9c867a6 100644 --- a/src/sage/schemes/hyperelliptic_curves/hyperelliptic_generic.py +++ b/src/sage/schemes/hyperelliptic_curves/hyperelliptic_generic.py @@ -274,6 +274,7 @@ def odd_degree_model(self): x^4 - 8*x^3 + 150*x^2 - 536*x + 4489 TESTS:: + sage: HyperellipticCurve(x^5 + 1, 1).odd_degree_model() Traceback (most recent call last): ... @@ -306,6 +307,7 @@ def has_odd_degree_model(self): Use ``odd_degree_model`` to calculate an odd degree model. EXAMPLES:: + sage: x = QQ['x'].0 sage: HyperellipticCurve(x^5 + x).has_odd_degree_model() True @@ -366,19 +368,20 @@ def invariant_differential(self): def local_coordinates_at_nonweierstrass(self, P, prec=20, name='t'): """ - For a non-Weierstrass point P = (a,b) on the hyperelliptic - curve y^2 = f(x), returns (x(t), y(t)) such that (y(t))^2 = f(x(t)), - where t = x - a is the local parameter. + For a non-Weierstrass point `P = (a,b)` on the hyperelliptic + curve `y^2 = f(x)`, return `(x(t), y(t))` such that `(y(t))^2 = f(x(t))`, + where `t = x - a` is the local parameter. INPUT: - - P = (a,b) a non-Weierstrass point on self - - prec: desired precision of the local coordinates - - name: gen of the power series ring (default: 't') + - ``P = (a, b)`` -- a non-Weierstrass point on self + - ``prec`` -- desired precision of the local coordinates + - ``name`` -- gen of the power series ring (default: ``t``) OUTPUT: - (x(t),y(t)) such that y(t)^2 = f(x(t)) and t = x - a - is the local parameter at P + + `(x(t),y(t))` such that `y(t)^2 = f(x(t))` and `t = x - a` + is the local parameter at `P` EXAMPLES:: @@ -420,26 +423,26 @@ def local_coordinates_at_nonweierstrass(self, P, prec=20, name='t'): def local_coordinates_at_weierstrass(self, P, prec=20, name='t'): """ For a finite Weierstrass point on the hyperelliptic - curve y^2 = f(x), returns (x(t), y(t)) such that - (y(t))^2 = f(x(t)), where t = y is the local parameter. + curve `y^2 = f(x)`, returns `(x(t), y(t))` such that + `(y(t))^2 = f(x(t))`, where `t = y` is the local parameter. INPUT: - - P a finite Weierstrass point on self - - prec: desired precision of the local coordinates - - name: gen of the power series ring (default: 't') + + - ``P`` -- a finite Weierstrass point on self + - ``prec`` -- desired precision of the local coordinates + - ``name`` -- gen of the power series ring (default: `t`) OUTPUT: - (x(t),y(t)) such that y(t)^2 = f(x(t)) and t = y - is the local parameter at P + `(x(t),y(t))` such that `y(t)^2 = f(x(t))` and `t = y` + is the local parameter at `P` + + EXAMPLES:: - EXAMPLES: sage: R. = QQ['x'] sage: H = HyperellipticCurve(x^5-23*x^3+18*x^2+40*x) sage: A = H(4, 0) - sage: x, y = H.local_coordinates_at_weierstrass(A, prec=7) - sage: x 4 + 1/360*t^2 - 191/23328000*t^4 + 7579/188956800000*t^6 + O(t^7) sage: y @@ -472,19 +475,22 @@ def local_coordinates_at_weierstrass(self, P, prec=20, name='t'): def local_coordinates_at_infinity(self, prec = 20, name = 't'): """ - For the genus g hyperelliptic curve y^2 = f(x), returns (x(t), y(t)) such that - (y(t))^2 = f(x(t)), where t = x^g/y is the local parameter at infinity + For the genus `g` hyperelliptic curve `y^2 = f(x)`, return + `(x(t), y(t))` such that `(y(t))^2 = f(x(t))`, where `t = x^g/y` is + the local parameter at infinity INPUT: - - prec: desired precision of the local coordinates - - name: gen of the power series ring (default: 't') + + - ``prec`` -- desired precision of the local coordinates + - ``name`` -- generator of the power series ring (default: ``t``) OUTPUT: - (x(t),y(t)) such that y(t)^2 = f(x(t)) and t = x^g/y + + `(x(t),y(t))` such that `y(t)^2 = f(x(t))` and `t = x^g/y` is the local parameter at infinity + EXAMPLES:: - EXAMPLES: sage: R. = QQ['x'] sage: H = HyperellipticCurve(x^5-5*x^2+1) sage: x,y = H.local_coordinates_at_infinity(10) @@ -493,6 +499,8 @@ def local_coordinates_at_infinity(self, prec = 20, name = 't'): sage: y t^-5 + 10*t - 2*t^5 - 75*t^7 + 50*t^11 + O(t^12) + :: + sage: R. = QQ['x'] sage: H = HyperellipticCurve(x^3-x+1) sage: x,y = H.local_coordinates_at_infinity(10) @@ -501,7 +509,6 @@ def local_coordinates_at_infinity(self, prec = 20, name = 't'): sage: y t^-3 + t - t^3 - t^5 + 3*t^7 - 10*t^11 + O(t^12) - AUTHOR: - Jennifer Balakrishnan (2007-12) """ @@ -527,15 +534,18 @@ def local_coord(self, P, prec = 20, name = 't'): Calls the appropriate local_coordinates function INPUT: - - P a point on self - - prec: desired precision of the local coordinates - - name: gen of the power series ring (default: 't') + + - ``P`` -- a point on self + - ``prec`` -- desired precision of the local coordinates + - ``name`` -- generator of the power series ring (default: ``t``) OUTPUT: - (x(t),y(t)) such that y(t)^2 = f(x(t)), where t - is the local parameter at P - EXAMPLES: + `(x(t),y(t))` such that `y(t)^2 = f(x(t))`, where `t` + is the local parameter at `P` + + EXAMPLES:: + sage: R. = QQ['x'] sage: H = HyperellipticCurve(x^5-23*x^3+18*x^2+40*x) sage: H.local_coord(H(1 ,6), prec=5) @@ -547,8 +557,6 @@ def local_coord(self, P, prec = 20, name = 't'): AUTHOR: - Jennifer Balakrishnan (2007-12) - - """ if P[1] == 0: return self.local_coordinates_at_weierstrass(P, prec, name) From 472c754f6b833011877484e0ce944a5d2ad9d3e0 Mon Sep 17 00:00:00 2001 From: Martin von Gagern Date: Thu, 8 Jan 2015 15:03:01 +0100 Subject: [PATCH 139/217] Implement symbolic (radical) roots of polynomials. This is considered a preliminary implementation, to be extended once #17516 gets implemented. Other code which employs similar functionality is encouraged to delegate to this method, so that they will benefit from subsequent improvements here. Note that the case where K is SR is unlikely to benefit from Galois theory, so the core of the code will likely stay around, as one of several cases and most likely as the default fallback. Instead of expr=self(var), one might also have considered SR(self). Tests show that the former is faster (for a moderate rational polynomial), and the latter fails altogether if K is SR. So this substitution was chosen. Choosing a fixed, long and unlikely-to-clash variable was inspired by the appoach taken for the charpoly computation in #14403, suggested by nbruin. --- .../rings/polynomial/polynomial_element.pyx | 59 ++++++++++++++++++- 1 file changed, 58 insertions(+), 1 deletion(-) diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index 81b49f94b80..f78c4bbb593 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -5313,13 +5313,48 @@ cdef class Polynomial(CommutativeAlgebraElement): sage: g.roots(multiplicities=False) [1.33000000000000 - 1.33000000000000*I, 2.66000000000000 + 2.66000000000000*I] + Describing roots using radical expressions:: + + sage: x = QQ['x'].0 + sage: f = x^2 + 2 + sage: f.roots(SR) + [(-I*sqrt(2), 1), (I*sqrt(2), 1)] + sage: f.roots(SR, multiplicities=False) + [-I*sqrt(2), I*sqrt(2)] + + The roots of some polynomials can't be described using radical + expressions:: + + sage: (x^5 - x + 1).roots(SR) + [] + + For some other polynomials, no roots can be found at the moment + due to the way roots are computed. :trac:`17516` addresses + these defecits. Until that gets implemented, one such example + is the following:: + + sage: f = x^6-300*x^5+30361*x^4-1061610*x^3+1141893*x^2-915320*x+101724 + sage: f.roots() + [] + A purely symbolic roots example:: sage: X = var('X') sage: f = expand((X-1)*(X-I)^3*(X^2 - sqrt(2))); f X^6 - (3*I + 1)*X^5 - sqrt(2)*X^4 + (3*I - 3)*X^4 + (3*I + 1)*sqrt(2)*X^3 + (I + 3)*X^3 - (3*I - 3)*sqrt(2)*X^2 - I*X^2 - (I + 3)*sqrt(2)*X + I*sqrt(2) - sage: print f.roots() + sage: f.roots() + [(I, 3), (-2^(1/4), 1), (2^(1/4), 1), (1, 1)] + + The same operation, performed over a polynomial ring + with symbolic coefficients:: + + sage: X = SR['X'].0 + sage: f = (X-1)*(X-I)^3*(X^2 - sqrt(2)); f + X^6 + (-3*I - 1)*X^5 + (-sqrt(2) + 3*I - 3)*X^4 + ((3*I + 1)*sqrt(2) + I + 3)*X^3 + (-(3*I - 3)*sqrt(2) - I)*X^2 + (-(I + 3)*sqrt(2))*X + I*sqrt(2) + sage: f.roots() [(I, 3), (-2^(1/4), 1), (2^(1/4), 1), (1, 1)] + sage: f.roots(multiplicities=False) + [I, -2^(1/4), 2^(1/4), 1] A couple of examples where the base ring doesn't have a factorization algorithm (yet). Note that this is currently done via @@ -5563,6 +5598,15 @@ cdef class Polynomial(CommutativeAlgebraElement): back to pari (numpy will fail if some coefficient is infinite, for instance). + If L is SR, then the roots will be radical expressions, + computed as the solutions of a symbolic polynomial expression. + At the moment this delegates to + :meth:`sage.symbolic.expression.Expression.solve` + which in turn uses Maxima to find radical solutions. + Some solutions may be lost in this approach. + Once :trac:`17516` gets implemented, all possible radical + solutions should become available. + If L is AA or RIF, and K is ZZ, QQ, or AA, then the root isolation algorithm sage.rings.polynomial.real_roots.real_roots() is used. (You can call real_roots() directly to get more control than this @@ -5775,6 +5819,19 @@ cdef class Polynomial(CommutativeAlgebraElement): else: return [rt for (rt, mult) in rts_mult] + from sage.symbolic.ring import SR + if L is SR: + vname = 'do_not_use_this_name_in_a_polynomial_coefficient' + var = SR(vname) + expr = self(var) + rts = expr.solve(var, + explicit_solutions=True, + multiplicities=multiplicities) + if multiplicities: + return [(rt.rhs(), mult) for rt, mult in zip(*rts)] + else: + return [rt.rhs() for rt in rts] + if L != K or is_AlgebraicField_common(L): # So far, the only "special" implementations are for real # and complex root isolation and for p-adic factorization From f46c53dd9cce39eda24df08481913267ba915e5c Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Thu, 8 Jan 2015 15:14:18 +0100 Subject: [PATCH 140/217] Raise TypeError instead of ValueError if conversion fails --- src/sage/rings/function_field/function_field_order.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/rings/function_field/function_field_order.py b/src/sage/rings/function_field/function_field_order.py index 4b2ec6680e2..f77b96e0b63 100644 --- a/src/sage/rings/function_field/function_field_order.py +++ b/src/sage/rings/function_field/function_field_order.py @@ -299,7 +299,7 @@ def _element_constructor_(self, f, check=True): V, fr, to = fraction_field.vector_space() f_vector = to(fraction_field(f)) if not f_vector in self._module: - raise ValueError("%s is not an element of %s"%(f_vector,self)) + raise TypeError("%r is not an element of %r"%(f_vector,self)) return fraction_field._element_class(self, f) def fraction_field(self): @@ -479,11 +479,11 @@ def _element_constructor_(self, f): sage: O._element_constructor_(1/y) Traceback (most recent call last): ... - ValueError: `1/y` is not a member of `Maximal order in Rational function field in y over Rational Field` + TypeError: 1/y is not an element of Maximal order in Rational function field in y over Rational Field """ if f.parent() is self.fraction_field(): if not f.denominator() in self.fraction_field().constant_base_field(): - raise ValueError("`%s` is not a member of `%s`"%(f,self)) + raise TypeError("%r is not an element of %r"%(f,self)) f = f.element() from function_field_element import FunctionFieldElement_rational return FunctionFieldElement_rational(self, self._ring(f)) From a84624002490417b18adb6534a1c73843822306e Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Thu, 8 Jan 2015 22:06:04 +0530 Subject: [PATCH 141/217] trac #17605: "How to call C code from Sage" thematic tutorial --- .../thematic_tutorials/cython_interface.rst | 113 ++++++++++++++++++ src/doc/en/thematic_tutorials/index.rst | 1 + src/doc/en/thematic_tutorials/toctree.rst | 1 + 3 files changed, 115 insertions(+) create mode 100644 src/doc/en/thematic_tutorials/cython_interface.rst diff --git a/src/doc/en/thematic_tutorials/cython_interface.rst b/src/doc/en/thematic_tutorials/cython_interface.rst new file mode 100644 index 00000000000..a545b30e456 --- /dev/null +++ b/src/doc/en/thematic_tutorials/cython_interface.rst @@ -0,0 +1,113 @@ +.. _cython_interface: +.. nodoctest +================================ +How to call a C code from Sage ? +================================ + +If you have some C/C++ code that you would like to call from Sage for your own +use, this document is for you. + +- Do you want to **contibute** to Sage by adding your interface to its code? The + (more complex) instructions are `available here + `_ + +Calling "hello_world()" from hello.c +------------------------------------ + +Let us suppose that you have in your current directory a file named hello.c:: + + ~/a$ cat hello.c + #include + + void hello_world(){ + printf("Hello World\n"); + } + + void main(){ + hello_world(); + } + ~/a$ gcc hello.c -o hello; ./hello + Hello World + +In order to call this function from Sage, you must create a Cython file (i.e. a +file whose extension is .pyx). This file contains a header containing the +signature of the function that you want to call:: + + ~/a$ cat hello_sage.pyx + cdef extern from "hello.c": + void hello_world() + + def my_interface_function(): + hello_world() # This is the C function from hello.c + +You can now load this file in Sage, and call the C code though the Python +function ``my_interface_function``:: + + sage: %runfile hello_sage.pyx + Compiling ./hello_sage.pyx... + sage: my_bridge_function() + Hello World + +Arguments and return value +-------------------------- + +Calling function with more complex arguments and return values works the same +way. To learn more about the Cython language, `click here +`_ + +The following example defines a function taking and returning ``int *`` +pointers, and involves some memory allocation. The C code defines a function +whose purpose is to return the sum of two vectors as a third vector. + +**The C file**:: + + #include + + int * sum_of_two_vectors(int n, int * vec1, int * vec2){ + /* + INPUT : two arrays vec1,vec2 of n integers + OUTPUT: an array of size n equal to vec1+vec2 + */ + int * sum = (int *) malloc(n*sizeof(int)); + int i; + + for(i=0;i malloc(n*sizeof(int)) + cdef int * vec2 = malloc(n*sizeof(int)) + + # Fill the vectors + for i in range(n): + vec1[i] = list1[i] + vec2[i] = list2[i] + + # Call the C function + cdef int * vec3 = sum_of_two_vectors(n,vec1,vec2) + + # Save the answer in a Python object + answer = [vec3[i] for i in range(n)] + + free(vec1) + free(vec2) + free(vec3) + + return answer + + +**Call from Sage**:: + + sage: %runfile double_vector_sage.pyx + Compiling ./double_vector_sage.pyx... + sage: sage_sum_of_vectors(3,[1,1,1],[2,3,4]) + [3, 4, 5] diff --git a/src/doc/en/thematic_tutorials/index.rst b/src/doc/en/thematic_tutorials/index.rst index 6476dbea510..9d5af8c2f5c 100644 --- a/src/doc/en/thematic_tutorials/index.rst +++ b/src/doc/en/thematic_tutorials/index.rst @@ -100,6 +100,7 @@ Programming and Design * :ref:`tutorial-comprehensions` * :ref:`tutorial-objects-and-classes` * :ref:`functional_programming` +* :ref:`cython_interface` .. Sage development .. ---------------- diff --git a/src/doc/en/thematic_tutorials/toctree.rst b/src/doc/en/thematic_tutorials/toctree.rst index 6d43d89fe69..d95a4cc14db 100644 --- a/src/doc/en/thematic_tutorials/toctree.rst +++ b/src/doc/en/thematic_tutorials/toctree.rst @@ -19,3 +19,4 @@ Thematic tutorial document tree tutorial-objects-and-classes functional_programming coercion_and_categories + cython_interface From d941a1762c4cfd4da9d186992d19a80e572b6c4d Mon Sep 17 00:00:00 2001 From: Andrey Novoseltsev Date: Thu, 8 Jan 2015 14:08:27 -0700 Subject: [PATCH 142/217] Don't create toric sublattices with rational generators. --- src/sage/geometry/toric_lattice.py | 46 ++++++++++++++---------------- 1 file changed, 21 insertions(+), 25 deletions(-) diff --git a/src/sage/geometry/toric_lattice.py b/src/sage/geometry/toric_lattice.py index 2bb12a197d5..0d4ab50a73f 100644 --- a/src/sage/geometry/toric_lattice.py +++ b/src/sage/geometry/toric_lattice.py @@ -699,7 +699,7 @@ def saturation(self): S = super(ToricLattice_generic, self).saturation() return S if is_ToricLattice(S) else self.ambient_module().submodule(S) - def span(self, *args, **kwds): + def span(self, gens, base_ring=ZZ, *args, **kwds): """ Return the span of the given generators. @@ -707,6 +707,8 @@ def span(self, *args, **kwds): - ``gens`` -- list of elements of the ambient vector space of ``self``. + + - ``base_ring`` -- (default: `\ZZ`) base ring for the generated module. OUTPUT: @@ -735,19 +737,14 @@ def span(self, *args, **kwds): ArithmeticError: Argument gens (= [N(0, 1, 0)]) does not generate a submodule of self. """ - if len(args) > 1: - base_ring = args[1] - elif "base_ring" in kwds: - base_ring = kwds["base_ring"] + A = self.ambient_module() + if base_ring is ZZ and all(g in A for g in gens): + return ToricLattice_sublattice(A, gens) else: - base_ring = None - if base_ring is None or base_ring is ZZ: - gens = args[0] if args else kwds["gens"] - return ToricLattice_sublattice(self.ambient_module(), gens) - else: - return super(ToricLattice_generic, self).span(*args, **kwds) + return super(ToricLattice_generic, self).span(gens, base_ring, + *args, **kwds) - def span_of_basis(self, *args, **kwds): + def span_of_basis(self, basis, base_ring=ZZ, *args, **kwds): r""" Return the submodule with the given ``basis``. @@ -755,7 +752,9 @@ def span_of_basis(self, *args, **kwds): - ``basis`` -- list of elements of the ambient vector space of ``self``. - + + - ``base_ring`` -- (default: `\ZZ`) base ring for the generated module. + OUTPUT: - submodule spanned by ``basis``. @@ -778,7 +777,10 @@ def span_of_basis(self, *args, **kwds): sage: Ns.span_of_basis([(2,4,0)]) Sublattice sage: Ns.span_of_basis([(1/5,2/5,0), (1/7,1/7,0)]) - Sublattice <(1/5, 2/5, 0), (1/7, 1/7, 0)> + Free module of degree 3 and rank 2 over Integer Ring + User basis matrix: + [1/5 2/5 0] + [1/7 1/7 0] Of course the input basis vectors must be linearly independent:: @@ -787,18 +789,12 @@ def span_of_basis(self, *args, **kwds): ... ValueError: The given basis vectors must be linearly independent. """ - if len(args) > 1: - base_ring = args[1] - elif "base_ring" in kwds: - base_ring = kwds["base_ring"] - else: - base_ring = None - if base_ring is None or base_ring is ZZ: - return ToricLattice_sublattice_with_basis(self.ambient_module(), - *args, **kwds) + A = self.ambient_module() + if base_ring is ZZ and all(g in A for g in basis): + return ToricLattice_sublattice_with_basis(A, basis) else: - return super(ToricLattice_generic, self).span_with_basis(*args, - **kwds) + return super(ToricLattice_generic, self).span_of_basis( + basis, base_ring, *args, **kwds) class ToricLattice_ambient(ToricLattice_generic, FreeModule_ambient_pid): From 977753859b47646db350b6b655e04a697e07f1d9 Mon Sep 17 00:00:00 2001 From: kcrisman Date: Thu, 8 Jan 2015 21:55:31 -0500 Subject: [PATCH 143/217] let cvxopt correctly install spkg docs According to a bug report, > When installing cvxopt with the environment variable SAGE_SPKG_INSTALL_DOCS set to "yes", the install fails. This is due to a bug in spkg-install. On the last-but-one line of that file, it attempts to copy the docs from build/html to the install directory; however, the (pre-built) docs are in html/, not in build/html/. Changing this line fixes the bug. --- build/pkgs/cvxopt/spkg-install | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/pkgs/cvxopt/spkg-install b/build/pkgs/cvxopt/spkg-install index 81aec3c081f..bdd025c1944 100755 --- a/build/pkgs/cvxopt/spkg-install +++ b/build/pkgs/cvxopt/spkg-install @@ -58,6 +58,6 @@ if [ "x$SAGE_SPKG_INSTALL_DOCS" = xyes ] ; then rm -rf $SAGE_LOCAL/share/doc/cvxopt/html fi mkdir -p $SAGE_LOCAL/share/doc/cvxopt/html - cp -r build/html/* $SAGE_LOCAL/share/doc/cvxopt/html/ + cp -r html/* $SAGE_LOCAL/share/doc/cvxopt/html/ fi From cf85e0428f9b0a9b2c1a85472d662bf816b34fd0 Mon Sep 17 00:00:00 2001 From: Ivan Andrus Date: Thu, 8 Jan 2015 22:02:52 -0700 Subject: [PATCH 144/217] If starting notebook server fails, try starting it in Terminal --- src/mac-app/start-sage.sh | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/mac-app/start-sage.sh b/src/mac-app/start-sage.sh index e314e354f62..220a3ee5e1d 100755 --- a/src/mac-app/start-sage.sh +++ b/src/mac-app/start-sage.sh @@ -50,12 +50,18 @@ echo Checking install location >> "$SAGE_LOG" ./local/bin/sage-location >> "$SAGE_LOG" 2>> "$SAGE_LOG" || exit 1 echo Checking existence of notebook directory >> "$SAGE_LOG" -if [ -d $DOT_SAGE/sage_notebook.sagenb ]; then +if [ -e $DOT_SAGE/sage_notebook.sagenb/users.pickle ]; then echo Starting Notebook >> "$SAGE_LOG" # $3 is not quoted because it comes as one argument from the app, # so we need the shell to parse it here. ./sage --notebook $3 >> "$SAGE_LOG" 2>> "$SAGE_LOG" else + false +fi + +# If it failed to start or it hasn't been run before, hope that we can +# fix it by running in a terminal to allow typing in a password. +if [ $? != 0 ]; then # if Terminal.app is not running before it is activated by # osascript, then it inherits the environment from osascript. # This includes SAGE_ENV_SOURCED which causes problems because @@ -70,6 +76,6 @@ else -e " do script \"'$SAGE_ROOT'/sage --notebook\"" \ -e 'end' # We don't include $3 here since this should only happen the first time - # they run it, and then we don't have to worry about quoting it. + # they run it, and this way we don't have to worry about quoting it. fi exit $? From 2ad9b510df07b6f97741f934e9830b3b405401e8 Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Fri, 9 Jan 2015 11:18:34 +0530 Subject: [PATCH 145/217] trac #17605: Make it work for libraries --- .../thematic_tutorials/cython_interface.rst | 66 +++++++++++++++++-- 1 file changed, 59 insertions(+), 7 deletions(-) diff --git a/src/doc/en/thematic_tutorials/cython_interface.rst b/src/doc/en/thematic_tutorials/cython_interface.rst index a545b30e456..8dc809af166 100644 --- a/src/doc/en/thematic_tutorials/cython_interface.rst +++ b/src/doc/en/thematic_tutorials/cython_interface.rst @@ -1,15 +1,19 @@ -.. _cython_interface: .. nodoctest -================================ -How to call a C code from Sage ? -================================ + +.. _cython_interface: + +======================================================== +How to call a C code (or a compiled library) from Sage ? +======================================================== If you have some C/C++ code that you would like to call from Sage for your own use, this document is for you. - Do you want to **contibute** to Sage by adding your interface to its code? The (more complex) instructions are `available here - `_ + `_. + +.. _section-cython-interface-helloworld: Calling "hello_world()" from hello.c ------------------------------------ @@ -59,7 +63,7 @@ The following example defines a function taking and returning ``int *`` pointers, and involves some memory allocation. The C code defines a function whose purpose is to return the sum of two vectors as a third vector. -**The C file**:: +**The C file** (``double_vector.c``):: #include @@ -76,7 +80,7 @@ whose purpose is to return the sum of two vectors as a third vector. return sum; } -**The Cython file**:: +**The Cython file** (``double_vector_sage.pyx``):: cdef extern from "double_vector.c": int * sum_of_two_vectors(int n, int * vec1, int * vec2) @@ -111,3 +115,51 @@ whose purpose is to return the sum of two vectors as a third vector. Compiling ./double_vector_sage.pyx... sage: sage_sum_of_vectors(3,[1,1,1],[2,3,4]) [3, 4, 5] + +Calling code from a compiled library +------------------------------------ + +The procedure is very similar again. For our purposes, we build a library from +the file **hello.c** defined in :ref:`section-cython-interface-helloworld` +(stripped from its ``main()`` function), and a **hello.h** header file. :: + + ~/a$ cat hello.c + #include + + void hello_world(){ + printf("Hello World\n"); + } + ~/a$ cat hello.h + void hello_world(); + +We can now **compile it** as a library:: + + ~/a$ gcc -c -Wall -Werror -fpic hello.c + ~/a$ gcc -shared -o libhello.so hello.o + +The only files that we need now are ``hello.h`` and ``libhello.so`` (you can +remove the others if you like). We must now indicate the location of the ``.so`` +and ``.h`` files in the header of our ``.pyx`` file: :: + + ~/a$ cat hello_sage.pyx + #clib /home/ncohen/a/hello + + cdef extern from "hello.h": + void hello_world() + + def my_bridge_function(): + hello_world() # This is the C function from hello.c + +.. NOTE:: + + The instruction ``#clib /home/ncohen/a/hello`` indicates that the library is + actually named ``/home/ncohen/a/libhello.so``. Change it according to your + needs. For more information about these instructions, see + :func:`~sage.misc.cython.cython`. + +We can now **load** this file in Sage and **call** the function:: + + sage: %runfile hello_sage.pyx + Compiling ./hello_sage.pyx... + sage: my_bridge_function() + Hello World From e03f93ae3016870383b50bf2a3abea4b5234e51b Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Thu, 8 Jan 2015 18:05:40 +0100 Subject: [PATCH 146/217] trac #16603: change keywords for `rook_vector` + doc --- src/sage/graphs/bipartite_graph.py | 16 +-- src/sage/matrix/matrix2.pyx | 150 +++++++++++++++++------------ 2 files changed, 101 insertions(+), 65 deletions(-) diff --git a/src/sage/graphs/bipartite_graph.py b/src/sage/graphs/bipartite_graph.py index c6f0c428df3..d730f97cac9 100644 --- a/src/sage/graphs/bipartite_graph.py +++ b/src/sage/graphs/bipartite_graph.py @@ -894,18 +894,22 @@ def plot(self, *args, **kwds): kwds["pos"] = pos return Graph.plot(self, *args, **kwds) - def matching_polynomial(self, algorithm="Godsil", complement=True, name=None): + def matching_polynomial(self, algorithm="Godsil", name=None): r""" Computes the matching polynomial. + If `p(G, k)` denotes the number of `k`-matchings (matchings with `k` edges) + in `G`, then the *matching polynomial* is defined as [Godsil93]_: + + .. MATH:: + + \mu(x)=\sum_{k \geq 0} (-1)^k p(G,k) x^{n-2k} + INPUT: - ``algorithm`` - a string which must be either "Godsil" (default) or "rook"; "rook" is usually faster for larger graphs. - - ``complement`` - Boolean (default: ``True``) whether to compute the - rook vector from its complement; it is used in the "rook" algorithm. - - ``name`` - optional string for the variable name in the polynomial. EXAMPLE:: @@ -936,11 +940,11 @@ def matching_polynomial(self, algorithm="Godsil", complement=True, name=None): True """ if algorithm == "Godsil": - return Graph.matching_polynomial(self, complement=False, name=name) + return Graph.matching_polynomial(self, name=name) elif algorithm == "rook": from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing A = self.reduced_adjacency_matrix() - a = A.rook_vector(complement=complement) + a = A.rook_vector() m = A.nrows() n = A.ncols() b = [0]*(m + n + 1) diff --git a/src/sage/matrix/matrix2.pyx b/src/sage/matrix/matrix2.pyx index dd449a36aa6..be8746ae449 100644 --- a/src/sage/matrix/matrix2.pyx +++ b/src/sage/matrix/matrix2.pyx @@ -739,8 +739,8 @@ cdef class Matrix(matrix1.Matrix): sage: B.permanent() 36.0000000000000 - The permanent above is directed to the Sloane's sequence OEIS - A079908(3) = 36, "The Dancing School Problems" + The permanent above is directed to the Sloane's sequence :oeis:`A079908` + ("The Dancing School Problems") for which the third term is 36: :: @@ -947,7 +947,7 @@ cdef class Matrix(matrix1.Matrix): pm = pm + self.matrix_from_rows_and_columns(rows, cols).permanent() return pm - def rook_vector(self, algorithm="ButeraPernici", complement=True): + def rook_vector(self, algorithm="ButeraPernici", complement=False, use_complement=None): r""" Return the rook vector of this matrix. @@ -976,12 +976,18 @@ cdef class Matrix(matrix1.Matrix): - ``self`` -- an `m` by `n` matrix - - ``algorithm`` - a string which must be either "Ryser" or + - ``algorithm`` -- a string which must be either "Ryser" or "ButeraPernici" (default) or "Godsil"; Ryser one might be faster on simple and small instances. Godsil only accepts input in 0,1. - - ``complement`` -- Boolean (default: ``True``) whether to compute the - rook vector of a (0,1)-matrix from its complement. + - ``complement`` -- boolean (default: ``False``) wether we consider the + rook vector of the complement matrix. If set to ``True`` then the + matrix must have entries in {0, 1} and the complement matrix is the + one for which the 0's are replaced by 1's and 1's by 0's. + + - ``use_complement`` -- Boolean (default: ``None``) whether to compute the + rook vector of a (0,1)-matrix from its complement. By default this is + determined by the density of ones in the matrix. EXAMPLES: @@ -999,13 +1005,14 @@ cdef class Matrix(matrix1.Matrix): x^8 + 64*x^7 + 1568*x^6 + 18816*x^5 + 117600*x^4 + 376320*x^3 + 564480*x^2 + 322560*x + 40320 - The number of desarrangements of length `n` is the permanent + The number of derangements of length `n` is the permanent of a matrix with 0 on the diagonal and 1 elsewhere; - for `n=21` it is `18795307255050944540` (see OEIS A000166): + for `n=21` it is `18795307255050944540` (see :oeis:`A000166`): - sage: n = 21 - sage: A = matrix([[int(j != i) for i in range(n)] for j in range(n)]) - sage: A.rook_vector()[-1] + sage: A = identity_matrix(21) + sage: A.rook_vector(complement=True)[-1] + 18795307255050944540 + sage: Derangements(21).cardinality() 18795307255050944540 An other example that we convert into a rook polynomial:: @@ -1041,8 +1048,8 @@ cdef class Matrix(matrix1.Matrix): r_k(A) = \sum_{j=0}^k (-1)^j \binom{m-j}{k-j} \binom{n-j}{k-j} (k-j)! r_j(B) - see [Riordan] or the introductory text [Allenby]. - + see [Riordan] or the introductory text [Allenby]. This can be done + setting the argument ``use_complement`` to ``True``. An example with an exotic matrix (for which only Butera-Pernici and Ryser algorithms are available):: @@ -1075,11 +1082,16 @@ cdef class Matrix(matrix1.Matrix): [1, 8, 12] sage: matrix.ones(4, 2).rook_vector("Godsil") [1, 8, 12] - sage: m = matrix(8,9,[int(j != i) for i in range(9) for j in range(8)]) - sage: m.rook_vector() - [1, 64, 1568, 18816, 117600, 376320, 564480, 322560, 40320] - sage: m.rook_vector(complement=False) - [1, 64, 1568, 18816, 117600, 376320, 564480, 322560, 40320] + sage: m = matrix(ZZ,4,5) + sage: m[:4,:4] = identity_matrix(4) + sage: for algorithm in ("Godsil","Ryser","ButeraPernici"): + ....: v = m.rook_vector(complement=True, use_complement=True, algorithm=algorithm) + ....: if v != [1, 16, 78, 128, 53]: + ....: print "ERROR with algorithm={} use_complement=True".format(algorithm) + ....: v = m.rook_vector(complement=True, use_complement=False, algorithm=algorithm) + ....: v = m.rook_vector(complement=True, use_complement=False) + ....: if v != [1, 16, 78, 128, 53]: + ....: print "ERROR with algorithm={} use_complement=False".format(algorithm) REFERENCES: @@ -1093,61 +1105,81 @@ cdef class Matrix(matrix1.Matrix): - Jaap Spies (2006-02-24) - Mario Pernici (2014-07-01) """ - m = self._nrows - n = self._ncols - mn = min(m,n) - - # z2 flag for self[i, j] in {0, 1} - z2 = True - num_ones = 1 + cdef Py_ssize_t i,j + cdef unsigned int num_ones + cdef int m = self._nrows + cdef int n = self._ncols + cdef int mn = min(m,n) + cdef Matrix B zero = self.base_ring().zero() one = self.base_ring().one() - for i in range(m): - for j in range(n): - x = self.get_unsafe(i, j) - if x != zero: - if x != one: - z2 = False - if algorithm == "Godsil": - raise ValueError("coefficients must be zero or one, but we have '{}' in position ({},{}).".format(x,i,j)) - else: - num_ones += 1 - - fill = 0.55 - if z2 and complement and num_ones > fill*m*n: - from sage.matrix.constructor import matrix - B = [[1-self.get_unsafe(i, j) for j in range(n)] for i in range(m)] - B = matrix(B) - b = B.rook_vector(algorithm=algorithm, complement=False) - a = [1] - c1 = 1 - for k in range(1, mn + 1): - c1 = c1*(m-k+1)*(n-k+1)/k - c = c1 - s = c*b[0] + (-1)**k*b[k] - for j in range(1, k): - c = -c*(k-j+1)/((m-j+1)*(n-j+1)) - s += c*b[j] - a.append(s) - return a - if algorithm == "Ryser": - return [self.permanental_minor(k,algorithm="Ryser") for k in range(mn+1)] + # we first run through the coefficients of the matrix to compute the + # number of non-zero coefficients and to see whether or not it contains + # only elements in {0,1}... but this is not always needed + if complement or use_complement is None or algorithm == "Godsil": + # z2 flag is True if all coefficients belong to {0,1} + z2 = True + num_ones = 1 + for i in range(m): + for j in range(n): + x = self.get_unsafe(i,j) + if x != zero: + if x != one: + z2 = False + break + else: + num_ones += 1 + else: + continue + break + + if not z2 and (complement or algorithm == "Godsil"): + raise ValueError("coefficients must be zero or one, but we have '{}' in position ({},{}).".format(x,i,j)) + + if use_complement is None: + use_complement = z2 and num_ones > 0.55 * m * n + + if use_complement: + B = self.new_matrix() + for i in range(m): + for j in range(n): + B.set_unsafe(i,j, one-self.get_unsafe(i,j)) + b = B.rook_vector(algorithm=algorithm, use_complement=False) + complement = not complement + + elif algorithm == "Ryser": + b = [self.permanental_minor(k,algorithm="Ryser") for k in range(mn+1)] elif algorithm == "ButeraPernici": p = permanental_minor_polynomial(self) - return [p[k] for k in range(mn+1)] + b = [p[k] for k in range(mn+1)] elif algorithm == "Godsil": from sage.graphs.bipartite_graph import BipartiteGraph - g = BipartiteGraph(self) - p = g.matching_polynomial(complement=False) + p = BipartiteGraph(self).matching_polynomial() d = p.degree() - return [p[i]*(-1)**((d - i)/2) for i in range(d, d-2*mn-1, -2)] + b = [p[i]*(-1)**((d - i)/2) for i in range(d, d-2*mn-1, -2)] else: raise ValueError('algorithm must be one of "Ryser", "ButeraPernici" or "Godsil".') + # now compute the permanental minor of the complement matrix if needed + if complement: + a = [one] + c1 = 1 + for k in range(1, mn + 1): + c1 = c1*(m-k+1)*(n-k+1)/k + c = c1 + s = c*b[0] + (-one)**k*b[k] + for j in range(1, k): + c = -c*(k-j+1)/((m-j+1)*(n-j+1)) + s += c*b[j] + a.append(s) + return a + else: + return b + def minors(self, k): r""" Return the list of all `k \times k` minors of self. From 96177d8ed62e5c88325c95f168406b27249b2ea1 Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Fri, 9 Jan 2015 19:17:10 +0530 Subject: [PATCH 147/217] trac #17605: no trace that Sage is written by jews --- .../thematic_tutorials/cython_interface.rst | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/doc/en/thematic_tutorials/cython_interface.rst b/src/doc/en/thematic_tutorials/cython_interface.rst index 8dc809af166..1f7c37b3a67 100644 --- a/src/doc/en/thematic_tutorials/cython_interface.rst +++ b/src/doc/en/thematic_tutorials/cython_interface.rst @@ -20,7 +20,7 @@ Calling "hello_world()" from hello.c Let us suppose that you have in your current directory a file named hello.c:: - ~/a$ cat hello.c + [user@localhost ~/my_dir/] cat hello.c #include void hello_world(){ @@ -30,14 +30,14 @@ Let us suppose that you have in your current directory a file named hello.c:: void main(){ hello_world(); } - ~/a$ gcc hello.c -o hello; ./hello + [user@localhost ~/my_dir/] gcc hello.c -o hello; ./hello Hello World In order to call this function from Sage, you must create a Cython file (i.e. a file whose extension is .pyx). This file contains a header containing the signature of the function that you want to call:: - ~/a$ cat hello_sage.pyx + [user@localhost ~/my_dir/] cat hello_sage.pyx cdef extern from "hello.c": void hello_world() @@ -123,26 +123,26 @@ The procedure is very similar again. For our purposes, we build a library from the file **hello.c** defined in :ref:`section-cython-interface-helloworld` (stripped from its ``main()`` function), and a **hello.h** header file. :: - ~/a$ cat hello.c + [user@localhost ~/my_dir/] cat hello.c #include void hello_world(){ printf("Hello World\n"); } - ~/a$ cat hello.h + [user@localhost ~/my_dir/] cat hello.h void hello_world(); We can now **compile it** as a library:: - ~/a$ gcc -c -Wall -Werror -fpic hello.c - ~/a$ gcc -shared -o libhello.so hello.o + [user@localhost ~/my_dir/] gcc -c -Wall -Werror -fpic hello.c + [user@localhost ~/my_dir/] gcc -shared -o libhello.so hello.o The only files that we need now are ``hello.h`` and ``libhello.so`` (you can remove the others if you like). We must now indicate the location of the ``.so`` and ``.h`` files in the header of our ``.pyx`` file: :: - ~/a$ cat hello_sage.pyx - #clib /home/ncohen/a/hello + [user@localhost ~/my_dir/] cat hello_sage.pyx + #clib /home/username/my_dir/hello cdef extern from "hello.h": void hello_world() @@ -152,9 +152,9 @@ and ``.h`` files in the header of our ``.pyx`` file: :: .. NOTE:: - The instruction ``#clib /home/ncohen/a/hello`` indicates that the library is - actually named ``/home/ncohen/a/libhello.so``. Change it according to your - needs. For more information about these instructions, see + The instruction ``#clib /home/username/my_dir/hello`` indicates that the + library is actually named ``/home/username/my_dir/hello``. Change it + according to your needs. For more information about these instructions, see :func:`~sage.misc.cython.cython`. We can now **load** this file in Sage and **call** the function:: From bd0dae3b8577fe2e339fca82934a388639e95668 Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Fri, 9 Jan 2015 20:33:48 +0530 Subject: [PATCH 148/217] trac #17605: Errors... --- src/doc/en/thematic_tutorials/cython_interface.rst | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/doc/en/thematic_tutorials/cython_interface.rst b/src/doc/en/thematic_tutorials/cython_interface.rst index 1f7c37b3a67..b58bd1a9822 100644 --- a/src/doc/en/thematic_tutorials/cython_interface.rst +++ b/src/doc/en/thematic_tutorials/cython_interface.rst @@ -24,11 +24,12 @@ Let us suppose that you have in your current directory a file named hello.c:: #include void hello_world(){ - printf("Hello World\n"); + printf("Hello World\n"); } - void main(){ - hello_world(); + int main(){ + hello_world(); + return 0; } [user@localhost ~/my_dir/] gcc hello.c -o hello; ./hello Hello World @@ -39,13 +40,13 @@ signature of the function that you want to call:: [user@localhost ~/my_dir/] cat hello_sage.pyx cdef extern from "hello.c": - void hello_world() + void hello_world() - def my_interface_function(): + def my_bridge_function(): hello_world() # This is the C function from hello.c You can now load this file in Sage, and call the C code though the Python -function ``my_interface_function``:: +function ``my_bridge_function``:: sage: %runfile hello_sage.pyx Compiling ./hello_sage.pyx... From e40fe19cf613d6f64072a487371a933095b5a0da Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Sat, 10 Jan 2015 10:03:55 +0530 Subject: [PATCH 149/217] trac #17592: Typo/Rephrase --- src/doc/en/developer/coding_basics.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/doc/en/developer/coding_basics.rst b/src/doc/en/developer/coding_basics.rst index abeb127a286..d79049fefa1 100644 --- a/src/doc/en/developer/coding_basics.rst +++ b/src/doc/en/developer/coding_basics.rst @@ -623,8 +623,8 @@ written. checked, as they are the most likely to be broken, now or in the future. This probably belongs to the TESTS block (see :ref:`section-docstring-function`). -- **Random of systematic tests** of all instances of small size, or of some - random instances if possible. +- **Systematic tests** of all small-sized inputs, or tests of **random** + instances if possible. .. NOTE:: From 5f20f0a3872d2f3f12b3c716c9a4e445530969bc Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Sat, 10 Jan 2015 10:38:42 +0530 Subject: [PATCH 150/217] trac #17605: Remove useless (and problematic) compilation (+indentation fixes) --- .../en/thematic_tutorials/cython_interface.rst | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/src/doc/en/thematic_tutorials/cython_interface.rst b/src/doc/en/thematic_tutorials/cython_interface.rst index b58bd1a9822..41d73c7bbfe 100644 --- a/src/doc/en/thematic_tutorials/cython_interface.rst +++ b/src/doc/en/thematic_tutorials/cython_interface.rst @@ -27,13 +27,6 @@ Let us suppose that you have in your current directory a file named hello.c:: printf("Hello World\n"); } - int main(){ - hello_world(); - return 0; - } - [user@localhost ~/my_dir/] gcc hello.c -o hello; ./hello - Hello World - In order to call this function from Sage, you must create a Cython file (i.e. a file whose extension is .pyx). This file contains a header containing the signature of the function that you want to call:: @@ -121,14 +114,14 @@ Calling code from a compiled library ------------------------------------ The procedure is very similar again. For our purposes, we build a library from -the file **hello.c** defined in :ref:`section-cython-interface-helloworld` -(stripped from its ``main()`` function), and a **hello.h** header file. :: +the file **hello.c** defined in :ref:`section-cython-interface-helloworld`, and +a **hello.h** header file. :: [user@localhost ~/my_dir/] cat hello.c #include void hello_world(){ - printf("Hello World\n"); + printf("Hello World\n"); } [user@localhost ~/my_dir/] cat hello.h void hello_world(); @@ -146,10 +139,10 @@ and ``.h`` files in the header of our ``.pyx`` file: :: #clib /home/username/my_dir/hello cdef extern from "hello.h": - void hello_world() + void hello_world() def my_bridge_function(): - hello_world() # This is the C function from hello.c + hello_world() # This is the C function from hello.c .. NOTE:: From fb4a68048d5acd303154311b6c58027aa63bf962 Mon Sep 17 00:00:00 2001 From: "John H. Palmieri" Date: Fri, 9 Jan 2015 21:44:39 -0800 Subject: [PATCH 151/217] #17592: referee fixes --- src/doc/en/developer/coding_basics.rst | 52 ++++++++++++++------------ 1 file changed, 29 insertions(+), 23 deletions(-) diff --git a/src/doc/en/developer/coding_basics.rst b/src/doc/en/developer/coding_basics.rst index d79049fefa1..4b296124d02 100644 --- a/src/doc/en/developer/coding_basics.rst +++ b/src/doc/en/developer/coding_basics.rst @@ -498,10 +498,10 @@ there is not one already. That is, you can do the following:: LaTeX Typesetting ----------------- -In Sage's documentation LaTeX code is allowed, and is marked with **backticks or +In Sage's documentation LaTeX code is allowed and is marked with **backticks or dollar signs**: - ```x^2 + y^2 = 1``` and ``$x^2 + y^2 = 1$`` both yield `x^2 + y^2 = 1` + ```x^2 + y^2 = 1``` and ``$x^2 + y^2 = 1$`` both yield `x^2 + y^2 = 1`. **Backslashes:** For LaTeX commands containing backslashes, either use double backslashes or begin the docstring with a ``r"""`` instead of ``"""``. Both of @@ -517,7 +517,7 @@ the following are valid:: Return $\sin(x)$. """ -**MATH block:** It is similar to LaTeX' syntax ``\[\]`` (or +**MATH block:** This is similar to the LaTeX syntax ``\[\]`` (or ``$$$$``). For instance:: .. MATH:: @@ -548,15 +548,21 @@ The **aligned** environment works as it does in LaTeX:: g(x) & = x^x - f(x - 2) \end{aligned} -For **non-math** LaTeX environments (like ``align``), the pdf documentation -will not compile unless you add a **:nowrap:** flag to the MATH mode:: +When building the PDF documentation, everything is translated to LaTeX +and each MATH block is automatically wrapped in a math environment -- +in particular, it is turned into ``\begin{gather} block +\end{gather}``. So if you want to use a LaTeX environment (like +``align``) which in ordinary LaTeX would not be wrapped like this, you +must add a **:nowrap:** flag to the MATH mode. See also `Sphinx's +documentation for math blocks +`_. :: .. MATH:: :nowrap: \begin{align} - 1+...+n &= n(n+1)/2\\ - &= O(n^2)\\ + 1+...+n &= n(n+1)/2\\ + &= O(n^2)\\ \end{tabular} .. MATH:: @@ -568,8 +574,8 @@ will not compile unless you add a **:nowrap:** flag to the MATH mode:: \end{align} **Readability balance:** in the interactive console, LaTeX formulas contained in -the documentation are represented by their LaTeX code (stripped from -backslashes). In this situation ``\\frac{a}{b}`` is less readable than ``a/b`` +the documentation are represented by their LaTeX code (with +backslashes stripped). In this situation ``\\frac{a}{b}`` is less readable than ``a/b`` or ``a b^{-1}`` (some users may not even know LaTeX code). Make it pleasant for everybody as much as you can manage. @@ -577,13 +583,13 @@ everybody as much as you can manage. standard rings and fields using the locally-defined macro ``\\Bold`` (e.g. ``\\Bold{Z}`` gives `\Bold{Z}`). -**Shortcuts** are available which preserve readability, e.g. ``\\Z`` (`\\Z`), -``\\R`` (`\\R`), ``\\C`` (`\\C`), and ``\\Q`` (`\\Q`). They appear as +**Shortcuts** are available which preserve readability, e.g. ``\\ZZ`` (`\ZZ`), +``\\RR`` (`\RR`), ``\\CC`` (`\CC`), and ``\\QQ`` (`\QQ`). They appear as LaTeX-formatted ``\\Bold{Z}`` in the html manual, and as ``Z`` in the -interactive help. Other examples: ``\\GF_{q}`` (`\\GF_{q}`) and ``\\Zmod{p}`` +interactive help. Other examples: ``\\GF{q}`` (`\GF{q}`) and ``\\Zmod{p}`` (`\Zmod{p}`). -See the file ``$SAGE_ROOT/src/sage/misc/latex_macros.py`` for a full list and +See the file ``SAGE_ROOT/src/sage/misc/latex_macros.py`` for a full list and for details about how to add more macros. .. _section-doctest-writing: @@ -595,18 +601,18 @@ The examples from Sage's documentation have a double purpose: - They provide **illustrations** of the code's usage to the users -- They are **tests** that are checked before each release, helping us avoid the - apparition of new bugs. +- They are **tests** that are checked before each release, helping us avoid + new bugs. All new doctests added to Sage should **pass all tests** (see :ref:`chapter-doctesting`), i.e. running ``sage -t your_file.py`` should not give any error messages. Below are instructions about how doctests should be written. -.. centered:: **1) What doctests should test** +**What doctests should test:** -- **Interesting examples** of what the function can do. This is what will be the - most meaningful to a lost user. It is also the occasion to check famous +- **Interesting examples** of what the function can do. This will be the + most helpful to a lost user. It is also the occasion to check famous theorems (just in case):: sage: is_prime(6) # 6 is not prime @@ -629,10 +635,10 @@ written. .. NOTE:: Note that **TestSuites** are an automatic way to generate some of these - tests in specific situations. - ``SAGE_ROOT/src/sage/modular/modsym/tests.py``. + tests in specific situations. See + ``SAGE_ROOT/src/sage/misc/sage_unittest.py``. -.. centered:: **2) The syntax** +**The syntax:** - **Environment:** doctests should work if you copy/paste them in Sage's interactive console. For example, the function ``AA()`` in the file @@ -646,8 +652,8 @@ written. Sage does not know about the function ``AA()`` by default, so it needs to be imported before it is tested. Hence the first line in the example. -- **Preparsing:** As in Sage's console, `4/3` return `4/3` and not `1` as in - Python. Testing occurs with full Sage preparsing of input within the standard +- **Preparsing:** As in Sage's console, `4/3` returns `4/3` and not `1` as in + Python 2.7. Testing occurs with full Sage preparsing of input within the standard Sage shell environment, as described in :ref:`section-preparsing`. - **Writing files:** If a test outputs to a file, the file should be a temporary From 49eac6fa5956bd46be27182b5eae887d9632e450 Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Sat, 10 Jan 2015 15:52:13 +0530 Subject: [PATCH 152/217] trac #17582: More comments --- .../graphs/graph_decompositions/bandwidth.pyx | 34 ++++++++++++++----- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/src/sage/graphs/graph_decompositions/bandwidth.pyx b/src/sage/graphs/graph_decompositions/bandwidth.pyx index 726ae18c9df..5512cf270a2 100644 --- a/src/sage/graphs/graph_decompositions/bandwidth.pyx +++ b/src/sage/graphs/graph_decompositions/bandwidth.pyx @@ -313,40 +313,56 @@ cdef bint bandwidth_C(int n, int k, i = 0 while True: + + # There are (n-i) choices for vertex i, as i-1 have already been + # determined. Thus, i<=current[i] pi or ith_range_array[i][vi].M < pi): continue - # swap + # As the choice is admissible, we update left_to_order so that + # left_to_order[i] = vi. left_to_order[i], left_to_order[current[i]] = left_to_order[current[i]], left_to_order[i] + + # vi is at position pi in the final ordering. ordering[pi] = vi - if i == n-1: # We did it ! + # If we found the position of the nth vertex, we are done. + if i == n-1: return 1 - # build the range array of depth i+1 knowing that vertex vi is at - # position pi. + # As vertex vi has been assigned position pi, we use that information to + # update the intervals of admissible positions of all other vertices. # - # k*d[v][vi] >= |p_v-p_{vi}| + # \forall v, k*d[v][vi] >= |p_v-p_{vi}| (see module documentation) for v in range(n): radius = k*d[v][vi] ith_range_array[i+1][v].m = max( ith_range_array[i][v].m,pi-radius) ith_range_array[i+1][v].M = min( ith_range_array[i][v].M,pi+radius) - # check feasibility at depth i+1 + # Check the feasibility of a matching with the updated intervals of + # admissible positions (see module doc). + # + # If it is possible we explore deeper, otherwise we undo the changes as + # pi is not a good position for vi after all. if is_matching_feasible(n,ith_range_array[i+1],range_array_tmp, index_array_tmp): i += 1 current[i] = i-1 From 4e37400aaa42d454ac8fee1796edbef201d21b8a Mon Sep 17 00:00:00 2001 From: Sebastien Gouezel Date: Sat, 10 Jan 2015 21:11:22 +0100 Subject: [PATCH 153/217] Add missing library gmp in module_list.py --- src/module_list.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/module_list.py b/src/module_list.py index 0684b954b7c..4b0cc1da1d5 100755 --- a/src/module_list.py +++ b/src/module_list.py @@ -426,7 +426,8 @@ def uname_specific(name, value, alternative): sources = ['sage/graphs/graph_generators_pyx.pyx']), Extension('sage.graphs.distances_all_pairs', - sources = ['sage/graphs/distances_all_pairs.pyx']), + sources = ['sage/graphs/distances_all_pairs.pyx'], + libraries = ['gmp']), Extension('sage.graphs.base.static_dense_graph', sources = ['sage/graphs/base/static_dense_graph.pyx']), @@ -515,7 +516,8 @@ def uname_specific(name, value, alternative): sources = ['sage/graphs/genus.pyx']), Extension('sage.graphs.hyperbolicity', - sources = ['sage/graphs/hyperbolicity.pyx']), + sources = ['sage/graphs/hyperbolicity.pyx'], + libraries = ['gmp']), ################################ ## From 2f37b6fc906262f0cda839734eec8ab6f9cbf389 Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Sun, 11 Jan 2015 12:10:00 +0530 Subject: [PATCH 154/217] trac #17605: Syntax highlighting --- .../thematic_tutorials/cython_interface.rst | 39 ++++++++++++------- 1 file changed, 25 insertions(+), 14 deletions(-) diff --git a/src/doc/en/thematic_tutorials/cython_interface.rst b/src/doc/en/thematic_tutorials/cython_interface.rst index 41d73c7bbfe..0c607d36147 100644 --- a/src/doc/en/thematic_tutorials/cython_interface.rst +++ b/src/doc/en/thematic_tutorials/cython_interface.rst @@ -18,9 +18,10 @@ use, this document is for you. Calling "hello_world()" from hello.c ------------------------------------ -Let us suppose that you have in your current directory a file named hello.c:: +Let us suppose that you have a file named ``~/my_dir/hello.c`` containing: + +.. code-block:: c - [user@localhost ~/my_dir/] cat hello.c #include void hello_world(){ @@ -28,10 +29,11 @@ Let us suppose that you have in your current directory a file named hello.c:: } In order to call this function from Sage, you must create a Cython file (i.e. a -file whose extension is .pyx). This file contains a header containing the -signature of the function that you want to call:: +file whose extension is .pyx). Here, ``~/my_dir/hello_sage.pyx`` contains a +header describing the signature of the function that you want to call: + +.. code-block:: cython - [user@localhost ~/my_dir/] cat hello_sage.pyx cdef extern from "hello.c": void hello_world() @@ -57,7 +59,9 @@ The following example defines a function taking and returning ``int *`` pointers, and involves some memory allocation. The C code defines a function whose purpose is to return the sum of two vectors as a third vector. -**The C file** (``double_vector.c``):: +**The C file** (``double_vector.c``) + +.. code-block:: c #include @@ -74,7 +78,9 @@ whose purpose is to return the sum of two vectors as a third vector. return sum; } -**The Cython file** (``double_vector_sage.pyx``):: +**The Cython file** (``double_vector_sage.pyx``) + +.. code-block:: cython cdef extern from "double_vector.c": int * sum_of_two_vectors(int n, int * vec1, int * vec2) @@ -114,16 +120,20 @@ Calling code from a compiled library ------------------------------------ The procedure is very similar again. For our purposes, we build a library from -the file **hello.c** defined in :ref:`section-cython-interface-helloworld`, and -a **hello.h** header file. :: +the file ``~/my_dir/hello.c``: + +.. code-block:: c - [user@localhost ~/my_dir/] cat hello.c #include void hello_world(){ printf("Hello World\n"); - } - [user@localhost ~/my_dir/] cat hello.h + } + +We also need a ``~/my_dir/hello.h`` header file: + +.. code-block:: c + void hello_world(); We can now **compile it** as a library:: @@ -133,9 +143,10 @@ We can now **compile it** as a library:: The only files that we need now are ``hello.h`` and ``libhello.so`` (you can remove the others if you like). We must now indicate the location of the ``.so`` -and ``.h`` files in the header of our ``.pyx`` file: :: +and ``.h`` files in the header of our ``~/my_dir/hello_sage.pyx`` file: + +.. code-block:: cython - [user@localhost ~/my_dir/] cat hello_sage.pyx #clib /home/username/my_dir/hello cdef extern from "hello.h": From 4645442d47d76426a8481a6cd9f77b04067bf053 Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Sun, 11 Jan 2015 14:36:29 +0530 Subject: [PATCH 155/217] trac #17614: Move numerical_sage to thematic_tutorials (and only that) --- .../numerical_sage/comparison_to_cython.rst | 0 src/doc/en/{ => thematic_tutorials}/numerical_sage/conf.py | 0 src/doc/en/{ => thematic_tutorials}/numerical_sage/ctypes.rst | 0 .../{ => thematic_tutorials}/numerical_sage/ctypes_examples.rst | 0 src/doc/en/{ => thematic_tutorials}/numerical_sage/cvxopt.rst | 0 src/doc/en/{ => thematic_tutorials}/numerical_sage/f2py.rst | 0 .../en/{ => thematic_tutorials}/numerical_sage/f2py_examples.rst | 0 src/doc/en/{ => thematic_tutorials}/numerical_sage/index.rst | 0 .../en/{ => thematic_tutorials}/numerical_sage/installation.rst | 0 .../numerical_sage/installation_linux.rst | 0 .../{ => thematic_tutorials}/numerical_sage/installation_osx.rst | 0 src/doc/en/{ => thematic_tutorials}/numerical_sage/mpi4py.rst | 0 .../{ => thematic_tutorials}/numerical_sage/numerical_tools.rst | 0 src/doc/en/{ => thematic_tutorials}/numerical_sage/numpy.rst | 0 .../numerical_sage/parallel_computation.rst | 0 .../numerical_sage/parallel_laplace_solver.rst | 0 src/doc/en/{ => thematic_tutorials}/numerical_sage/plotting.rst | 0 src/doc/en/{ => thematic_tutorials}/numerical_sage/scipy.rst | 0 .../numerical_sage/using_compiled_code_iteractively.rst | 0 .../en/{ => thematic_tutorials}/numerical_sage/visualization.rst | 0 src/doc/en/{ => thematic_tutorials}/numerical_sage/weave.rst | 0 21 files changed, 0 insertions(+), 0 deletions(-) rename src/doc/en/{ => thematic_tutorials}/numerical_sage/comparison_to_cython.rst (100%) rename src/doc/en/{ => thematic_tutorials}/numerical_sage/conf.py (100%) rename src/doc/en/{ => thematic_tutorials}/numerical_sage/ctypes.rst (100%) rename src/doc/en/{ => thematic_tutorials}/numerical_sage/ctypes_examples.rst (100%) rename src/doc/en/{ => thematic_tutorials}/numerical_sage/cvxopt.rst (100%) rename src/doc/en/{ => thematic_tutorials}/numerical_sage/f2py.rst (100%) rename src/doc/en/{ => thematic_tutorials}/numerical_sage/f2py_examples.rst (100%) rename src/doc/en/{ => thematic_tutorials}/numerical_sage/index.rst (100%) rename src/doc/en/{ => thematic_tutorials}/numerical_sage/installation.rst (100%) rename src/doc/en/{ => thematic_tutorials}/numerical_sage/installation_linux.rst (100%) rename src/doc/en/{ => thematic_tutorials}/numerical_sage/installation_osx.rst (100%) rename src/doc/en/{ => thematic_tutorials}/numerical_sage/mpi4py.rst (100%) rename src/doc/en/{ => thematic_tutorials}/numerical_sage/numerical_tools.rst (100%) rename src/doc/en/{ => thematic_tutorials}/numerical_sage/numpy.rst (100%) rename src/doc/en/{ => thematic_tutorials}/numerical_sage/parallel_computation.rst (100%) rename src/doc/en/{ => thematic_tutorials}/numerical_sage/parallel_laplace_solver.rst (100%) rename src/doc/en/{ => thematic_tutorials}/numerical_sage/plotting.rst (100%) rename src/doc/en/{ => thematic_tutorials}/numerical_sage/scipy.rst (100%) rename src/doc/en/{ => thematic_tutorials}/numerical_sage/using_compiled_code_iteractively.rst (100%) rename src/doc/en/{ => thematic_tutorials}/numerical_sage/visualization.rst (100%) rename src/doc/en/{ => thematic_tutorials}/numerical_sage/weave.rst (100%) diff --git a/src/doc/en/numerical_sage/comparison_to_cython.rst b/src/doc/en/thematic_tutorials/numerical_sage/comparison_to_cython.rst similarity index 100% rename from src/doc/en/numerical_sage/comparison_to_cython.rst rename to src/doc/en/thematic_tutorials/numerical_sage/comparison_to_cython.rst diff --git a/src/doc/en/numerical_sage/conf.py b/src/doc/en/thematic_tutorials/numerical_sage/conf.py similarity index 100% rename from src/doc/en/numerical_sage/conf.py rename to src/doc/en/thematic_tutorials/numerical_sage/conf.py diff --git a/src/doc/en/numerical_sage/ctypes.rst b/src/doc/en/thematic_tutorials/numerical_sage/ctypes.rst similarity index 100% rename from src/doc/en/numerical_sage/ctypes.rst rename to src/doc/en/thematic_tutorials/numerical_sage/ctypes.rst diff --git a/src/doc/en/numerical_sage/ctypes_examples.rst b/src/doc/en/thematic_tutorials/numerical_sage/ctypes_examples.rst similarity index 100% rename from src/doc/en/numerical_sage/ctypes_examples.rst rename to src/doc/en/thematic_tutorials/numerical_sage/ctypes_examples.rst diff --git a/src/doc/en/numerical_sage/cvxopt.rst b/src/doc/en/thematic_tutorials/numerical_sage/cvxopt.rst similarity index 100% rename from src/doc/en/numerical_sage/cvxopt.rst rename to src/doc/en/thematic_tutorials/numerical_sage/cvxopt.rst diff --git a/src/doc/en/numerical_sage/f2py.rst b/src/doc/en/thematic_tutorials/numerical_sage/f2py.rst similarity index 100% rename from src/doc/en/numerical_sage/f2py.rst rename to src/doc/en/thematic_tutorials/numerical_sage/f2py.rst diff --git a/src/doc/en/numerical_sage/f2py_examples.rst b/src/doc/en/thematic_tutorials/numerical_sage/f2py_examples.rst similarity index 100% rename from src/doc/en/numerical_sage/f2py_examples.rst rename to src/doc/en/thematic_tutorials/numerical_sage/f2py_examples.rst diff --git a/src/doc/en/numerical_sage/index.rst b/src/doc/en/thematic_tutorials/numerical_sage/index.rst similarity index 100% rename from src/doc/en/numerical_sage/index.rst rename to src/doc/en/thematic_tutorials/numerical_sage/index.rst diff --git a/src/doc/en/numerical_sage/installation.rst b/src/doc/en/thematic_tutorials/numerical_sage/installation.rst similarity index 100% rename from src/doc/en/numerical_sage/installation.rst rename to src/doc/en/thematic_tutorials/numerical_sage/installation.rst diff --git a/src/doc/en/numerical_sage/installation_linux.rst b/src/doc/en/thematic_tutorials/numerical_sage/installation_linux.rst similarity index 100% rename from src/doc/en/numerical_sage/installation_linux.rst rename to src/doc/en/thematic_tutorials/numerical_sage/installation_linux.rst diff --git a/src/doc/en/numerical_sage/installation_osx.rst b/src/doc/en/thematic_tutorials/numerical_sage/installation_osx.rst similarity index 100% rename from src/doc/en/numerical_sage/installation_osx.rst rename to src/doc/en/thematic_tutorials/numerical_sage/installation_osx.rst diff --git a/src/doc/en/numerical_sage/mpi4py.rst b/src/doc/en/thematic_tutorials/numerical_sage/mpi4py.rst similarity index 100% rename from src/doc/en/numerical_sage/mpi4py.rst rename to src/doc/en/thematic_tutorials/numerical_sage/mpi4py.rst diff --git a/src/doc/en/numerical_sage/numerical_tools.rst b/src/doc/en/thematic_tutorials/numerical_sage/numerical_tools.rst similarity index 100% rename from src/doc/en/numerical_sage/numerical_tools.rst rename to src/doc/en/thematic_tutorials/numerical_sage/numerical_tools.rst diff --git a/src/doc/en/numerical_sage/numpy.rst b/src/doc/en/thematic_tutorials/numerical_sage/numpy.rst similarity index 100% rename from src/doc/en/numerical_sage/numpy.rst rename to src/doc/en/thematic_tutorials/numerical_sage/numpy.rst diff --git a/src/doc/en/numerical_sage/parallel_computation.rst b/src/doc/en/thematic_tutorials/numerical_sage/parallel_computation.rst similarity index 100% rename from src/doc/en/numerical_sage/parallel_computation.rst rename to src/doc/en/thematic_tutorials/numerical_sage/parallel_computation.rst diff --git a/src/doc/en/numerical_sage/parallel_laplace_solver.rst b/src/doc/en/thematic_tutorials/numerical_sage/parallel_laplace_solver.rst similarity index 100% rename from src/doc/en/numerical_sage/parallel_laplace_solver.rst rename to src/doc/en/thematic_tutorials/numerical_sage/parallel_laplace_solver.rst diff --git a/src/doc/en/numerical_sage/plotting.rst b/src/doc/en/thematic_tutorials/numerical_sage/plotting.rst similarity index 100% rename from src/doc/en/numerical_sage/plotting.rst rename to src/doc/en/thematic_tutorials/numerical_sage/plotting.rst diff --git a/src/doc/en/numerical_sage/scipy.rst b/src/doc/en/thematic_tutorials/numerical_sage/scipy.rst similarity index 100% rename from src/doc/en/numerical_sage/scipy.rst rename to src/doc/en/thematic_tutorials/numerical_sage/scipy.rst diff --git a/src/doc/en/numerical_sage/using_compiled_code_iteractively.rst b/src/doc/en/thematic_tutorials/numerical_sage/using_compiled_code_iteractively.rst similarity index 100% rename from src/doc/en/numerical_sage/using_compiled_code_iteractively.rst rename to src/doc/en/thematic_tutorials/numerical_sage/using_compiled_code_iteractively.rst diff --git a/src/doc/en/numerical_sage/visualization.rst b/src/doc/en/thematic_tutorials/numerical_sage/visualization.rst similarity index 100% rename from src/doc/en/numerical_sage/visualization.rst rename to src/doc/en/thematic_tutorials/numerical_sage/visualization.rst diff --git a/src/doc/en/numerical_sage/weave.rst b/src/doc/en/thematic_tutorials/numerical_sage/weave.rst similarity index 100% rename from src/doc/en/numerical_sage/weave.rst rename to src/doc/en/thematic_tutorials/numerical_sage/weave.rst From 612c3cf444c1cd10e4889947db90f53b551c2b76 Mon Sep 17 00:00:00 2001 From: Nathann Cohen Date: Sun, 11 Jan 2015 14:38:21 +0530 Subject: [PATCH 156/217] trac #17614: Update the links --- src/doc/en/thematic_tutorials/index.rst | 7 ++++++- .../numerical_sage/index.rst | 2 ++ src/doc/en/thematic_tutorials/toctree.rst | 1 + src/doc/en/website/templates/index.html | 18 ------------------ 4 files changed, 9 insertions(+), 19 deletions(-) diff --git a/src/doc/en/thematic_tutorials/index.rst b/src/doc/en/thematic_tutorials/index.rst index 6476dbea510..fb732a34bed 100644 --- a/src/doc/en/thematic_tutorials/index.rst +++ b/src/doc/en/thematic_tutorials/index.rst @@ -47,7 +47,6 @@ Calculus and plotting Algebra ======= -* :ref:`linear_programming` * :ref:`group_theory` * :ref:`lie` @@ -84,6 +83,12 @@ Algebraic Combinatorics * :ref:`sage.combinat.root_system.plot` * :ref:`abelian_sandpile_model` +Numerical computations +---------------------- + +* :ref:`numerical_computing` +* :ref:`linear_programming` + .. Words .. ----- diff --git a/src/doc/en/thematic_tutorials/numerical_sage/index.rst b/src/doc/en/thematic_tutorials/numerical_sage/index.rst index 21848e4266e..3f49c0f7243 100644 --- a/src/doc/en/thematic_tutorials/numerical_sage/index.rst +++ b/src/doc/en/thematic_tutorials/numerical_sage/index.rst @@ -2,6 +2,8 @@ You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. +.. _numerical_computing: + Numerical Computing with Sage ========================================== This document is designed to introduce the reader to the tools in Sage diff --git a/src/doc/en/thematic_tutorials/toctree.rst b/src/doc/en/thematic_tutorials/toctree.rst index 6d43d89fe69..df0aeb4ea21 100644 --- a/src/doc/en/thematic_tutorials/toctree.rst +++ b/src/doc/en/thematic_tutorials/toctree.rst @@ -19,3 +19,4 @@ Thematic tutorial document tree tutorial-objects-and-classes functional_programming coercion_and_categories + numerical_sage/index diff --git a/src/doc/en/website/templates/index.html b/src/doc/en/website/templates/index.html index 42fdb27c124..f3577e4d97c 100644 --- a/src/doc/en/website/templates/index.html +++ b/src/doc/en/website/templates/index.html @@ -264,24 +264,6 @@

- - - -