From 4958fdbd915bb6d90faa9684ae1a9e2f58ca6798 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Sun, 19 Jan 2020 14:12:56 +1000 Subject: [PATCH 001/713] Adding custom __reduce__ methods to Action and subclasses. --- src/sage/categories/action.pyx | 66 +++++++++++++++++++++++++-- src/sage/structure/coerce_actions.pyx | 30 ++++++++++++ 2 files changed, 91 insertions(+), 5 deletions(-) diff --git a/src/sage/categories/action.pyx b/src/sage/categories/action.pyx index 7a007b4dba1..70386a4a5ee 100644 --- a/src/sage/categories/action.pyx +++ b/src/sage/categories/action.pyx @@ -102,6 +102,27 @@ cdef class Action(Functor): def _apply_functor(self, x): return self(x) + def __reduce__(self): + """ + Used in pickling. + + .. WARNING:: + + If you change the signature of the ``__init__`` for a subclass, + you must override this method as well. + + TESTS: + + Check that this action is can be pickled (:trac:`29031`):: + + sage: P = QQ['x'] + sage: R = (ZZ['x'])['y'] + sage: A = R.get_action(P, operator.mul, True) + sage: loads(dumps(A)) is not None + True + """ + return (type(self), (self.G, self.underlying_set(), self._is_left, self.op)) + def __call__(self, *args): """ Let this action act. @@ -363,6 +384,23 @@ cdef class InverseAction(Action): pass raise TypeError(f"no inverse defined for {action!r}") + def __reduce__(self): + """ + Used in pickling. + + TESTS: + + Check that this action is can be pickled (:trac:`29031`):: + + sage: V = QQ^3 + sage: v = V((1, 2, 3)) + sage: cm = get_coercion_model() + sage: a = cm.get_action(V, QQ, operator.mul) + sage: loads(dumps(~a)) is not None + True + """ + return (type(self), (self._action,)) + cpdef _act_(self, g, x): if self.S_precomposition is not None: x = self.S_precomposition(x) @@ -430,6 +468,24 @@ cdef class PrecomposedAction(Action): self.G_precomposition = right_precomposition self.S_precomposition = left_precomposition + def __reduce__(self): + """ + Used in pickling. + + TESTS: + + Check that this action is can be pickled (:trac:`29031`):: + + sage: E = ModularSymbols(11).2 + sage: v = E.manin_symbol_rep() + sage: c,x = v[0] + sage: y = x.modular_symbol_rep() + sage: act = coercion_model.get_action(QQ, parent(y), op=operator.mul) + sage: loads(dumps(act)) is not None + True + """ + return (type(self), (self._action, self.G_precomposition, self.S_precomposition)) + cpdef _act_(self, g, x): if self.G_precomposition is not None: g = self.G_precomposition._call_(g) @@ -564,10 +620,10 @@ cdef class ActionEndomorphism(Morphism): return Morphism.__mul__(left, right) def __invert__(self): - inv_g = ~self._g - if parent(inv_g) is parent(self._g): - return ActionEndomorphism(self._action, inv_g) - else: - return (~self._action)(self._g) + inv_g = ~self._g + if parent(inv_g) is parent(self._g): + return ActionEndomorphism(self._action, inv_g) + else: + return (~self._action)(self._g) diff --git a/src/sage/structure/coerce_actions.pyx b/src/sage/structure/coerce_actions.pyx index c0cba898257..31d49e4ff4f 100644 --- a/src/sage/structure/coerce_actions.pyx +++ b/src/sage/structure/coerce_actions.pyx @@ -365,6 +365,19 @@ cdef class ModuleAction(Action): # In particular we will raise an error if res is None raise CoercionException("Result is None or has wrong parent.") + def __reduce__(self): + """ + Used in pickling. + + TESTS: + + Check that this action is can be pickled (:trac:`29031`):: + + sage: A = ZZ['x'].get_action(QQ, self_on_left=False, op=operator.mul) + sage: loads(dumps(A)) is not None + True + """ + return (type(self), (self.G, self.underlying_set())) def _repr_name_(self): """ @@ -656,6 +669,23 @@ cdef class IntegerAction(Action): Z = Set_PythonType(Z) super().__init__(Z, S, is_left, op) + def __reduce__(self): + """ + Used in pickling. + + TESTS: + + Check that this action is can be pickled (:trac:`29031`):: + + sage: from sage.structure.coerce_actions import IntegerMulAction + sage: act = IntegerMulAction(ZZ, CDF) + sage: loads(dumps(act)) is not None + True + """ + # All base classes must take the signature + # (Z, S, is_left) + return (type(self), (self.G, self.underlying_set(), self._is_left)) + def __invert__(self): """ EXAMPLES:: From b000bb00eb53d05908da87f4ce20e1c1ed9d32e3 Mon Sep 17 00:00:00 2001 From: Dave Witte Morris Date: Mon, 7 Oct 2019 10:40:46 -0600 Subject: [PATCH 002/713] made the correction --- src/sage/rings/fast_arith.pyx | 41 ++++++++++++++++++++--------------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/src/sage/rings/fast_arith.pyx b/src/sage/rings/fast_arith.pyx index 31cc47deb66..0a4850efa8f 100644 --- a/src/sage/rings/fast_arith.pyx +++ b/src/sage/rings/fast_arith.pyx @@ -137,25 +137,28 @@ cpdef prime_range(start, stop=None, algorithm="pari_primes", bint py_ints=False) - Robert Bradshaw (speedup using Pari prime table, py_ints option) """ cdef Integer z - cdef long c_start, c_stop, p, maxpr + cdef long c_start, c_stop, p cdef byteptr pari_prime_ptr - if algorithm == "pari_primes": - if stop is None: - # In this case, "start" is really stop - c_start = 1 - c_stop = start - else: - c_start = start - c_stop = stop - if c_start < 1: - c_start = 1 - if c_stop <= c_start: - return [] - + DEF prime_init_max = 436273290 # hardcoded maximum in definition of prime_init + DEF prime_gap_bound = 1500 # upper bound for gap between primes less than 2^63 + + if stop is None: + # In this case, "start" is really stop + c_start = 1 + c_stop = start + else: + c_start = start + c_stop = stop + if c_start < 1: + c_start = 1 + if c_stop <= c_start: + return [] + + if (algorithm == "pari_primes") and (c_stop + prime_gap_bound <= prime_init_max): if maxprime() < c_stop: - # Adding 1500 should be sufficient to guarantee an + # Adding prime_gap_bound should be sufficient to guarantee an # additional prime, given that c_stop < 2^63. - pari.init_primes(c_stop + 1500) + pari.init_primes(c_stop + prime_gap_bound) assert maxprime() >= c_stop pari_prime_ptr = diffptr @@ -172,7 +175,11 @@ cpdef prime_range(start, stop=None, algorithm="pari_primes", bint py_ints=False) res.append(z) NEXT_PRIME_VIADIFF(p, pari_prime_ptr) - elif algorithm == "pari_isprime": + elif (algorithm == "pari_isprime") or (algorithm == "pari_primes"): + if (algorithm == "pari_primes"): + Print(""" + Warning: algorithm ''pari_primes'' cannot find primes greater than {}. + Using ''pari_isprime'' instead.""".format(prime_init_max - prime_gap_bound)) from sage.arith.all import primes res = list(primes(start, stop)) else: From 4d2e367147f30f2bdbb3a56e4023d89913be9e52 Mon Sep 17 00:00:00 2001 From: Dave Witte Morris Date: Mon, 7 Oct 2019 10:44:41 -0600 Subject: [PATCH 003/713] eliminated tabs --- src/sage/rings/fast_arith.pyx | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/sage/rings/fast_arith.pyx b/src/sage/rings/fast_arith.pyx index 0a4850efa8f..c7eed484c0e 100644 --- a/src/sage/rings/fast_arith.pyx +++ b/src/sage/rings/fast_arith.pyx @@ -142,17 +142,17 @@ cpdef prime_range(start, stop=None, algorithm="pari_primes", bint py_ints=False) DEF prime_init_max = 436273290 # hardcoded maximum in definition of prime_init DEF prime_gap_bound = 1500 # upper bound for gap between primes less than 2^63 - if stop is None: - # In this case, "start" is really stop - c_start = 1 - c_stop = start - else: - c_start = start - c_stop = stop - if c_start < 1: - c_start = 1 - if c_stop <= c_start: - return [] + if stop is None: + # In this case, "start" is really stop + c_start = 1 + c_stop = start + else: + c_start = start + c_stop = stop + if c_start < 1: + c_start = 1 + if c_stop <= c_start: + return [] if (algorithm == "pari_primes") and (c_stop + prime_gap_bound <= prime_init_max): if maxprime() < c_stop: From ae63a5392fab47a64c90670d50c05200426cd35c Mon Sep 17 00:00:00 2001 From: Dave Witte Morris Date: Mon, 7 Oct 2019 10:45:59 -0600 Subject: [PATCH 004/713] print, not Print --- src/sage/rings/fast_arith.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/fast_arith.pyx b/src/sage/rings/fast_arith.pyx index c7eed484c0e..b886d64bbf9 100644 --- a/src/sage/rings/fast_arith.pyx +++ b/src/sage/rings/fast_arith.pyx @@ -177,7 +177,7 @@ cpdef prime_range(start, stop=None, algorithm="pari_primes", bint py_ints=False) elif (algorithm == "pari_isprime") or (algorithm == "pari_primes"): if (algorithm == "pari_primes"): - Print(""" + print(""" Warning: algorithm ''pari_primes'' cannot find primes greater than {}. Using ''pari_isprime'' instead.""".format(prime_init_max - prime_gap_bound)) from sage.arith.all import primes From 61ce5934763a55f96976328236ae9eadc2f3f6ac Mon Sep 17 00:00:00 2001 From: Dave Witte Morris Date: Mon, 7 Oct 2019 10:49:19 -0600 Subject: [PATCH 005/713] warning was indented --- src/sage/rings/fast_arith.pyx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/rings/fast_arith.pyx b/src/sage/rings/fast_arith.pyx index b886d64bbf9..8c1aeccb73b 100644 --- a/src/sage/rings/fast_arith.pyx +++ b/src/sage/rings/fast_arith.pyx @@ -178,8 +178,8 @@ cpdef prime_range(start, stop=None, algorithm="pari_primes", bint py_ints=False) elif (algorithm == "pari_isprime") or (algorithm == "pari_primes"): if (algorithm == "pari_primes"): print(""" - Warning: algorithm ''pari_primes'' cannot find primes greater than {}. - Using ''pari_isprime'' instead.""".format(prime_init_max - prime_gap_bound)) +Warning: algorithm "pari_primes" cannot find primes greater than {}. +Using "pari_isprime" instead.""".format(prime_init_max - prime_gap_bound)) from sage.arith.all import primes res = list(primes(start, stop)) else: From 16fd3361e976d6aa434d7d9940cdad4ec33fb79f Mon Sep 17 00:00:00 2001 From: Dave Witte Morris Date: Mon, 7 Oct 2019 11:12:33 -0600 Subject: [PATCH 006/713] docstring and doctest --- src/sage/rings/fast_arith.pyx | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/sage/rings/fast_arith.pyx b/src/sage/rings/fast_arith.pyx index 8c1aeccb73b..fdeabbfc848 100644 --- a/src/sage/rings/fast_arith.pyx +++ b/src/sage/rings/fast_arith.pyx @@ -52,15 +52,9 @@ cpdef prime_range(start, stop=None, algorithm="pari_primes", bint py_ints=False) If the second argument is omitted, this returns the primes up to the first argument. - This function is closely related to (and can use) the primes - iterator. Use algorithm ``"pari_primes"`` when both ``start`` and - ``stop`` are not too large, since in all cases this function makes - a table of primes up to ``stop``. If both are large, use algorithm - ``"pari_isprime"`` instead. - - Algorithm ``"pari_primes"`` is faster for most input, but crashes - for larger input. - Algorithm ``"pari_isprime"`` is slower but will work for much larger input. + Algorithm "pari_primes" is faster but may use a lot of memory and cannot find + primes greater than 436271790. + Algorithm "pari_isprime" is slower but will work for much larger input. INPUT: @@ -121,6 +115,14 @@ cpdef prime_range(start, stop=None, algorithm="pari_primes", bint py_ints=False) sage: prime_range(4652360, 4652400) [] + + Confirm the fix for trac ticket 28467:: + + sage: prime_range(436271790,436271791) + + Warning: algorithm "pari_primes" cannot find primes greater than 436271790. + Using "pari_isprime" instead. + [] Test for non-existing algorithm:: @@ -180,6 +182,7 @@ cpdef prime_range(start, stop=None, algorithm="pari_primes", bint py_ints=False) print(""" Warning: algorithm "pari_primes" cannot find primes greater than {}. Using "pari_isprime" instead.""".format(prime_init_max - prime_gap_bound)) + from sage.arith.all import primes res = list(primes(start, stop)) else: From 07a511ca48f2a7cba97645195beb139033ec7fd3 Mon Sep 17 00:00:00 2001 From: Dave Witte Morris Date: Mon, 7 Oct 2019 13:39:06 -0600 Subject: [PATCH 007/713] warning was off by 1 --- src/sage/rings/fast_arith.pyx | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/sage/rings/fast_arith.pyx b/src/sage/rings/fast_arith.pyx index fdeabbfc848..4dc554fd264 100644 --- a/src/sage/rings/fast_arith.pyx +++ b/src/sage/rings/fast_arith.pyx @@ -53,8 +53,8 @@ cpdef prime_range(start, stop=None, algorithm="pari_primes", bint py_ints=False) first argument. Algorithm "pari_primes" is faster but may use a lot of memory and cannot find - primes greater than 436271790. - Algorithm "pari_isprime" is slower but will work for much larger input. + primes greater than 436271789. Algorithm "pari_isprime" is slower but will work + for much larger input. INPUT: @@ -120,8 +120,8 @@ cpdef prime_range(start, stop=None, algorithm="pari_primes", bint py_ints=False) sage: prime_range(436271790,436271791) - Warning: algorithm "pari_primes" cannot find primes greater than 436271790. - Using "pari_isprime" instead. + Warning: algorithm "pari_primes" cannot find primes greater than 436271789. + Using "pari_isprime" instead (which may be slower). [] Test for non-existing algorithm:: @@ -141,7 +141,7 @@ cpdef prime_range(start, stop=None, algorithm="pari_primes", bint py_ints=False) cdef Integer z cdef long c_start, c_stop, p cdef byteptr pari_prime_ptr - DEF prime_init_max = 436273290 # hardcoded maximum in definition of prime_init + DEF init_primes_max = 436273290 # hardcoded maximum in definition of pari.init_primes DEF prime_gap_bound = 1500 # upper bound for gap between primes less than 2^63 if stop is None: @@ -156,10 +156,11 @@ cpdef prime_range(start, stop=None, algorithm="pari_primes", bint py_ints=False) if c_stop <= c_start: return [] - if (algorithm == "pari_primes") and (c_stop + prime_gap_bound <= prime_init_max): + if (algorithm == "pari_primes") and (c_stop + prime_gap_bound <= init_primes_max): if maxprime() < c_stop: # Adding prime_gap_bound should be sufficient to guarantee an # additional prime, given that c_stop < 2^63. + # Input to pari.init_primes cannot be greater than init_primes_max. pari.init_primes(c_stop + prime_gap_bound) assert maxprime() >= c_stop @@ -181,7 +182,7 @@ cpdef prime_range(start, stop=None, algorithm="pari_primes", bint py_ints=False) if (algorithm == "pari_primes"): print(""" Warning: algorithm "pari_primes" cannot find primes greater than {}. -Using "pari_isprime" instead.""".format(prime_init_max - prime_gap_bound)) +Using "pari_isprime" instead (which may be slower).""".format(prime_init_max - prime_gap_bound - 1)) from sage.arith.all import primes res = list(primes(start, stop)) From a2479a940e257958062f5d05869b63f236b3b7eb Mon Sep 17 00:00:00 2001 From: Dave Witte Morris Date: Mon, 7 Oct 2019 13:41:32 -0600 Subject: [PATCH 008/713] wrong name for init_primes_max --- src/sage/rings/fast_arith.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/fast_arith.pyx b/src/sage/rings/fast_arith.pyx index 4dc554fd264..afd9077bee5 100644 --- a/src/sage/rings/fast_arith.pyx +++ b/src/sage/rings/fast_arith.pyx @@ -182,7 +182,7 @@ cpdef prime_range(start, stop=None, algorithm="pari_primes", bint py_ints=False) if (algorithm == "pari_primes"): print(""" Warning: algorithm "pari_primes" cannot find primes greater than {}. -Using "pari_isprime" instead (which may be slower).""".format(prime_init_max - prime_gap_bound - 1)) +Using "pari_isprime" instead (which may be slower).""".format(init_primes_max - prime_gap_bound - 1)) from sage.arith.all import primes res = list(primes(start, stop)) From eb13de678b6eaace95680a4978759ce7ee2950a7 Mon Sep 17 00:00:00 2001 From: Dave Witte Morris Date: Mon, 7 Oct 2019 14:09:54 -0600 Subject: [PATCH 009/713] too long for c integers --- src/sage/rings/fast_arith.pyx | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/sage/rings/fast_arith.pyx b/src/sage/rings/fast_arith.pyx index afd9077bee5..c37594c50ed 100644 --- a/src/sage/rings/fast_arith.pyx +++ b/src/sage/rings/fast_arith.pyx @@ -143,20 +143,21 @@ cpdef prime_range(start, stop=None, algorithm="pari_primes", bint py_ints=False) cdef byteptr pari_prime_ptr DEF init_primes_max = 436273290 # hardcoded maximum in definition of pari.init_primes DEF prime_gap_bound = 1500 # upper bound for gap between primes less than 2^63 + + if (algorithm == "pari_primes") and (max(start,stop) + prime_gap_bound <= init_primes_max): - if stop is None: - # In this case, "start" is really stop - c_start = 1 - c_stop = start - else: - c_start = start - c_stop = stop - if c_start < 1: + if stop is None: + # In this case, "start" is really stop c_start = 1 - if c_stop <= c_start: - return [] + c_stop = start + else: + c_start = start + c_stop = stop + if c_start < 1: + c_start = 1 + if c_stop <= c_start: + return [] - if (algorithm == "pari_primes") and (c_stop + prime_gap_bound <= init_primes_max): if maxprime() < c_stop: # Adding prime_gap_bound should be sufficient to guarantee an # additional prime, given that c_stop < 2^63. From 44eff0e8dac16cb60601c5c3d4814e6396cdfd54 Mon Sep 17 00:00:00 2001 From: Dave Witte Morris Date: Mon, 7 Oct 2019 14:13:11 -0600 Subject: [PATCH 010/713] failed doctest --- src/sage/rings/fast_arith.pyx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sage/rings/fast_arith.pyx b/src/sage/rings/fast_arith.pyx index c37594c50ed..b8aec9a1f49 100644 --- a/src/sage/rings/fast_arith.pyx +++ b/src/sage/rings/fast_arith.pyx @@ -119,7 +119,6 @@ cpdef prime_range(start, stop=None, algorithm="pari_primes", bint py_ints=False) Confirm the fix for trac ticket 28467:: sage: prime_range(436271790,436271791) - Warning: algorithm "pari_primes" cannot find primes greater than 436271789. Using "pari_isprime" instead (which may be slower). [] From 0c858a69aebfd5c2f8e97bcd6dcf9a4e4208ea75 Mon Sep 17 00:00:00 2001 From: Dave Witte Morris Date: Sun, 13 Oct 2019 00:21:05 -0600 Subject: [PATCH 011/713] cast to Integer --- src/sage/rings/fast_arith.pyx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/sage/rings/fast_arith.pyx b/src/sage/rings/fast_arith.pyx index b8aec9a1f49..ba593fbad05 100644 --- a/src/sage/rings/fast_arith.pyx +++ b/src/sage/rings/fast_arith.pyx @@ -122,6 +122,8 @@ cpdef prime_range(start, stop=None, algorithm="pari_primes", bint py_ints=False) Warning: algorithm "pari_primes" cannot find primes greater than 436271789. Using "pari_isprime" instead (which may be slower). [] + sage: prime_range("90","100") + [97] Test for non-existing algorithm:: @@ -143,6 +145,10 @@ cpdef prime_range(start, stop=None, algorithm="pari_primes", bint py_ints=False) DEF init_primes_max = 436273290 # hardcoded maximum in definition of pari.init_primes DEF prime_gap_bound = 1500 # upper bound for gap between primes less than 2^63 + start = Integer(start) + if stop is not None: + stop = Integer(stop) + if (algorithm == "pari_primes") and (max(start,stop) + prime_gap_bound <= init_primes_max): if stop is None: From 0331901374a2fb2ea7b51249b6dec2fc3cf31460 Mon Sep 17 00:00:00 2001 From: Dave Witte Morris Date: Mon, 21 Oct 2019 15:37:18 -0700 Subject: [PATCH 012/713] eliminated warning and replaced 1500 with 250 --- src/sage/rings/fast_arith.pyx | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/src/sage/rings/fast_arith.pyx b/src/sage/rings/fast_arith.pyx index ba593fbad05..627ef54d003 100644 --- a/src/sage/rings/fast_arith.pyx +++ b/src/sage/rings/fast_arith.pyx @@ -142,14 +142,16 @@ cpdef prime_range(start, stop=None, algorithm="pari_primes", bint py_ints=False) cdef Integer z cdef long c_start, c_stop, p cdef byteptr pari_prime_ptr - DEF init_primes_max = 436273290 # hardcoded maximum in definition of pari.init_primes - DEF prime_gap_bound = 1500 # upper bound for gap between primes less than 2^63 + # input to pari.init_primes cannot be greater than 436273290 (hardcoded bound) + DEF init_primes_max = 436273290 + DEF small_prime_max = 436273009 # a prime < init_primes_max (preferably the largest) + DEF prime_gap_bound = 250 # upper bound for gap between primes <= small_prime_max start = Integer(start) if stop is not None: stop = Integer(stop) - if (algorithm == "pari_primes") and (max(start,stop) + prime_gap_bound <= init_primes_max): + if (algorithm == "pari_primes") and (max(start,stop) <= small_prime_max): if stop is None: # In this case, "start" is really stop @@ -165,9 +167,8 @@ cpdef prime_range(start, stop=None, algorithm="pari_primes", bint py_ints=False) if maxprime() < c_stop: # Adding prime_gap_bound should be sufficient to guarantee an - # additional prime, given that c_stop < 2^63. - # Input to pari.init_primes cannot be greater than init_primes_max. - pari.init_primes(c_stop + prime_gap_bound) + # additional prime, given that c_stop <= small_prime_max. + pari.init_primes(min(c_stop + prime_gap_bound, init_primes_max)) assert maxprime() >= c_stop pari_prime_ptr = diffptr @@ -185,11 +186,6 @@ cpdef prime_range(start, stop=None, algorithm="pari_primes", bint py_ints=False) NEXT_PRIME_VIADIFF(p, pari_prime_ptr) elif (algorithm == "pari_isprime") or (algorithm == "pari_primes"): - if (algorithm == "pari_primes"): - print(""" -Warning: algorithm "pari_primes" cannot find primes greater than {}. -Using "pari_isprime" instead (which may be slower).""".format(init_primes_max - prime_gap_bound - 1)) - from sage.arith.all import primes res = list(primes(start, stop)) else: From 553ac5962ab4ad2ceae42a99696937103faa4f2b Mon Sep 17 00:00:00 2001 From: Dave Witte Morris Date: Mon, 21 Oct 2019 17:54:39 -0700 Subject: [PATCH 013/713] revised docstring --- src/sage/rings/fast_arith.pyx | 32 ++++++++++++++------------------ 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/src/sage/rings/fast_arith.pyx b/src/sage/rings/fast_arith.pyx index 627ef54d003..1105d829f14 100644 --- a/src/sage/rings/fast_arith.pyx +++ b/src/sage/rings/fast_arith.pyx @@ -52,9 +52,9 @@ cpdef prime_range(start, stop=None, algorithm="pari_primes", bint py_ints=False) If the second argument is omitted, this returns the primes up to the first argument. - Algorithm "pari_primes" is faster but may use a lot of memory and cannot find - primes greater than 436271789. Algorithm "pari_isprime" is slower but will work - for much larger input. + The sage command ``primes`` is an + alternative that uses less memory (but may be slower), because it returns an + iterator, rather than building a list of the primes. INPUT: @@ -64,15 +64,13 @@ cpdef prime_range(start, stop=None, algorithm="pari_primes", bint py_ints=False) - ``algorithm`` -- optional string, one of: - - ``"pari_primes"``: Uses PARI's :pari:`primes` function. - Generates all primes up to stop. - Depends on PARI's :pari:`primepi` function. + - "pari_primes": Uses PARI's primes function to generate all primes from 2 to + stop if stop <= 436273009 (approximately 4.36E8). (Otherwise uses algorithm + "pari_isprime".) This is fast but may crash if there is insufficient memory + (and will not be used when stop > 436273009). - - "pari_isprime": Uses a mod 2 wheel and PARI's :pari:`isprime` - function by calling the primes iterator. - - - ``py_ints`` -- optional boolean (default ``False``), return - Python ints rather than Sage Integers (faster) + - ``py_ints`` -- optional boolean (default ``False``), return Python ints rather + than Sage Integers (faster). Ignored unless algorithm "pari_primes" is being used. EXAMPLES:: @@ -115,13 +113,11 @@ cpdef prime_range(start, stop=None, algorithm="pari_primes", bint py_ints=False) sage: prime_range(4652360, 4652400) [] - + Confirm the fix for trac ticket 28467:: - - sage: prime_range(436271790,436271791) - Warning: algorithm "pari_primes" cannot find primes greater than 436271789. - Using "pari_isprime" instead (which may be slower). - [] + + sage: prime_range(436273009,436273010) + [436273009] sage: prime_range("90","100") [97] @@ -152,7 +148,7 @@ cpdef prime_range(start, stop=None, algorithm="pari_primes", bint py_ints=False) stop = Integer(stop) if (algorithm == "pari_primes") and (max(start,stop) <= small_prime_max): - + if stop is None: # In this case, "start" is really stop c_start = 1 From 6e2b71ce95d425b85d970aeb1882b63759421030 Mon Sep 17 00:00:00 2001 From: Dave Witte Morris Date: Fri, 25 Oct 2019 22:09:44 -0600 Subject: [PATCH 014/713] allow reals --- src/sage/rings/fast_arith.pyx | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/src/sage/rings/fast_arith.pyx b/src/sage/rings/fast_arith.pyx index 1105d829f14..b4c9e5813d0 100644 --- a/src/sage/rings/fast_arith.pyx +++ b/src/sage/rings/fast_arith.pyx @@ -52,9 +52,8 @@ cpdef prime_range(start, stop=None, algorithm="pari_primes", bint py_ints=False) If the second argument is omitted, this returns the primes up to the first argument. - The sage command ``primes`` is an - alternative that uses less memory (but may be slower), because it returns an - iterator, rather than building a list of the primes. + The sage command ``primes`` is an alternative that uses less memory (but may be + slower), because it returns an iterator, rather than building a list of the primes. INPUT: @@ -143,9 +142,24 @@ cpdef prime_range(start, stop=None, algorithm="pari_primes", bint py_ints=False) DEF small_prime_max = 436273009 # a prime < init_primes_max (preferably the largest) DEF prime_gap_bound = 250 # upper bound for gap between primes <= small_prime_max - start = Integer(start) + # make sure that start and stop are integers + try: + start = Integer(start) + except TypeError as integer_error: + try: + start = Integer(round(start)) + except (ValueError, TypeError) as real_error: + raise TypeError(str(integer_error) + + "\nand argument is also not real: " + str(real_error)) if stop is not None: - stop = Integer(stop) + try: + stop = Integer(stop) + except TypeError as integer_error: + try: + stop = Integer(round(stop)) + except (ValueError, TypeError) as real_error: + raise ValueError(str(integer_error) + + "\nand argument is also not real: " + str(real_error)) if (algorithm == "pari_primes") and (max(start,stop) <= small_prime_max): From 2bf45656f187351d1a6d977c2be041a1b4ef5668 Mon Sep 17 00:00:00 2001 From: Dave Witte Morris Date: Sun, 27 Oct 2019 22:01:38 -0600 Subject: [PATCH 015/713] added note to docstring --- src/sage/rings/fast_arith.pyx | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/sage/rings/fast_arith.pyx b/src/sage/rings/fast_arith.pyx index b4c9e5813d0..1a835cef716 100644 --- a/src/sage/rings/fast_arith.pyx +++ b/src/sage/rings/fast_arith.pyx @@ -57,7 +57,7 @@ cpdef prime_range(start, stop=None, algorithm="pari_primes", bint py_ints=False) INPUT: - - ``start`` -- integer, lower bound + - ``start`` -- integer, lower bound (default: 1) - ``stop`` -- integer, upper bound @@ -98,6 +98,13 @@ cpdef prime_range(start, stop=None, algorithm="pari_primes", bint py_ints=False) sage: type(prime_range(8,algorithm="pari_isprime")[0]) + .. NOTE:: + + ``start`` and ``stop`` should be integers, but real numbers will also be accepted + as input. In this case, they will be rounded to nearby integers start\* and + stop\*, so the output will be the primes between start\* and stop\* - 1, which may + not be exactly the same as the primes between ``start`` and ``stop - 1``. + TESTS:: sage: prime_range(-1) From ba5af72674444499a3de0969cb8a5eac1354bdb8 Mon Sep 17 00:00:00 2001 From: Dave Witte Morris Date: Sun, 27 Oct 2019 22:14:27 -0600 Subject: [PATCH 016/713] added real number to doctest --- src/sage/rings/fast_arith.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/fast_arith.pyx b/src/sage/rings/fast_arith.pyx index 1a835cef716..a3b9b6acf66 100644 --- a/src/sage/rings/fast_arith.pyx +++ b/src/sage/rings/fast_arith.pyx @@ -124,7 +124,7 @@ cpdef prime_range(start, stop=None, algorithm="pari_primes", bint py_ints=False) sage: prime_range(436273009,436273010) [436273009] - sage: prime_range("90","100") + sage: prime_range(94.3,"100") [97] Test for non-existing algorithm:: From dd8c29668647d3f73ceb6aba9546d1c15ac23ede Mon Sep 17 00:00:00 2001 From: Dave Witte Morris Date: Fri, 8 Nov 2019 10:21:43 -0700 Subject: [PATCH 017/713] remove input coercion --- src/sage/rings/fast_arith.pyx | 37 +++++++++++------------------------ 1 file changed, 11 insertions(+), 26 deletions(-) diff --git a/src/sage/rings/fast_arith.pyx b/src/sage/rings/fast_arith.pyx index a3b9b6acf66..aad41fdfdfa 100644 --- a/src/sage/rings/fast_arith.pyx +++ b/src/sage/rings/fast_arith.pyx @@ -100,10 +100,11 @@ cpdef prime_range(start, stop=None, algorithm="pari_primes", bint py_ints=False) .. NOTE:: - ``start`` and ``stop`` should be integers, but real numbers will also be accepted - as input. In this case, they will be rounded to nearby integers start\* and - stop\*, so the output will be the primes between start\* and stop\* - 1, which may - not be exactly the same as the primes between ``start`` and ``stop - 1``. + ``start`` and ``stop`` should be integers. Other input may produce unexpected + results. For example, ``prime_range(7, "10", "pari_isprime")`` returns + ``[7]``, but causes an error if the algorithm is changed to "pari_primes", + whereas ``prime_range(7.9, 10, "pari_primes")`` returns ``[7]``, but causes an + error if the algorithm is changed to "pari_isprime". TESTS:: @@ -122,10 +123,13 @@ cpdef prime_range(start, stop=None, algorithm="pari_primes", bint py_ints=False) Confirm the fix for trac ticket 28467:: - sage: prime_range(436273009,436273010) + sage: prime_range(436273009, 436273010) [436273009] - sage: prime_range(94.3,"100") - [97] + + To avoid a doctest error in functions/prime_pi.pyx, prime_range must allow real input:: + + sage: prime_range(9.5, 14.3) + [11, 13] Test for non-existing algorithm:: @@ -149,25 +153,6 @@ cpdef prime_range(start, stop=None, algorithm="pari_primes", bint py_ints=False) DEF small_prime_max = 436273009 # a prime < init_primes_max (preferably the largest) DEF prime_gap_bound = 250 # upper bound for gap between primes <= small_prime_max - # make sure that start and stop are integers - try: - start = Integer(start) - except TypeError as integer_error: - try: - start = Integer(round(start)) - except (ValueError, TypeError) as real_error: - raise TypeError(str(integer_error) - + "\nand argument is also not real: " + str(real_error)) - if stop is not None: - try: - stop = Integer(stop) - except TypeError as integer_error: - try: - stop = Integer(round(stop)) - except (ValueError, TypeError) as real_error: - raise ValueError(str(integer_error) - + "\nand argument is also not real: " + str(real_error)) - if (algorithm == "pari_primes") and (max(start,stop) <= small_prime_max): if stop is None: From 2393ca825aa52f29cf484a279e2a175cfc407d35 Mon Sep 17 00:00:00 2001 From: Dave Witte Morris Date: Fri, 27 Dec 2019 20:49:08 -0700 Subject: [PATCH 018/713] bug fix: max does not compare integer with None --- src/sage/rings/fast_arith.pyx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sage/rings/fast_arith.pyx b/src/sage/rings/fast_arith.pyx index aad41fdfdfa..dc5e5ee8be1 100644 --- a/src/sage/rings/fast_arith.pyx +++ b/src/sage/rings/fast_arith.pyx @@ -153,7 +153,8 @@ cpdef prime_range(start, stop=None, algorithm="pari_primes", bint py_ints=False) DEF small_prime_max = 436273009 # a prime < init_primes_max (preferably the largest) DEF prime_gap_bound = 250 # upper bound for gap between primes <= small_prime_max - if (algorithm == "pari_primes") and (max(start,stop) <= small_prime_max): + # if 'stop' is 'None', need to change it to an integer before comparing with 'start' + if (algorithm == "pari_primes") and (max(start, stop or 0) <= small_prime_max): if stop is None: # In this case, "start" is really stop From 320ac6d110f9aab8048694b0012c85f4743b720f Mon Sep 17 00:00:00 2001 From: Dave Witte Morris Date: Sat, 9 Nov 2019 00:29:19 -0700 Subject: [PATCH 019/713] coerce input --- src/sage/rings/fast_arith.pyx | 38 ++++++++++++++++++++++++++--------- 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/src/sage/rings/fast_arith.pyx b/src/sage/rings/fast_arith.pyx index dc5e5ee8be1..e1fe98c9158 100644 --- a/src/sage/rings/fast_arith.pyx +++ b/src/sage/rings/fast_arith.pyx @@ -100,11 +100,10 @@ cpdef prime_range(start, stop=None, algorithm="pari_primes", bint py_ints=False) .. NOTE:: - ``start`` and ``stop`` should be integers. Other input may produce unexpected - results. For example, ``prime_range(7, "10", "pari_isprime")`` returns - ``[7]``, but causes an error if the algorithm is changed to "pari_primes", - whereas ``prime_range(7.9, 10, "pari_primes")`` returns ``[7]``, but causes an - error if the algorithm is changed to "pari_isprime". + ``start`` and ``stop`` should be integers, but real numbers will also be accepted + as input. In this case, they will be rounded to nearby integers start\* and + stop\*, so the output will be the primes between start\* and stop\* - 1, which may + not be exactly the same as the primes between ``start`` and ``stop - 1``. TESTS:: @@ -126,9 +125,11 @@ cpdef prime_range(start, stop=None, algorithm="pari_primes", bint py_ints=False) sage: prime_range(436273009, 436273010) [436273009] - To avoid a doctest error in functions/prime_pi.pyx, prime_range must allow real input:: + Confirm the fix in trac ticket 28712:: - sage: prime_range(9.5, 14.3) + sage: prime_range(9.5, "14", "pari_primes") + [11, 13] + sage: prime_range(9.5, "14", "pari_isprime") [11, 13] Test for non-existing algorithm:: @@ -153,8 +154,27 @@ cpdef prime_range(start, stop=None, algorithm="pari_primes", bint py_ints=False) DEF small_prime_max = 436273009 # a prime < init_primes_max (preferably the largest) DEF prime_gap_bound = 250 # upper bound for gap between primes <= small_prime_max - # if 'stop' is 'None', need to change it to an integer before comparing with 'start' - if (algorithm == "pari_primes") and (max(start, stop or 0) <= small_prime_max): + # make sure that start and stop are integers + # First try coercing them. If that does not work, then try rounding them. + try: + start = Integer(start) + except TypeError as integer_error: + try: + start = Integer(round(start)) + except (ValueError, TypeError) as real_error: + raise TypeError(str(integer_error) + + "\nand argument is also not real: " + str(real_error)) + if stop is not None: + try: + stop = Integer(stop) + except TypeError as integer_error: + try: + stop = Integer(round(stop)) + except (ValueError, TypeError) as real_error: + raise ValueError(str(integer_error) + + "\nand argument is also not real: " + str(real_error)) + + if (algorithm == "pari_primes") and (max(start,stop) <= small_prime_max): if stop is None: # In this case, "start" is really stop From 4af06865840144d6671cce61ad0168f21d10e3bd Mon Sep 17 00:00:00 2001 From: Dave Witte Morris Date: Sat, 18 Jan 2020 23:52:37 -0700 Subject: [PATCH 020/713] coerce to float before trying to round --- src/sage/rings/fast_arith.pyx | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/sage/rings/fast_arith.pyx b/src/sage/rings/fast_arith.pyx index e1fe98c9158..c9181d4e34b 100644 --- a/src/sage/rings/fast_arith.pyx +++ b/src/sage/rings/fast_arith.pyx @@ -120,17 +120,21 @@ cpdef prime_range(start, stop=None, algorithm="pari_primes", bint py_ints=False) sage: prime_range(4652360, 4652400) [] - Confirm the fix for trac ticket 28467:: + Confirm the fix in :trac:`28467`:: sage: prime_range(436273009, 436273010) [436273009] - Confirm the fix in trac ticket 28712:: + Confirm the fix in :trac:`28712`:: sage: prime_range(9.5, "14", "pari_primes") [11, 13] sage: prime_range(9.5, "14", "pari_isprime") [11, 13] + sage: prime_range(sqrt(10), "10.2", "pari_primes") + [3, 5, 7] + sage: prime_range(sqrt(10), "10.2", "pari_isprime") + [3, 5, 7] Test for non-existing algorithm:: @@ -160,7 +164,7 @@ cpdef prime_range(start, stop=None, algorithm="pari_primes", bint py_ints=False) start = Integer(start) except TypeError as integer_error: try: - start = Integer(round(start)) + start = Integer(round(float(start))) except (ValueError, TypeError) as real_error: raise TypeError(str(integer_error) + "\nand argument is also not real: " + str(real_error)) @@ -169,7 +173,7 @@ cpdef prime_range(start, stop=None, algorithm="pari_primes", bint py_ints=False) stop = Integer(stop) except TypeError as integer_error: try: - stop = Integer(round(stop)) + stop = Integer(round(float(stop))) except (ValueError, TypeError) as real_error: raise ValueError(str(integer_error) + "\nand argument is also not real: " + str(real_error)) From 188ab2a917e2f7973813a424f8b1bee0e53a1864 Mon Sep 17 00:00:00 2001 From: Dave Witte Morris Date: Mon, 6 Apr 2020 19:58:52 -0600 Subject: [PATCH 021/713] accidentally deleted "is_prime" from docstring --- src/sage/rings/fast_arith.pyx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/sage/rings/fast_arith.pyx b/src/sage/rings/fast_arith.pyx index c9181d4e34b..49541cfafdc 100644 --- a/src/sage/rings/fast_arith.pyx +++ b/src/sage/rings/fast_arith.pyx @@ -68,8 +68,12 @@ cpdef prime_range(start, stop=None, algorithm="pari_primes", bint py_ints=False) "pari_isprime".) This is fast but may crash if there is insufficient memory (and will not be used when stop > 436273009). - - ``py_ints`` -- optional boolean (default ``False``), return Python ints rather - than Sage Integers (faster). Ignored unless algorithm "pari_primes" is being used. + - "pari_isprime": Wrapper for ``list(primes(start, stop))``. Each (odd) + integer in the specified range is tested for primality by applying PARI's + isprime function. This is slower but will work for much larger input. + + - ``py_ints`` -- optional boolean (default ``False``), return Python ints rather + than Sage Integers (faster). Ignored unless algorithm "pari_primes" is being used. EXAMPLES:: From aa3f31b471c57b8ab466afb0d4e56513f8259638 Mon Sep 17 00:00:00 2001 From: Dave Witte Morris Date: Sun, 12 Apr 2020 16:30:06 -0600 Subject: [PATCH 022/713] rearrange some doctests --- src/sage/rings/fast_arith.pyx | 34 ++++++++++++---------------------- 1 file changed, 12 insertions(+), 22 deletions(-) diff --git a/src/sage/rings/fast_arith.pyx b/src/sage/rings/fast_arith.pyx index bf881b3ba86..dcba5170260 100644 --- a/src/sage/rings/fast_arith.pyx +++ b/src/sage/rings/fast_arith.pyx @@ -128,22 +128,6 @@ cpdef prime_range(start, stop=None, algorithm=None, bint py_ints=False): sage: prime_range(4652360, 4652400) [] - Confirm the fix in :trac:`28467`:: - - sage: prime_range(436273009, 436273010) - [436273009] - - Confirm the fix in :trac:`28712`:: - - sage: prime_range(9.5, "14", "pari_primes") - [11, 13] - sage: prime_range(9.5, "14", "pari_isprime") - [11, 13] - sage: prime_range(sqrt(10), "10.2", "pari_primes") - [3, 5, 7] - sage: prime_range(sqrt(10), "10.2", "pari_isprime") - [3, 5, 7] - Test for non-existing algorithm:: sage: prime_range(55, algorithm='banana') @@ -160,16 +144,22 @@ cpdef prime_range(start, stop=None, algorithm=None, bint py_ints=False): ... ValueError: algorithm "pari_primes" cannot compute primes larger than 436273008 - To avoid a doctest error in functions/prime_pi.pyx, prime_range must allow real input:: + To avoid a doctest error in functions/prime_pi.pyx, the default algorithm of prime_range + must allow real input:: sage: prime_range(9.5, 14.3) [11, 13] - sage: prime_range(9.5, 14.3, algorithm="pari_primes") + + Confirm the fix in :trac:`28712`:: + + sage: prime_range(9.5, "14", "pari_primes") [11, 13] - sage: prime_range(9.5, 14.3, algorithm="pari_isprime") - Traceback (most recent call last): - ... - TypeError: Attempt to coerce non-integral RealNumber to Integer + sage: prime_range(9.5, "14", "pari_isprime") + [11, 13] + sage: prime_range(sqrt(10), "10.2", "pari_primes") + [3, 5, 7] + sage: prime_range(sqrt(10), "10.2", "pari_isprime") + [3, 5, 7] AUTHORS: From 86aa73b21ced659445d49c600bb304574d29feb5 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Mon, 15 Jun 2020 15:34:08 +0200 Subject: [PATCH 023/713] add march=native in configure.ac --- configure.ac | 105 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 104 insertions(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index bc7e9fe1510..fd54c080a37 100644 --- a/configure.ac +++ b/configure.ac @@ -304,10 +304,42 @@ AX_PROG_PERL_VERSION([5.8.0],[],[ AC_MSG_ERROR([Exiting, since AC_PACKAGE_NAME requires perl-5.8.0 or later]) ]) + + ############################################################################### -# Check C/C++/Fortran compilers +# Check C/C++/Fortran compilers and set CFLAGS ############################################################################### +ORIGINAL_CFLAGS="$CFLAGS" +ORIGINAL_CXXFLAGS="$CXXFLAGS" +ORIGINAL_CXXFLAGS="$FCFLAGS" +ORIGINAL_CXXFLAGS="$F77FLAGS" +AC_SUBST(ORIGINAL_CFLAGS) +AC_SUBST(ORIGINAL_CXXFLAGS) +AC_SUBST(ORIGINAL_FCFLAGS) +AC_SUBST(ORIGINAL_F77FLAGS) + +if test "x${CFLAGS}" = "x"; then + GOT_FLAGS="no" + + # Evaluate SAGE_DEBUG: + if test "x$SAGE_DEBUG" = "xyes" ; then + CFLAGS="-Og -g" + CFLAGS_O3="-Og -g" + else + if test "x$SAGE_DEBUG" = "no" ; then + CFLAGS="-O2" + CFLAGS_O3="-O3" + else + CFLAGS="-O2 -g" + CFLAGS_O3="-O3 -g" + fi + fi +else + GOT_FLAGS="yes" + CFLAGS_O3="${CFLAGS}" +fi + SAGE_CHECK_CONDA_COMPILERS AC_PROG_CC() @@ -328,6 +360,76 @@ AC_SUBST(OBJCXX) AS_IF([test "x$CXX" = x], [AC_MSG_ERROR([a C++ compiler is missing])]) +CFLAGS_NON_NATIVE="${CFLAGS}" +AC_SUBST(CFLAGS_NON_NATIVE) + +# Adding march native in certain cases. +if test "x$SAGE_FAT_BINARY" != "xyes" ; then + if test "x$GOT_FLAGS" = "xno"; then + + # GCC less than 5.1 is not yet ready for "-march=native", + # but it claims it is. + GCC_LT_51="no" + if test "x$GCC" = "xyes"; then + GCC_GTE_51=$(expr `${CC} --version | grep ^gcc | sed 's/^.* //g' | sed -e 's/\.\([0-9][0-9]\)/\1/g' -e 's/\.\([0-9]\)/0\1/g' -e 's/^[0-9]\{3,4\}$/&00/'` \>= 50100) + if test "x$GCC_GTE_51" = "x0"; then + GCC_LT_51="yes" + fi + fi + + if test "x$GCC_LT_51" = "xno"; then + AX_CHECK_COMPILE_FLAG("-march=native", [MARCH=" -march=native"], [MARCH=""], [], []) + CFLAGS="${CFLAGS}${MARCH}" + CFLAGS_O3="${CFLAGS_O3}${MARCH}" + fi + + fi +fi +AC_SUBST(CFLAGS) +AC_SUBST(CFLAGS_O3) +AC_MSG_NOTICE(CFLAGS_NON_NATIVE=$CFLAGS_NON_NATIVE) +AC_MSG_NOTICE(CFLAGS=$CFLAGS) +AC_MSG_NOTICE(CFLAGS_O3=$CFLAGS_O3) + +# Copy to CXXFLAGS, if this is not set. +if test "x${CXXFLAGS}" = "x"; then + CXXFLAGS="$CFLAGS" + CXXFLAGS_O3="$CFLAGS_O3" + CXXFLAGS_NON_NATIVE="$CFLAGS_NON_NATIVE" +else + CXXFLAGS_03="$CXXFLAGS" + CXXFLAGS_NON_NATIVE="$CXXFLAGS" +fi +AC_SUBST(CXXFLAGS) +AC_SUBST(CCXFLAGS_O3) +AC_SUBST(CXXFLAGS_NON_NATIVE) + +# Copy to FCFLAGS, if this is not set. +if test "x${FCFLAGS}" = "x"; then + FCFLAGS="$CFLAGS" + FCFLAGS_O3="$CFLAGS_O3" + FCFLAGS_NON_NATIVE="$CFLAGS_NON_NATIVE" +else + FCFLAGS_03="$FCFLAGS" + FCFLAGS_NON_NATIVE="$FCFLAGS" +fi +AC_SUBST(FCFLAGS) +AC_SUBST(FCFLAGS_O3) +AC_SUBST(FCFLAGS_NON_NATIVE) + +# Copy to F77FLAGS, if this is not set. +if test "x${F77FLAGS}" = "x"; then + F77FLAGS="$CFLAGS" + F77FLAGS_O3="$CFLAGS_O3" + F77FLAGS_NON_NATIVE="$CFLAGS_NON_NATIVE" +else + F77FLAGS_03="$FCFLAGS" + F77FLAGS_NON_NATIVE="$FCFLAGS" +fi +AC_SUBST(F77FLAGS) +AC_SUBST(F77FLAGS_O3) +AC_SUBST(F77FLAGS_NON_NATIVE) + ############################################################################### # Check header files @@ -370,6 +472,7 @@ AS_IF([echo "$ac_pwd" |grep " " >/dev/null], SAGE_CHECK_OSX_SUPPORTED() + ############################################################################### # Collect substitutions for build/make/Makefile.in ############################################################################### From dc591ea97df73c0389479819438fcad4c7445d45 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Mon, 15 Jun 2020 15:43:12 +0200 Subject: [PATCH 024/713] remove reduntant flag setting in packages --- build/pkgs/e_antic/spkg-install.in | 4 --- build/pkgs/ecl/spkg-install.in | 8 ----- build/pkgs/eclib/spkg-install.in | 14 ++------ build/pkgs/ecm/spkg-install.in | 8 ++--- build/pkgs/flint/spkg-install.in | 1 - build/pkgs/fplll/spkg-install.in | 3 -- build/pkgs/gap/spkg-install.in | 3 -- build/pkgs/gc/spkg-check.in | 6 ---- build/pkgs/gc/spkg-install.in | 7 ---- build/pkgs/gf2x/spkg-install.in | 5 +-- build/pkgs/gmp/spkg-install.in | 10 ------ build/pkgs/gsl/spkg-install.in | 8 ----- build/pkgs/iml/spkg-check.in | 6 ---- build/pkgs/iml/spkg-install.in | 7 +--- build/pkgs/latte_int/spkg-check.in | 8 ----- build/pkgs/lcalc/spkg-build.in | 15 -------- build/pkgs/libatomic_ops/spkg-check.in | 6 ---- build/pkgs/libatomic_ops/spkg-install.in | 7 ---- build/pkgs/m4ri/spkg-install.in | 2 -- build/pkgs/m4rie/spkg-install.in | 2 -- build/pkgs/mpfr/spkg-install.in | 6 ++-- build/pkgs/mpir/spkg-install.in | 10 ------ build/pkgs/ncurses/spkg-install.in | 13 ------- build/pkgs/pari/spkg-check.in | 10 +----- build/pkgs/pari/spkg-install.in | 12 ++----- build/pkgs/r/spkg-install.in | 8 ----- build/pkgs/readline/spkg-install.in | 18 ---------- build/pkgs/rubiks/spkg-install.in | 44 +++++------------------- build/pkgs/singular/spkg-install.in | 10 ------ build/pkgs/sqlite/spkg-install.in | 8 ----- build/pkgs/sympow/spkg-install.in | 2 ++ build/pkgs/zn_poly/spkg-check.in | 10 ++---- build/pkgs/zn_poly/spkg-install.in | 10 ++---- 33 files changed, 30 insertions(+), 261 deletions(-) diff --git a/build/pkgs/e_antic/spkg-install.in b/build/pkgs/e_antic/spkg-install.in index c9aa58efa63..0c0f5d01a1f 100644 --- a/build/pkgs/e_antic/spkg-install.in +++ b/build/pkgs/e_antic/spkg-install.in @@ -3,10 +3,6 @@ # e-antic Sage install script # ############################################################################### -if [ "$SAGE_DEBUG" = "yes" ]; then - CFLAGS="-O0 -g $CFLAGS"; export CFLAGS -fi - cd src sdh_configure diff --git a/build/pkgs/ecl/spkg-install.in b/build/pkgs/ecl/spkg-install.in index 81b28acba57..a03c27e5708 100644 --- a/build/pkgs/ecl/spkg-install.in +++ b/build/pkgs/ecl/spkg-install.in @@ -1,13 +1,5 @@ cd src -if [ "x$SAGE_DEBUG" = "xyes" ] ; then - CFLAGS="-g -O0 $CFLAGS" - CXXFLAGS="-g -O0 $CXXFLAGS" -else - CFLAGS="-g -O2 $CFLAGS" - CXXFLAGS="-g -O2 $CXXFLAGS" -fi - if [ "$UNAME" = "CYGWIN" ]; then # Some of ECL's sources rely on GNU-isms that are allowed by default on # most glibcs, but not in newlib; https://trac.sagemath.org/ticket/25057 diff --git a/build/pkgs/eclib/spkg-install.in b/build/pkgs/eclib/spkg-install.in index ba9a4c0b358..9ccc0420e34 100644 --- a/build/pkgs/eclib/spkg-install.in +++ b/build/pkgs/eclib/spkg-install.in @@ -1,15 +1,7 @@ -if [ "$SAGE_DEBUG" = yes ]; then - echo >&2 "Warning: Setting SAGE_DEBUG=yes completely disables optimization." - CFLAGS="$CFLAGS -g -O0" - CXXFLAGS="$CXXFLAGS -g -O0" -else - # Add debug symbols by default, enable optimization, but let the user - # still override these settings: - CFLAGS="-g -O3 $CFLAGS" - CXXFLAGS="-g -O3 $CXXFLAGS" -fi +CFLAGS="$CFLAGS_O3" +CXXFLAGS="$CXXFLAGS_O3" -export CFLAGS CPPFLAGS CXXFLAGS LDFLAGS +export CFLAGS CXXFLAGS echo "Deleting old versions of eclib libraries, which" diff --git a/build/pkgs/ecm/spkg-install.in b/build/pkgs/ecm/spkg-install.in index 610dd249822..e537735a038 100644 --- a/build/pkgs/ecm/spkg-install.in +++ b/build/pkgs/ecm/spkg-install.in @@ -52,6 +52,8 @@ if [ -n "$system_gmp_h" ]; then esac fi +CFLAGS="$CFLAGS_O3" + # libtool should add the proper flags, but doesn't use "-fPIC" # for the *static* library (which the Sage library links to unless @@ -62,6 +64,7 @@ if ! (echo $ECM_CONFIGURE | egrep -- "--enable-shared|--with-pic" >/dev/null) || then echo "Adding '-fPIC' to CFLAGS since we don't (also) build a shared library." CFLAGS="$CFLAGS -fPIC" # alternatively add '--with-pic' to 'configure' options + ORIGINAL_CFLAGS="$ORIGINAL_CFLAGS -fPIC" else # PIC usually slows down the execution, so don't use it for the *static* # library (unless '--with-pic' was given). libtool does the right thing @@ -79,7 +82,6 @@ fi if [ "$SAGE_DEBUG" = yes ]; then # Add debug symbols and disable optimization: echo >&2 "Warning: Setting SAGE_DEBUG=yes completely disables optimization." - CFLAGS="-g -O0 $CFLAGS" echo "You may in addition (or instead) pass '--enable-assert' and/or" echo "'--enable-memory-debug' to GMP-ECM's 'configure' by setting (and" echo "of course exporting) ECM_CONFIGURE accordingly." @@ -96,10 +98,8 @@ else echo >&2 "See http://trac.sagemath.org/sage_trac/ticket/5847#comment:35" \ "ff. for details." echo >&2 - CFLAGS="-O3 $CFLAGS" + CFLAGS="-O3 $ORIGINAL_CFLAGS" ;; - *) - CFLAGS="-g -O3 $CFLAGS" esac fi diff --git a/build/pkgs/flint/spkg-install.in b/build/pkgs/flint/spkg-install.in index c648fe3ca01..eacd7b6e491 100644 --- a/build/pkgs/flint/spkg-install.in +++ b/build/pkgs/flint/spkg-install.in @@ -5,7 +5,6 @@ ############################################################################### if [ "$SAGE_DEBUG" = "yes" ]; then echo "Building a debug version of FLINT." - CFLAGS="-O0 -g $CFLAGS"; export CFLAGS FLINT_TUNE=" $FLINT_TUNE"; export FLINT_TUNE FLINT_CONFIGURE="--enable-assert $FLINT_CONFIGURE" fi diff --git a/build/pkgs/fplll/spkg-install.in b/build/pkgs/fplll/spkg-install.in index 4e1fab1dd87..619a3e8d53c 100644 --- a/build/pkgs/fplll/spkg-install.in +++ b/build/pkgs/fplll/spkg-install.in @@ -17,10 +17,7 @@ if [ "$UNAME" = "CYGWIN" ]; then fi if [ "x$SAGE_DEBUG" = "xyes" ]; then - CXXFLAGS="$CXXFLAGS -O0" CONFIGUREFLAGS="$CONFIGUREFLAGS --enable-debug" -else - CXXFLAGS="$CXXFLAGS -O3" fi export CXXFLAGS="$CXXFLAGS" diff --git a/build/pkgs/gap/spkg-install.in b/build/pkgs/gap/spkg-install.in index 45f6fed445e..56e95338df1 100644 --- a/build/pkgs/gap/spkg-install.in +++ b/build/pkgs/gap/spkg-install.in @@ -12,9 +12,6 @@ DESTDIR_GAP_ROOT="$SAGE_DESTDIR$GAP_ROOT" # Note that -g3 allows you to use preprocessor macros in gdb which are widely used if [ "$SAGE_DEBUG" = yes ] ; then export CFLAGS="-O0 -g3 -DDEBUG_MASTERPOINTERS -DDEBUG_GLOBAL_BAGS -DDEBUG_FUNCTIONS_BAGS $CFLAGS" -else - # Default flags - export CFLAGS="-O2 -g $CFLAGS" fi sdh_configure $SAGE_CONFIGURE_GMP diff --git a/build/pkgs/gc/spkg-check.in b/build/pkgs/gc/spkg-check.in index 6d620bd7417..917c8eb7209 100644 --- a/build/pkgs/gc/spkg-check.in +++ b/build/pkgs/gc/spkg-check.in @@ -1,9 +1,3 @@ cd src -if [ "$SAGE_DEBUG" = "yes" ]; then - export CFLAGS="-O0 -g $CFLAGS" -else - export CFLAGS="-O2 -g $CFLAGS" -fi - $MAKE check diff --git a/build/pkgs/gc/spkg-install.in b/build/pkgs/gc/spkg-install.in index 78d60cc8309..e5d254c86fe 100644 --- a/build/pkgs/gc/spkg-install.in +++ b/build/pkgs/gc/spkg-install.in @@ -1,12 +1,5 @@ cd src -if [ "$SAGE_DEBUG" = "yes" ]; then - echo "Building a debug version of BoehmGC." - export CFLAGS="-O0 -g $CFLAGS" -else - export CFLAGS="-O2 -g $CFLAGS" -fi - GC_CONFIGURE="--enable-large-config" if [ "$UNAME" = "CYGWIN" ]; then diff --git a/build/pkgs/gf2x/spkg-install.in b/build/pkgs/gf2x/spkg-install.in index 7ae410af1a8..5f023fc1b8b 100644 --- a/build/pkgs/gf2x/spkg-install.in +++ b/build/pkgs/gf2x/spkg-install.in @@ -13,12 +13,9 @@ touch aclocal.m4 configure Makefile.in gf2x/gf2x-config.h.in if [ "$SAGE_DEBUG" = "yes" ]; then echo "Building a debug version of gf2x." - export CFLAGS="-O0 -g $CFLAGS" elif $CC --version 2>/dev/null |grep 'gcc.* 5[.][12]' >/dev/null; then echo "Using compiler flags to work around problems with GCC 5.1/5.2 (Trac #18580,#18978)" - export CFLAGS="-O2 -fno-forward-propagate -g $CFLAGS" -else - export CFLAGS="-O2 -g $CFLAGS" + export CFLAGS="-fno-forward-propagate $CFLAGS" fi if [ "$SAGE_FAT_BINARY" = "yes" ]; then diff --git a/build/pkgs/gmp/spkg-install.in b/build/pkgs/gmp/spkg-install.in index 61096b76cab..fa53576f8c6 100644 --- a/build/pkgs/gmp/spkg-install.in +++ b/build/pkgs/gmp/spkg-install.in @@ -27,16 +27,6 @@ if [ -z "$CFLAG64" ]; then fi -if [ "$SAGE_DEBUG" = yes ]; then - # Disable optimization, add debug symbols: - required_cflags="$required_cflags -g -O0" - echo >&2 "Warning: Building GMP with SAGE_DEBUG=yes disables optimization." -else - # Add debug symbols by default - required_cflags="$required_cflags -g" -fi - - case "$UNAME" in SunOS) true;; # Auto-detect ABI diff --git a/build/pkgs/gsl/spkg-install.in b/build/pkgs/gsl/spkg-install.in index a6cc2072dda..179b78963a1 100644 --- a/build/pkgs/gsl/spkg-install.in +++ b/build/pkgs/gsl/spkg-install.in @@ -1,11 +1,3 @@ -if [ "$SAGE_DEBUG" = "yes" ] ; then - CFLAGS="-g -O0 $CFLAGS" # No optimisation, aids debugging. -else - CFLAGS="-g -O2 $CFLAGS" # Normal optimisation. -fi - -export CFLAGS - cd src sdh_configure LIBS="`pkg-config --libs-only-l cblas` -lm" diff --git a/build/pkgs/iml/spkg-check.in b/build/pkgs/iml/spkg-check.in index 6d620bd7417..917c8eb7209 100644 --- a/build/pkgs/iml/spkg-check.in +++ b/build/pkgs/iml/spkg-check.in @@ -1,9 +1,3 @@ cd src -if [ "$SAGE_DEBUG" = "yes" ]; then - export CFLAGS="-O0 -g $CFLAGS" -else - export CFLAGS="-O2 -g $CFLAGS" -fi - $MAKE check diff --git a/build/pkgs/iml/spkg-install.in b/build/pkgs/iml/spkg-install.in index 8764fd17cf8..1c082006267 100644 --- a/build/pkgs/iml/spkg-install.in +++ b/build/pkgs/iml/spkg-install.in @@ -1,11 +1,6 @@ cd src -if [ "$SAGE_DEBUG" = "yes" ]; then - echo "Building a debug version of IML." - export CFLAGS="-O0 -g $CFLAGS" -else - export CFLAGS="-O3 -g $CFLAGS" -fi +export CFLAGS=CFLAGS_O3 # When using GMP from a standard system location it shouldn't really # matter what we put here, but iml's configure script requires we diff --git a/build/pkgs/latte_int/spkg-check.in b/build/pkgs/latte_int/spkg-check.in index 7bdcf72a10c..27cd9419538 100644 --- a/build/pkgs/latte_int/spkg-check.in +++ b/build/pkgs/latte_int/spkg-check.in @@ -1,10 +1,2 @@ -if [ "x$SAGE_DEBUG" = xyes ] ; then - CFLAGS="$CFLAGS -g -O0" # No optimisation, aids debugging. -else - CFLAGS="$CFLAGS -g -O2" # Normal optimisation. -fi - -export CFLAGS - cd src $MAKE check diff --git a/build/pkgs/lcalc/spkg-build.in b/build/pkgs/lcalc/spkg-build.in index b477bd0d153..a5c67c2cafa 100644 --- a/build/pkgs/lcalc/spkg-build.in +++ b/build/pkgs/lcalc/spkg-build.in @@ -1,23 +1,8 @@ -# If SAGE_DEBUG is set to 'yes', add debugging information. Since both -# the Sun and GNU compilers accept -g to give debugging information, -# there is no need to do anything specific to one compiler or the other. -if [ "x$SAGE_DEBUG" = xyes ]; then - echo "Code will be built with debugging information present. Unset 'SAGE_DEBUG'" - echo "or set it to 'no' if you don't want that." - - CFLAGS="$CFLAGS -O0 -g" - CXXFLAGS="$CXXFLAGS -O0 -g" -else - echo "No debugging information will be used during the build of this package." - echo "Set 'SAGE_DEBUG' to 'yes' if you want debugging information present (-g added)." -fi - # Using pari in a C++17 file with "using namespace std doesn't # work due to a conflict between std::rank and pari's rank CXXFLAGS=$(echo "${CXXFLAGS}" | sed "s/-std=c++17//g") # Export everything. Probably not necessary in most cases. -export CFLAGS export CXXFLAGS export DEFINES="" diff --git a/build/pkgs/libatomic_ops/spkg-check.in b/build/pkgs/libatomic_ops/spkg-check.in index 1e19e3eb37f..75794c7ee97 100644 --- a/build/pkgs/libatomic_ops/spkg-check.in +++ b/build/pkgs/libatomic_ops/spkg-check.in @@ -1,11 +1,5 @@ cd src -if [ "$SAGE_DEBUG" = "yes" ]; then - export CFLAGS="-O0 -g $CFLAGS" -else - export CFLAGS="-O2 -g $CFLAGS" -fi - if [ "$SAGE64" = "yes" ]; then export CFLAGS="-m64 $CFLAGS" fi diff --git a/build/pkgs/libatomic_ops/spkg-install.in b/build/pkgs/libatomic_ops/spkg-install.in index 4347ff3cd5a..8bcf5eec8d7 100644 --- a/build/pkgs/libatomic_ops/spkg-install.in +++ b/build/pkgs/libatomic_ops/spkg-install.in @@ -1,12 +1,5 @@ cd src -if [ "$SAGE_DEBUG" = "yes" ]; then - echo "Building a debug version of libatomic_ops." - export CFLAGS="-O0 -g $CFLAGS" -else - export CFLAGS="-O2 -g $CFLAGS" -fi - if [ "$SAGE64" = "yes" ]; then echo "Building a 64-bit version of libatomic_ops." export CFLAGS="-m64 $CFLAGS" diff --git a/build/pkgs/m4ri/spkg-install.in b/build/pkgs/m4ri/spkg-install.in index 0ed379b4511..c3ae2270406 100644 --- a/build/pkgs/m4ri/spkg-install.in +++ b/build/pkgs/m4ri/spkg-install.in @@ -11,10 +11,8 @@ elif [ "$COMPILER" = "HP_on_HP-UX" ] ; then fi if [ "x$SAGE_DEBUG" = "xyes" ]; then - CFLAGS="$CFLAGS -O0" ENABLE_DEBUG="--enable-debug" else - CFLAGS="$CFLAGS -O2" ENABLE_DEBUG="" fi diff --git a/build/pkgs/m4rie/spkg-install.in b/build/pkgs/m4rie/spkg-install.in index 9a0449aaf46..925706f0581 100644 --- a/build/pkgs/m4rie/spkg-install.in +++ b/build/pkgs/m4rie/spkg-install.in @@ -18,10 +18,8 @@ fi CPPFLAGS="$INCLUDES" if [ "x$SAGE_DEBUG" = "xyes" ]; then - CFLAGS="-O0 $CFLAGS" ENABLE_DEBUG="--enable-debug" else - CFLAGS="-O2 $CFLAGS" ENABLE_DEBUG="" fi diff --git a/build/pkgs/mpfr/spkg-install.in b/build/pkgs/mpfr/spkg-install.in index ea918577af4..36663a48392 100644 --- a/build/pkgs/mpfr/spkg-install.in +++ b/build/pkgs/mpfr/spkg-install.in @@ -30,18 +30,18 @@ mpfr_configure() # Set up environment variables: ########################################################################### - user_cflags=$CFLAGS # Save them. 'sage-env' sets CC, but not CFLAGS. + user_cflags=$ORIGINAL_CFLAGS # Save them. 'sage-env' sets CC, but not CFLAGS. required_cflags="" # Additional mandatory settings required by Sage, accumulated below. default_cflags="" # Spkg defaults that can and might get overridden. if [ "$SAGE_DEBUG" = yes ]; then # Disable optimization, add debug symbols: - required_cflags="$required_cflags -g -O0" + required_cflags=$CFLAGS_NON_NATIVE echo >&2 "Warning: Building MPFR with SAGE_DEBUG=yes disables optimization." else # Add debug symbols by default, enable optimization, but do not (yet) # add processor-specific flags (these are eventually added later): - default_cflags="$default_cflags -g -O3" + default_cflags=$CFLAGS_O3 fi # Enabling thread-safe (which meanwhile is or at least may be the default) diff --git a/build/pkgs/mpir/spkg-install.in b/build/pkgs/mpir/spkg-install.in index 86b84036358..01070d1dc3e 100644 --- a/build/pkgs/mpir/spkg-install.in +++ b/build/pkgs/mpir/spkg-install.in @@ -49,16 +49,6 @@ if [ -z "$CFLAG64" ]; then fi -if [ "$SAGE_DEBUG" = yes ]; then - # Disable optimization, add debug symbols: - required_cflags="$required_cflags -g -O0" - echo >&2 "Warning: Building MPIR with SAGE_DEBUG=yes disables optimization." -else - # Add debug symbols by default - required_cflags="$required_cflags -g" -fi - - case "$UNAME" in SunOS) true;; # Auto-detect ABI diff --git a/build/pkgs/ncurses/spkg-install.in b/build/pkgs/ncurses/spkg-install.in index 4d4cfc18a6f..1f47e2f5493 100644 --- a/build/pkgs/ncurses/spkg-install.in +++ b/build/pkgs/ncurses/spkg-install.in @@ -1,23 +1,10 @@ DEBUG_CONFIGURE_FLAG='' if [ "$SAGE_DEBUG" = yes ]; then - CFLAGS="-O0 -g $CFLAGS" DEBUG_CONFIGURE_FLAG='--with-debug' else DEBUG_CONFIGURE_FLAG='--without-debug' fi - -echo "The following environment variables will be exported:" -echo "Using CC=$CC" -echo "Using CFLAGS=$CFLAGS" -echo "Using CPPFLAGS=$CPPFLAGS" -echo "Using LDFLAGS=$LDFLAGS" -echo - -export CFLAGS -export CPPFLAGS -export LDFLAGS - cd src # Ncurses cannot build narrow and wide (unicode, --enable-widec) diff --git a/build/pkgs/pari/spkg-check.in b/build/pkgs/pari/spkg-check.in index ae3aecb4a0a..9aaa5ff5fa0 100644 --- a/build/pkgs/pari/spkg-check.in +++ b/build/pkgs/pari/spkg-check.in @@ -2,15 +2,7 @@ ## PARI ########################################### -if [ "x$SAGE_DEBUG" = xyes ] ; then - CFLAGS="$CFLAGS -O0 -g" # Disable optimisation, add debug symbols. Good - # for debugging or working around compiler bugs. -else - CFLAGS="-O3 -g $CFLAGS" # Default optimisation, with debug symbols. - # Prepend to not override user's setting. -fi - -export CFLAGS +export CFLAGS=$CFLAGS_O3 cd src diff --git a/build/pkgs/pari/spkg-install.in b/build/pkgs/pari/spkg-install.in index d8896b62a18..3134aadb353 100644 --- a/build/pkgs/pari/spkg-install.in +++ b/build/pkgs/pari/spkg-install.in @@ -112,10 +112,9 @@ if [ $MACOSX_VERSION -ge 14 ]; then export MACOSX_DEPLOYMENT_TARGET=10.9 fi -# Set CFLAGS +export CFLAGS=$CFLAGS_O3 + if [ "$SAGE_DEBUG" = yes ]; then - # Disable optimisation, add debug symbols. - CFLAGS="-O0 -g $CFLAGS" # Compile kernel files with -O1 instead of -funroll-loops; -O0 gives # a segmentation fault on some OS X systems when doing @@ -123,15 +122,8 @@ if [ "$SAGE_DEBUG" = yes ]; then # See #13921, also reported upstream: # - http://pari.math.u-bordeaux.fr/archives/pari-dev-1301/msg00000.html PARI_MAKEFLAGS="KERNELCFLAGS=-O1 $PARI_MAKEFLAGS" -else - # Use PARI's default CFLAGS (with -g added). - # PARI's Configure adds -O3 to the CFLAGS, so we don't need to add - # it explicitly. - CFLAGS="-g $CFLAGS" fi -export CFLAGS - # Build PARI/GP # Configure PARI/GP, forcing bash instead of /bin/sh. It is not diff --git a/build/pkgs/r/spkg-install.in b/build/pkgs/r/spkg-install.in index d77db37688d..b3dea4c5539 100644 --- a/build/pkgs/r/spkg-install.in +++ b/build/pkgs/r/spkg-install.in @@ -7,14 +7,6 @@ unset GREP_OPTIONS # and cannot be used to pass this path. CPPFLAGS="$CPPFLAGS" -# Optimization flags -if [ "$SAGE_DEBUG" = yes ]; then - CFLAGS="-g -O0 $CFLAGS" - FCFLAGS="-g -O0 $FCFLAGS" -else - CFLAGS="-g -O2 $CFLAGS" - FCFLAGS="-g -O2 $FCFLAGS" -fi # #29170: Compilation errors caused by a silently failing configure check # "for type of 'hidden' Fortran character lengths" # on ubuntu-bionic-minimal, ubuntu-eoan/focal-minimal, debian-buster/bullseye/sid-minimal, diff --git a/build/pkgs/readline/spkg-install.in b/build/pkgs/readline/spkg-install.in index 6bdbc9a5cd0..126c4372169 100644 --- a/build/pkgs/readline/spkg-install.in +++ b/build/pkgs/readline/spkg-install.in @@ -1,21 +1,3 @@ -if [[ $SAGE_DEBUG = yes ]]; then - CFLAGS="$CFLAGS -g -O0" -else - CFLAGS="-g -O2 $CFLAGS" -fi - -echo "The following environment variables will be exported:" -echo "Using CC=$CC" -echo "Using CFLAGS=$CFLAGS" -echo "Using CPPFLAGS=$CPPFLAGS" -echo "Using LDFLAGS=$LDFLAGS" -echo "Configure scripts and/or makefiles might override these later." - -export CC -export CFLAGS -export CPPFLAGS -export LDFLAGS - cd src/ sdh_configure --with-curses --enable-shared --disable-static diff --git a/build/pkgs/rubiks/spkg-install.in b/build/pkgs/rubiks/spkg-install.in index 3ff0398d063..e03c76e432a 100644 --- a/build/pkgs/rubiks/spkg-install.in +++ b/build/pkgs/rubiks/spkg-install.in @@ -2,13 +2,11 @@ ## rubiks ########################################### -# Add a sensible default optimisation flag. Change if necessary. -OPTIMIZATION_FLAGS="-O2" # Work around a bug in gcc 4.6.0: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=48702 if [ "`testcc.sh $CC`" = GCC ] ; then if $CC -dumpversion 2>/dev/null |grep >/dev/null '^4\.6\.[01]$' ; then echo "Warning: Working around bug in gcc 4.6.0" - OPTIMIZATION_FLAGS="$OPTIMIZATION_FLAGS -fno-ivopts" + EXTRA_FLAG="-fno-ivopts" fi fi @@ -16,28 +14,14 @@ fi # But it is better to do them all each time, rather than ommit # a flag by mistake. -CFLAGS="$CFLAGS $OPTIMIZATION_FLAGS " -CXXFLAGS="$CXXFLAGS $OPTIMIZATION_FLAGS " -FCFLAGS="$FCFLAGS $OPTIMIZATION_FLAGS " -F77FLAGS="$F77FLAGS $OPTIMIZATION_FLAGS " - -# If SAGE_DEBUG is set either unset (the default), or set to 'yes' -# Add debugging information. -# Since both the Sun and GNU compilers accept -g to give debugging information -# there is no need to do anything specific to one compiler or the other. - -if [ "x$SAGE_DEBUG" = "x" ] || [ "x$SAGE_DEBUG" = "xyes" ] ; then - echo "Code will be built with debugging information present. Set 'SAGE_DEBUG' to 'no' if you don't want that." - # Actually anything other than 'yes' will cause - # no debugging information to be added. - CFLAGS="$CFLAGS -g " - CXXFLAGS="$CXXFLAGS -g " - FCFLAGS="$FCFLAGS -g " - F77FLAGS="$F77FLAGS -g " -else - echo "No debugging information will be used during the build of this package" - echo "Unset SAGE_DEBUG if you want debugging information present (-g added)" -fi +export CFLAGS="$CFLAGS $EXTRA_FLAG" +export CXXFLAGS="$CXXFLAGS $EXTRA_FLAG" +export FCFLAGS="$FCFLAGS $EXTRA_FLAG" +export F77FLAGS="$F77FLAGS $EXTRA_FLAG" +export CPPFLAGS +export LDFLAGS +export ABI +export CPPFLAGS # These are all used by GNU to specify compilers. echo "Using CC=$CC" @@ -57,16 +41,6 @@ echo "Using ABI=$ABI" echo "configure scripts and/or makefiles might override these later" echo " " -# export everything. Probably not necessary in most cases. -export CFLAGS -export CXXFLAGS -export FCFLAGS -export F77FLAGS -export CPPFLAGS -export LDFLAGS -export ABI -export CPPFLAGS - # End of pretty general spkg-install file. # Now do the specific things needed for this package (rubiks) diff --git a/build/pkgs/singular/spkg-install.in b/build/pkgs/singular/spkg-install.in index 75306d57805..a9be7bfaa54 100644 --- a/build/pkgs/singular/spkg-install.in +++ b/build/pkgs/singular/spkg-install.in @@ -9,18 +9,8 @@ if [ "x$SAGE_DEBUG" = "xyes" ]; then # This used to disable omalloc but that is not really supported # by upstream SINGULAR_CONFIGURE="$SINGULAR_CONFIGURE --enable-debug --disable-optimizationflags" - - CFLAGS="$CFLAGS -O0 -g" - CXXFLAGS="$CXXFLAGS -O0 -g" -else - # Singular 4.x (mostly) does not longer set defaults for CFLAGS and CXXFLAGS - CFLAGS="-O2 -g $CFLAGS" - CXXFLAGS="-O2 -g $CXXFLAGS" fi -export CFLAGS CXXFLAGS - - remove_old_version() { # the following is a little verbose but it ensures we leave no trace of 3.x diff --git a/build/pkgs/sqlite/spkg-install.in b/build/pkgs/sqlite/spkg-install.in index 3cca459dae3..f20e4ecdb23 100644 --- a/build/pkgs/sqlite/spkg-install.in +++ b/build/pkgs/sqlite/spkg-install.in @@ -6,15 +6,7 @@ cd src cp "$SAGE_ROOT"/config/config.* . -# Build with -O0 if debugging requested -if [ "$SAGE_DEBUG" = "yes" ]; then - CFLAGS="-g -O0 $CFLAGS" -else - CFLAGS="-g -O2 $CFLAGS" -fi - export CPPFLAGS="$CPPFLAGS -I$SAGE_LOCAL/include" -export CFLAGS # Old OS X systems need -DSQLITE_WITHOUT_ZONEMALLOC if uname -sr |grep 'Darwin [0-8][.]' >/dev/null; then diff --git a/build/pkgs/sympow/spkg-install.in b/build/pkgs/sympow/spkg-install.in index 8fe518f2d04..af47bbf6ac3 100644 --- a/build/pkgs/sympow/spkg-install.in +++ b/build/pkgs/sympow/spkg-install.in @@ -38,6 +38,8 @@ try_add_CFLAG() return 2 } +CFLAGS=$ORIGINAL_CFLAGS + # These flags never hurt, so add them if possible for FLAG in '-fno-fast-math' '-mfpmath=sse' '-Dx86'; do try_add_CFLAG $FLAG diff --git a/build/pkgs/zn_poly/spkg-check.in b/build/pkgs/zn_poly/spkg-check.in index 299ac7a6b8a..c7831aed276 100644 --- a/build/pkgs/zn_poly/spkg-check.in +++ b/build/pkgs/zn_poly/spkg-check.in @@ -8,14 +8,8 @@ # Set up environment variables: ############################################################################### -if [ "$SAGE_DEBUG" = yes ]; then - echo >&2 "Warning: Setting SAGE_DEBUG to 'yes' completely disables optimization." - CFLAGS="$CFLAGS -O0 -g -fPIC" - CXXFLAGS="$CXXFLAGS -O0 -g -fPIC" -else - CFLAGS="-O3 -g $CFLAGS -fPIC" - CXXFLAGS="-O3 -g $CXXFLAGS -fPIC" -fi +CFLAGS="$CFLAGS_O3 -fPIC" +CXXFLAGS="$CXXFLAGS_O3 -fPIC" # Work around a bug in GCC 4.7.0 which breaks the build on Itanium CPUs with # '-O3', '-O2' and '-O1' (cf. #12765, #12751, and the bug URL below.) diff --git a/build/pkgs/zn_poly/spkg-install.in b/build/pkgs/zn_poly/spkg-install.in index 169aef83d17..039986543ba 100644 --- a/build/pkgs/zn_poly/spkg-install.in +++ b/build/pkgs/zn_poly/spkg-install.in @@ -2,14 +2,8 @@ # Set up environment variables: ############################################################################### -if [ "$SAGE_DEBUG" = yes ]; then - echo >&2 "Warning: Setting SAGE_DEBUG to 'yes' completely disables optimization." - CFLAGS="-O0 -g $CFLAGS -fPIC" - CXXFLAGS="-O0 -g $CXXFLAGS -fPIC" -else - CFLAGS="-O3 -g $CFLAGS -fPIC" - CXXFLAGS="-O3 -g $CXXFLAGS -fPIC" -fi +CFLAGS="$CFLAGS_O3 -fPIC" +CXXFLAGS="$CXXFLAGS_O3 -fPIC" export CFLAGS CPPFLAGS CXXFLAGS LDFLAGS # Partially redundant, but safe. From 491621cd16389a31ac2a62bf37da3446f19f1d6e Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Mon, 15 Jun 2020 17:06:16 +0200 Subject: [PATCH 025/713] enable-fat-binary --- configure.ac | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/configure.ac b/configure.ac index fd54c080a37..6d501226920 100644 --- a/configure.ac +++ b/configure.ac @@ -84,6 +84,12 @@ if test "$enable_build_as_root" != yes; then AX_CHECK_ROOT([AC_MSG_ERROR([You cannot build Sage as root, switch to an unprivileged user. (If building in a container, use --enable-build-as-root.)])], []) fi +AC_ARG_ENABLE([fat-binary], + [AS_HELP_STRING([--enable-fat-binary], + [build binaries that will run on the widest range of target CPUs (but not relocatable)])], + [AC_SUBST(SAGE_FAT_BINARY, "yes")], + []) + # Check whether we are on a supported platform AC_CANONICAL_BUILD() AC_CANONICAL_HOST() From 38088b057c1fe4f19c86fac9133aad109612c1e7 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Tue, 16 Jun 2020 07:25:09 +0200 Subject: [PATCH 026/713] typo for iml --- build/pkgs/iml/spkg-install.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/pkgs/iml/spkg-install.in b/build/pkgs/iml/spkg-install.in index 1c082006267..bb00b407a29 100644 --- a/build/pkgs/iml/spkg-install.in +++ b/build/pkgs/iml/spkg-install.in @@ -1,6 +1,6 @@ cd src -export CFLAGS=CFLAGS_O3 +export CFLAGS=$CFLAGS_O3 # When using GMP from a standard system location it shouldn't really # matter what we put here, but iml's configure script requires we From 2d5bba5e353128e80e3c1e3615286bf3e24eba72 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Thu, 18 Jun 2020 00:38:11 +0200 Subject: [PATCH 027/713] copy/paste typo --- configure.ac | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index 6d501226920..367650a3ced 100644 --- a/configure.ac +++ b/configure.ac @@ -318,8 +318,8 @@ AX_PROG_PERL_VERSION([5.8.0],[],[ ORIGINAL_CFLAGS="$CFLAGS" ORIGINAL_CXXFLAGS="$CXXFLAGS" -ORIGINAL_CXXFLAGS="$FCFLAGS" -ORIGINAL_CXXFLAGS="$F77FLAGS" +ORIGINAL_FCFLAGS="$FCFLAGS" +ORIGINAL_F77FLAGS="$F77FLAGS" AC_SUBST(ORIGINAL_CFLAGS) AC_SUBST(ORIGINAL_CXXFLAGS) AC_SUBST(ORIGINAL_FCFLAGS) From 8aae4979c90bf256b0408e395a71315675095943 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Thu, 18 Jun 2020 00:40:44 +0200 Subject: [PATCH 028/713] export configured compilation flags for build --- build/bin/sage-build-env-config.in | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/build/bin/sage-build-env-config.in b/build/bin/sage-build-env-config.in index 01ba892dfd6..6eb6267f61b 100644 --- a/build/bin/sage-build-env-config.in +++ b/build/bin/sage-build-env-config.in @@ -22,6 +22,27 @@ # The configured CXX without special flags added that enable C++11 support export SAGE_CXX_WITHOUT_STD="@SAGE_CXX_WITHOUT_STD@" +# The configured compilation flags +export CFLAGS="@CFLAGS@" +export CXXFLAGS="@CXXFLAGS@" +export FCFLAGS="@FCFLAGS@" +export F77FLAGS="@F77FLAGS@" + +export CFLAGS_O3="@CFLAGS_O3@" +export CXXFLAGS_O3="@CXXFLAGS_O3@" +export FCFLAGS_O3="@FCFLAGS_O3@" +export F77FLAGS_O3="@F77FLAGS_O3@" + +export CFLAGS_NON_NATIVE="@CFLAGS_NON_NATIVE@" +export CXXFLAGS_NON_NATIVE="@CXXFLAGS_NON_NATIVE@" +export FCFLAGS_NON_NATIVE="@FCFLAGS_NON_NATIVE@" +export F77FLAGS_NON_NATIVE="@F77FLAGS_NON_NATIVE@" + +export ORIGINAL_CFLAGS="@ORIGINAL_CFLAGS@" +export ORIGINAL_CXXFLAGS="@ORIGINAL_CXXFLAGS@" +export ORIGINAL_FCFLAGS="@ORIGINAL_FCFLAGS@" +export ORIGINAL_F77FLAGS="@ORIGINAL_F77FLAGS@" + # This is usually blank if the system GMP is used, or $SAGE_LOCAL otherwise export SAGE_GMP_PREFIX="@SAGE_GMP_PREFIX@" export SAGE_GMP_INCLUDE="@SAGE_GMP_INCLUDE@" @@ -41,7 +62,7 @@ fi export SAGE_MPFR_PREFIX="@SAGE_MPFR_PREFIX@" if [ -n "$SAGE_MPFR_PREFIX" ]; then # Some packages that depend on MPFR accept a --with-mpfr= flag to - # their ./configure scripts. Thus we deal with this just as with GMP above. + # their ./configure scripts. Thus we deal with this just as with GMP above. export SAGE_CONFIGURE_MPFR="--with-mpfr=$SAGE_MPFR_PREFIX" fi @@ -50,7 +71,7 @@ fi export SAGE_MPC_PREFIX="@SAGE_MPC_PREFIX@" if [ -n "$SAGE_MPC_PREFIX" ]; then # Some packages that depend on MPC accept a --with-mpc= flag to - # their ./configure scripts. Thus we deal with this just as with GMP above. + # their ./configure scripts. Thus we deal with this just as with GMP above. export SAGE_CONFIGURE_MPC="--with-mpc=$SAGE_MPC_PREFIX" fi From caf0f6f4aabc1f1d1405db88c4d86904eef3450f Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Thu, 18 Jun 2020 08:00:51 +0200 Subject: [PATCH 029/713] O3 non native --- build/bin/sage-build-env-config.in | 5 +++++ configure.ac | 11 +++++++++++ 2 files changed, 16 insertions(+) diff --git a/build/bin/sage-build-env-config.in b/build/bin/sage-build-env-config.in index 6eb6267f61b..9a51c8476f3 100644 --- a/build/bin/sage-build-env-config.in +++ b/build/bin/sage-build-env-config.in @@ -38,6 +38,11 @@ export CXXFLAGS_NON_NATIVE="@CXXFLAGS_NON_NATIVE@" export FCFLAGS_NON_NATIVE="@FCFLAGS_NON_NATIVE@" export F77FLAGS_NON_NATIVE="@F77FLAGS_NON_NATIVE@" +export CFLAGS_O3_NON_NATIVE="@CFLAGS_O3_NON_NATIVE@" +export CXXFLAGS_O3_NON_NATIVE="@CXXFLAGS_O3_NON_NATIVE@" +export FCFLAGS_O3_NON_NATIVE="@FCFLAGS_O3_NON_NATIVE@" +export F77FLAGS_O3_NON_NATIVE="@F77FLAGS_O3_NON_NATIVE@" + export ORIGINAL_CFLAGS="@ORIGINAL_CFLAGS@" export ORIGINAL_CXXFLAGS="@ORIGINAL_CXXFLAGS@" export ORIGINAL_FCFLAGS="@ORIGINAL_FCFLAGS@" diff --git a/configure.ac b/configure.ac index 367650a3ced..4b028b3fc04 100644 --- a/configure.ac +++ b/configure.ac @@ -367,7 +367,9 @@ AC_SUBST(OBJCXX) AS_IF([test "x$CXX" = x], [AC_MSG_ERROR([a C++ compiler is missing])]) CFLAGS_NON_NATIVE="${CFLAGS}" +CFLAGS_O3_NON_NATIVE="${CFLAGS_O3}" AC_SUBST(CFLAGS_NON_NATIVE) +AC_SUBST(CFLAGS_O3_NON_NATIVE) # Adding march native in certain cases. if test "x$SAGE_FAT_BINARY" != "xyes" ; then @@ -402,39 +404,48 @@ if test "x${CXXFLAGS}" = "x"; then CXXFLAGS="$CFLAGS" CXXFLAGS_O3="$CFLAGS_O3" CXXFLAGS_NON_NATIVE="$CFLAGS_NON_NATIVE" + CXXFLAGS_O3_NON_NATIVE="$CFLAGS_O3_NON_NATIVE" else CXXFLAGS_03="$CXXFLAGS" CXXFLAGS_NON_NATIVE="$CXXFLAGS" + CXXFLAGS_O3_NON_NATIVE="$CXXFLAGS" fi AC_SUBST(CXXFLAGS) AC_SUBST(CCXFLAGS_O3) AC_SUBST(CXXFLAGS_NON_NATIVE) +AC_SUBST(CXXFLAGS_O3_NON_NATIVE) # Copy to FCFLAGS, if this is not set. if test "x${FCFLAGS}" = "x"; then FCFLAGS="$CFLAGS" FCFLAGS_O3="$CFLAGS_O3" FCFLAGS_NON_NATIVE="$CFLAGS_NON_NATIVE" + FCFLAGS_O3_NON_NATIVE="$CFLAGS_O3_NON_NATIVE" else FCFLAGS_03="$FCFLAGS" FCFLAGS_NON_NATIVE="$FCFLAGS" + FCFLAGS_O3_NON_NATIVE="$FCFLAGS" fi AC_SUBST(FCFLAGS) AC_SUBST(FCFLAGS_O3) AC_SUBST(FCFLAGS_NON_NATIVE) +AC_SUBST(FCFLAGS_O3_NON_NATIVE) # Copy to F77FLAGS, if this is not set. if test "x${F77FLAGS}" = "x"; then F77FLAGS="$CFLAGS" F77FLAGS_O3="$CFLAGS_O3" F77FLAGS_NON_NATIVE="$CFLAGS_NON_NATIVE" + F77FLAGS_O3_NON_NATIVE="$CFLAGS_NON_NATIVE" else F77FLAGS_03="$FCFLAGS" F77FLAGS_NON_NATIVE="$FCFLAGS" + F77FLAGS_O3_NON_NATIVE="$FCFLAGS" fi AC_SUBST(F77FLAGS) AC_SUBST(F77FLAGS_O3) AC_SUBST(F77FLAGS_NON_NATIVE) +AC_SUBST(F77FLAGS_O3_NON_NATIVE) ############################################################################### From 1f42fc8803a45cb0c3d7dddfa571e1d815161d5a Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Thu, 18 Jun 2020 08:01:28 +0200 Subject: [PATCH 030/713] non-native for eclib --- build/pkgs/eclib/spkg-install.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/pkgs/eclib/spkg-install.in b/build/pkgs/eclib/spkg-install.in index 9ccc0420e34..1611cfdd27d 100644 --- a/build/pkgs/eclib/spkg-install.in +++ b/build/pkgs/eclib/spkg-install.in @@ -1,5 +1,5 @@ -CFLAGS="$CFLAGS_O3" -CXXFLAGS="$CXXFLAGS_O3" +CFLAGS="$CFLAGS_O3_NON_NATIVE" +CXXFLAGS="$CXXFLAGS_O3_NON_NATIVE" export CFLAGS CXXFLAGS From 0329e0dac0594db020330baaa7aee788454e97fc Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Thu, 18 Jun 2020 08:24:19 +0200 Subject: [PATCH 031/713] export SAGE_FAT_BINARY of configure --- build/bin/sage-build-env-config.in | 3 +++ 1 file changed, 3 insertions(+) diff --git a/build/bin/sage-build-env-config.in b/build/bin/sage-build-env-config.in index 9a51c8476f3..019838a8863 100644 --- a/build/bin/sage-build-env-config.in +++ b/build/bin/sage-build-env-config.in @@ -48,6 +48,9 @@ export ORIGINAL_CXXFLAGS="@ORIGINAL_CXXFLAGS@" export ORIGINAL_FCFLAGS="@ORIGINAL_FCFLAGS@" export ORIGINAL_F77FLAGS="@ORIGINAL_F77FLAGS@" +# Export SAGE_FAT_BINARY if this was enabled during configure. +export SAGE_FAT_BINARY="@SAGE_FAT_BINARY@" + # This is usually blank if the system GMP is used, or $SAGE_LOCAL otherwise export SAGE_GMP_PREFIX="@SAGE_GMP_PREFIX@" export SAGE_GMP_INCLUDE="@SAGE_GMP_INCLUDE@" From 7399abd0955a7f5b65614c83a9428978ced5fe90 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Fri, 19 Jun 2020 11:21:53 +0200 Subject: [PATCH 032/713] r non-native --- build/pkgs/r/spkg-install.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/pkgs/r/spkg-install.in b/build/pkgs/r/spkg-install.in index b3dea4c5539..634a607c7ab 100644 --- a/build/pkgs/r/spkg-install.in +++ b/build/pkgs/r/spkg-install.in @@ -11,8 +11,8 @@ CPPFLAGS="$CPPFLAGS" # "for type of 'hidden' Fortran character lengths" # on ubuntu-bionic-minimal, ubuntu-eoan/focal-minimal, debian-buster/bullseye/sid-minimal, # linuxmint-19.3-minimal, archlinux-latest-minimal -CFLAGS="$CFLAGS -fPIC" -FCFLAGS="$FCFLAGS -fPIC" +CFLAGS="$CFLAGS_NON_NATIVE -fPIC" +FCFLAGS="$FCFLAGS_NON_NATIVE -fPIC" export CFLAGS CPPFLAGS FCFLAGS LDFLAGS From ceddbe1b24d52721c61858ccbef89fb0550181b4 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Fri, 19 Jun 2020 11:33:14 +0200 Subject: [PATCH 033/713] gap without native --- build/pkgs/gap/spkg-install.in | 3 +++ 1 file changed, 3 insertions(+) diff --git a/build/pkgs/gap/spkg-install.in b/build/pkgs/gap/spkg-install.in index 56e95338df1..c986e59da6d 100644 --- a/build/pkgs/gap/spkg-install.in +++ b/build/pkgs/gap/spkg-install.in @@ -4,6 +4,9 @@ cd src +export CFLAGS=$CFLAGS_NON_NATIVE +export CXXFLAGS=$CXXFLAGS_NON_NATIVE + GAP_BUILD_ROOT="$(pwd)" GAP_ROOT="$SAGE_LOCAL/share/gap" DESTDIR_GAP_ROOT="$SAGE_DESTDIR$GAP_ROOT" From 8ff40110dc5c896ba2c0a719b4b708e8f847daeb Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Tue, 23 Jun 2020 10:56:48 +0200 Subject: [PATCH 034/713] update documentation --- src/doc/en/developer/packaging.rst | 30 +++++++++++++++++++++++++++--- src/doc/en/installation/source.rst | 14 +++++++++++--- 2 files changed, 38 insertions(+), 6 deletions(-) diff --git a/src/doc/en/developer/packaging.rst b/src/doc/en/developer/packaging.rst index e4852e843e8..b065cf8bfb7 100644 --- a/src/doc/en/developer/packaging.rst +++ b/src/doc/en/developer/packaging.rst @@ -248,6 +248,30 @@ something like the following to install it: sdh_install doc/ "$SAGE_SHARE"/doc/PACKAGE_NAME fi +At build time :envvar:`CFLAGS`, :envvar:`CXXFLAGS`, :envvar:`FCFLAGS`, +and :envvar:`F77FLAGS` are usually set to ``-g -O2 -march=native`` +(according to `debugging options <../installation/source.html#sage-debug>`_ +and whether building +`fat binaries <../installation/source.html#sage-fat-binary>`_). + +Slightly modified versions are available: + +.. CODE-BLOCK:: bash + + # No ``-march=native``. + export CFLAGS=$CFLAGS_NON_NATIVE + + # ``-O3`` instead of ``-O2``. + export CFLAGS=$CFLAGS_O3 + + # No ``-march=native`` and ``-O3`` instead of ``-O2``. + export CFLAGS=$CFLAGS_O3_NON_NATIVE + + # Use flags as set by the user, possibly empty. + export CFLAGS=$ORIGINAL_CFLAGS + +Likewise for :envvar:`CXXFLAGS`, :envvar:`FCFLAGS`, and :envvar:`F77FLAGS`. + .. note:: Prior to Sage 9.1, the script templates were called ``spkg-build``, @@ -619,7 +643,7 @@ For example, considering the layout: SAGE_ROOT/build/pkgs/foo |-- patches | |-- solaris - | | |-- solaris.patch + | | |-- solaris.patch | |-- bar.patch | `-- baz.patch @@ -674,7 +698,7 @@ When to patch, when to repackage, when to autoconfiscate - If the upstream Makefile does not build shared libraries, don't bother trying to patch it. - + Autoconfiscate the package instead and use the standard facilities of Automake and Libtool. This ensures that the shared library build is portable between Linux and macOS. @@ -718,7 +742,7 @@ We recommend the following workflow for maintaining a set of patches. rm -Rf SAGE_ROOT/build/pkgs/PACKAGE/patches mkdir SAGE_ROOT/build/pkgs/PACKAGE/patches git format-patch -o SAGE_ROOT/build/pkgs/PACKAGE/patches/ upstream - + - Optionally, create an ``spkg-src`` file in the Sage package's directory that regenerates the patch directory using the above commands. diff --git a/src/doc/en/installation/source.rst b/src/doc/en/installation/source.rst index 15c62c9cf60..7e0a5afb8c3 100644 --- a/src/doc/en/installation/source.rst +++ b/src/doc/en/installation/source.rst @@ -1171,7 +1171,9 @@ Here are some of the more commonly used variables affecting the build process: building ccache for Sage, so that Sage can pull down the necessary sources. -- :envvar:`SAGE_DEBUG` - controls debugging support. +- .. _sage_debug: + + :envvar:`SAGE_DEBUG` - controls debugging support. There are three different possible values: * Not set (or set to anything else than "yes" or "no"): build binaries with @@ -1188,6 +1190,9 @@ Here are some of the more commonly used variables affecting the build process: These will be notably slower but, for example, make it much easier to pinpoint memory allocation problems. + Instead of using :envvar:`SAGE_DEBUG` one can configure with + ``--enable-debug={no|symbols|yes}``. + - :envvar:`SAGE_PROFILE` - controls profiling support. If this is set to ``yes``, profiling support is enabled where possible. Note that Python-level profiling is always available; This option enables @@ -1276,9 +1281,12 @@ Here are some of the more commonly used variables affecting the build process: So you can set this variable to ``yes`` instead of using the ``-s`` flag for ``sage -i`` and ``sage -f``. -- :envvar:`SAGE_FAT_BINARY` - to build binaries that will run on the +- .. _sage_fat_binary: + + :envvar:`SAGE_FAT_BINARY` - to build binaries that will run on the widest range of target CPUs set this variable to ``yes`` before - building Sage. This does not make the binaries relocatable, it only + building Sage or configure with ``--enable-fat-binary``. + This does not make the binaries relocatable, it only avoids newer CPU instruction set extensions. For relocatable (=can be moved to a different directory) binaries, you must use https://github.com/sagemath/binary-pkg From df53cf3187dc4ad937e4a0ca48bc034a4dcad9ae Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Tue, 23 Jun 2020 11:25:24 +0200 Subject: [PATCH 035/713] --enable-debug as configuration option --- configure.ac | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 4b028b3fc04..27bf142eb95 100644 --- a/configure.ac +++ b/configure.ac @@ -90,6 +90,12 @@ AC_ARG_ENABLE([fat-binary], [AC_SUBST(SAGE_FAT_BINARY, "yes")], []) +AC_ARG_ENABLE([debug], + [AS_HELP_STRING([--enable-debug={no|symbols|yes}], + [controls debugging support: "no" debugging; debugging "symbols" (default); build debug version ("yes")])], + [AC_SUBST(SAGE_DEBUG, $enableval)], + []) + # Check whether we are on a supported platform AC_CANONICAL_BUILD() AC_CANONICAL_HOST() @@ -333,7 +339,7 @@ if test "x${CFLAGS}" = "x"; then CFLAGS="-Og -g" CFLAGS_O3="-Og -g" else - if test "x$SAGE_DEBUG" = "no" ; then + if test "x$SAGE_DEBUG" = "xno" ; then CFLAGS="-O2" CFLAGS_O3="-O3" else From 6fd7ba74376c2ce09e150ac548ead5b138eadee0 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Tue, 23 Jun 2020 13:55:14 +0200 Subject: [PATCH 036/713] move to buil/spkg/{gcc,gfortran}/spkg-configure.ac --- build/pkgs/gcc/spkg-configure.m4 | 89 ++++++++++- build/pkgs/gfortran/spkg-configure.m4 | 28 ++++ configure.ac | 203 +++++--------------------- 3 files changed, 148 insertions(+), 172 deletions(-) diff --git a/build/pkgs/gcc/spkg-configure.m4 b/build/pkgs/gcc/spkg-configure.m4 index a358b37498d..6ea5260ab3b 100644 --- a/build/pkgs/gcc/spkg-configure.m4 +++ b/build/pkgs/gcc/spkg-configure.m4 @@ -49,13 +49,32 @@ AC_DEFUN([SAGE_CHECK_BROKEN_GCC], [ fi ]) +dnl Save the flags as configured by the user. +AC_DEFUN([SAGE_SAVE_ORIGINAL_FLAGS], [ + AC_SUBST(ORIGINAL_CFLAGS, "$CFLAGS") + AC_SUBST(ORIGINAL_CXXFLAGS, "$CXXFLAGS") + AC_SUBST(ORIGINAL_FCFLAGS, "$FCFLAGS") + AC_SUBST(ORIGINAL_F77FLAGS, "$F77FLAGS") +]) SAGE_SPKG_CONFIGURE_BASE([gcc], [ - AC_REQUIRE([AC_PROG_CC]) - AC_REQUIRE([AC_PROG_CPP]) - AC_REQUIRE([AC_PROG_CXX]) - AC_REQUIRE([AC_PROG_OBJC]) - AC_REQUIRE([AC_PROG_OBJCXX]) + AC_REQUIRE([SAGE_SAVE_ORIGINAL_FLAGS]) + + AC_REQUIRE([SAGE_CHECK_CONDA_COMPILERS]) + + AC_REQUIRE([AC_PROG_CC]) + AC_REQUIRE([AC_PROG_CPP]) + AC_REQUIRE([AC_PROG_CXX]) + + AC_SUBST(CC) + AC_SUBST(FC) + + AC_REQUIRE([AC_PROG_OBJC]) + AC_REQUIRE([AC_PROG_OBJCXX]) + AC_SUBST(OBJC) + AC_SUBST(OBJCXX) + + AS_IF([test "x$CXX" = x], [AC_MSG_ERROR([a C++ compiler is missing])]) if test -f "$SAGE_LOCAL/bin/gcc"; then # Special value for SAGE_INSTALL_GCC if GCC is already installed @@ -151,6 +170,10 @@ SAGE_SPKG_CONFIGURE_BASE([gcc], [ # Install our own GCC if the system-provided one is newer than 9.x. # See https://trac.sagemath.org/ticket/29456 SAGE_SHOULD_INSTALL_GCC([$CXX is g++ version $GXX_VERSION, which is too recent for this version of Sage]) + ], + [4.[[8-9]].*|5.[[0-1]].*], [ + # GCC less than 5.1 is not ready for AVX512. + sage_use_march_native=no ]) fi @@ -201,6 +224,62 @@ SAGE_SPKG_CONFIGURE_BASE([gcc], [ SAGE_SRC="$SAGE_SRC" ]) fi + + # Determine which compiler flags should be set. + if test "x$sage_use_march_native" = "xno"; then + march="" + elif test "x$SAGE_FAT_BINARY" = "xyes"; then + march="" + elif test "x$SAGE_DEBUG" = "xyes"; then + march="" + elif test "x$sage_spkg_install_gcc" =" xyes"; then + march=" -march=native" + else + AX_CHECK_COMPILE_FLAG("-march=native", [march=" -march=native"], [march=""], [], []) + fi + + if test "x$ORIGINAL_CFLAGS" = "x"; then + # Evaluate SAGE_DEBUG: + if test "x$SAGE_DEBUG" = "xyes" ; then + AC_SUBST(CFLAGS_NON_NATIVE, "-Og -g") + AC_SUBST(CFLAGS_O3_NON_NATIVE, "-Og -g") + else + if test "x$SAGE_DEBUG" = "xno" ; then + AC_SUBST(CFLAGS_NON_NATIVE, "-O2") + AC_SUBST(CFLAGS_O3_NON_NATIVE, "-O3") + else + AC_SUBST(CFLAGS_NON_NATIVE, "-O2 -g") + AC_SUBST(CFLAGS_O3_NON_NATIVE, "-O3 -g") + fi + fi + AC_SUBST(CFLAGS, "${CFLAGS_NON_NATIVE}${march}") + AC_SUBST(CFLAGS_O3, "${CFLAGS_O3_NON_NATIVE}${march}") + else + # Respect user environment variable. + AC_SUBST(CFLAGS, "${CFLAGS}") + AC_SUBST(CFLAGS_O3, "${CFLAGS}") + AC_SUBST(CFLAGS_NON_NATIVE, "${CFLAGS}") + AC_SUBST(CFLAGS_O3_NON_NATIVE, "${CFLAGS}") + fi + + AC_MSG_NOTICE(ORIGINAL_CFLAGS=$ORIGINAL_CFLAGS) + AC_MSG_NOTICE(CFLAGS=$CFLAGS) + AC_MSG_NOTICE(CFLAGS_O3=$CFLAGS_O3) + AC_MSG_NOTICE(CFLAGS_NON_NATIVE=$CFLAGS_NON_NATIVE) + AC_MSG_NOTICE(CFLAGS_O3_NON_NATIVE=$CFLAGS_O3_NON_NATIVE) + + # Copy to CXXFLAGS if this is not set. + if test "x$CXXFLAGS" = "x"; then + AC_SUBST(CXXFLAGS, "$CFLAGS") + AC_SUBST(CXXFLAGS_O3, "$CFLAGS_O3") + AC_SUBST(CXXFLAGS_NON_NATIVE, "$CFLAGS_NON_NATIVE") + AC_SUBST(CXXFLAGS_O3_NON_NATIVE, "$CFLAGS_O3_NON_NATIVE") + else + AC_SUBST(CXXFLAGS_03, "$CXXFLAGS") + AC_SUBST(CXXFLAGS_NON_NATIVE, "$CXXFLAGS") + AC_SUBST(CXXFLAGS_O3_NON_NATIVE, "$CXXFLAGS") + fi + ], , , [ # Trac #27907: Find location of crti.o from the system CC, in case we build our own gcc AC_MSG_CHECKING([for the location of crti.o]) diff --git a/build/pkgs/gfortran/spkg-configure.m4 b/build/pkgs/gfortran/spkg-configure.m4 index d75bbca9be5..09c0f99dbd2 100644 --- a/build/pkgs/gfortran/spkg-configure.m4 +++ b/build/pkgs/gfortran/spkg-configure.m4 @@ -1,6 +1,9 @@ SAGE_SPKG_CONFIGURE([gfortran], [ AC_REQUIRE([SAGE_SPKG_CONFIGURE_GCC]) + AC_REQUIRE([AC_PROG_FC]) + AC_SUBST(FC) + # Check that the Fortran compiler accepts free-format source code (as # opposed to the older fixed-format style from Fortran 77). # This helps verify the compiler works too, so if some idiot sets FC to @@ -21,4 +24,29 @@ SAGE_SPKG_CONFIGURE([gfortran], [ if test "x$sage_spkg_install_gcc" = "xyes" -o x$SAGE_INSTALL_GCC = xexists; then sage_spkg_install_gfortran=no fi + + # Copy CFLAGS to FCFLAGS if this is not set. + if test "x$FCFLAGS" = "x"; then + AC_SUBST(FCFLAGS, "$CFLAGS") + AC_SUBST(FCFLAGS_O3, "$CFLAGS_O3") + AC_SUBST(FCFLAGS_NON_NATIVE, "$CFLAGS_NON_NATIVE") + AC_SUBST(FCFLAGS_O3_NON_NATIVE, "$CFLAGS_O3_NON_NATIVE") + else + AC_SUBST(FCFLAGS_03, "$FCFLAGS") + AC_SUBST(FCFLAGS_NON_NATIVE, "$FCFLAGS") + AC_SUBST(FCFLAGS_O3_NON_NATIVE, "$FCFLAGS") + fi + + # Copy FCFLAGS to F77FLAGS if this is not set. + if test "x$F77FLAGS" = "x"; then + AC_SUBST(F77FLAGS, "$FCFLAGS") + AC_SUBST(F77FLAGS_O3, "$FCFLAGS_O3") + AC_SUBST(F77FLAGS_NON_NATIVE, "$FCFLAGS_NON_NATIVE") + AC_SUBST(F77FLAGS_O3_NON_NATIVE, "$FCFLAGS_O3_NON_NATIVE") + else + AC_SUBST(F77FLAGS_03, "$F77FLAGS") + AC_SUBST(F77FLAGS_NON_NATIVE, "$F77FLAGS") + AC_SUBST(F77FLAGS_O3_NON_NATIVE, "$F77FLAGS") + fi + ]) diff --git a/configure.ac b/configure.ac index 27bf142eb95..48f917fbb00 100644 --- a/configure.ac +++ b/configure.ac @@ -319,139 +319,48 @@ AX_PROG_PERL_VERSION([5.8.0],[],[ ############################################################################### -# Check C/C++/Fortran compilers and set CFLAGS +# Collect packages and check C/C++/Fortran compilers ############################################################################### -ORIGINAL_CFLAGS="$CFLAGS" -ORIGINAL_CXXFLAGS="$CXXFLAGS" -ORIGINAL_FCFLAGS="$FCFLAGS" -ORIGINAL_F77FLAGS="$F77FLAGS" -AC_SUBST(ORIGINAL_CFLAGS) -AC_SUBST(ORIGINAL_CXXFLAGS) -AC_SUBST(ORIGINAL_FCFLAGS) -AC_SUBST(ORIGINAL_F77FLAGS) - -if test "x${CFLAGS}" = "x"; then - GOT_FLAGS="no" - - # Evaluate SAGE_DEBUG: - if test "x$SAGE_DEBUG" = "xyes" ; then - CFLAGS="-Og -g" - CFLAGS_O3="-Og -g" - else - if test "x$SAGE_DEBUG" = "xno" ; then - CFLAGS="-O2" - CFLAGS_O3="-O3" - else - CFLAGS="-O2 -g" - CFLAGS_O3="-O3 -g" - fi - fi -else - GOT_FLAGS="yes" - CFLAGS_O3="${CFLAGS}" +# Python version +AC_ARG_WITH([python], +[AS_HELP_STRING([--with-python=3], + [build and use Python 3 (default)])]) + +case "$with_python" in + 3) SAGE_PYTHON_VERSION=3;; + 3*) AC_MSG_WARN([the only allowed value for --with-python is 3; specific Python 3.x versions cannot be requested using --with-python. For compatibility reasons, this is only a warning. Use './configure PYTHON3=/path/to/python3' to use a specific Python 3 binary for the Sage venv.]) + SAGE_PYTHON_VERSION=3;; + "") SAGE_PYTHON_VERSION=3;; + *) + AC_MSG_ERROR([the only allowed value for --with-python is 3. Support for Python 2 has been removed in Sage 9.2.]);; +esac +AC_SUBST(SAGE_PYTHON_VERSION) + +# $(TOOLCHAIN) variable containing prerequisites for the build +SAGE_TOOLCHAIN=gcc +if test "$SAGE_INSTALL_CCACHE" = yes ; then + SAGE_TOOLCHAIN="$SAGE_TOOLCHAIN ccache" fi +AC_SUBST([SAGE_TOOLCHAIN]) -SAGE_CHECK_CONDA_COMPILERS - -AC_PROG_CC() -AC_PROG_CPP() -AC_PROG_CXX() -AC_PROG_FC() - -AC_SUBST(CC) -AC_SUBST(FC) - -# On darwin, also set the objective C/C++ compilers -# Checking on all platforms doesn't hurt and stops -# configure from sending an error when run on non-darwin. -AC_PROG_OBJC() -AC_PROG_OBJCXX() -AC_SUBST(OBJC) -AC_SUBST(OBJCXX) - -AS_IF([test "x$CXX" = x], [AC_MSG_ERROR([a C++ compiler is missing])]) - -CFLAGS_NON_NATIVE="${CFLAGS}" -CFLAGS_O3_NON_NATIVE="${CFLAGS_O3}" -AC_SUBST(CFLAGS_NON_NATIVE) -AC_SUBST(CFLAGS_O3_NON_NATIVE) - -# Adding march native in certain cases. -if test "x$SAGE_FAT_BINARY" != "xyes" ; then - if test "x$GOT_FLAGS" = "xno"; then - - # GCC less than 5.1 is not yet ready for "-march=native", - # but it claims it is. - GCC_LT_51="no" - if test "x$GCC" = "xyes"; then - GCC_GTE_51=$(expr `${CC} --version | grep ^gcc | sed 's/^.* //g' | sed -e 's/\.\([0-9][0-9]\)/\1/g' -e 's/\.\([0-9]\)/0\1/g' -e 's/^[0-9]\{3,4\}$/&00/'` \>= 50100) - if test "x$GCC_GTE_51" = "x0"; then - GCC_LT_51="yes" - fi - fi +AC_ARG_ENABLE([experimental-packages], + [AS_HELP_STRING([--enable-experimental-packages], + [allow installing experimental packages (default: no = ask for user confirmation for each package)])]) +AC_ARG_ENABLE([download-from-upstream-url], + [AS_HELP_STRING([--enable-download-from-upstream-url], + [allow downloading packages from their upstream URL if they cannot be found on the Sage mirrors])]) - if test "x$GCC_LT_51" = "xno"; then - AX_CHECK_COMPILE_FLAG("-march=native", [MARCH=" -march=native"], [MARCH=""], [], []) - CFLAGS="${CFLAGS}${MARCH}" - CFLAGS_O3="${CFLAGS_O3}${MARCH}" - fi +SAGE_SPKG_OPTIONS="" +AS_IF([test "x$enable_experimental_packages" = "xyes"], [ + AS_VAR_APPEND([SAGE_SPKG_OPTIONS], [" -y"]) +]) +AS_IF([test "x$enable_download_from_upstream_url" = "xyes"], [ + AS_VAR_APPEND([SAGE_SPKG_OPTIONS], [" -o"]) +]) +AC_SUBST([SAGE_SPKG_OPTIONS]) - fi -fi -AC_SUBST(CFLAGS) -AC_SUBST(CFLAGS_O3) -AC_MSG_NOTICE(CFLAGS_NON_NATIVE=$CFLAGS_NON_NATIVE) -AC_MSG_NOTICE(CFLAGS=$CFLAGS) -AC_MSG_NOTICE(CFLAGS_O3=$CFLAGS_O3) - -# Copy to CXXFLAGS, if this is not set. -if test "x${CXXFLAGS}" = "x"; then - CXXFLAGS="$CFLAGS" - CXXFLAGS_O3="$CFLAGS_O3" - CXXFLAGS_NON_NATIVE="$CFLAGS_NON_NATIVE" - CXXFLAGS_O3_NON_NATIVE="$CFLAGS_O3_NON_NATIVE" -else - CXXFLAGS_03="$CXXFLAGS" - CXXFLAGS_NON_NATIVE="$CXXFLAGS" - CXXFLAGS_O3_NON_NATIVE="$CXXFLAGS" -fi -AC_SUBST(CXXFLAGS) -AC_SUBST(CCXFLAGS_O3) -AC_SUBST(CXXFLAGS_NON_NATIVE) -AC_SUBST(CXXFLAGS_O3_NON_NATIVE) - -# Copy to FCFLAGS, if this is not set. -if test "x${FCFLAGS}" = "x"; then - FCFLAGS="$CFLAGS" - FCFLAGS_O3="$CFLAGS_O3" - FCFLAGS_NON_NATIVE="$CFLAGS_NON_NATIVE" - FCFLAGS_O3_NON_NATIVE="$CFLAGS_O3_NON_NATIVE" -else - FCFLAGS_03="$FCFLAGS" - FCFLAGS_NON_NATIVE="$FCFLAGS" - FCFLAGS_O3_NON_NATIVE="$FCFLAGS" -fi -AC_SUBST(FCFLAGS) -AC_SUBST(FCFLAGS_O3) -AC_SUBST(FCFLAGS_NON_NATIVE) -AC_SUBST(FCFLAGS_O3_NON_NATIVE) - -# Copy to F77FLAGS, if this is not set. -if test "x${F77FLAGS}" = "x"; then - F77FLAGS="$CFLAGS" - F77FLAGS_O3="$CFLAGS_O3" - F77FLAGS_NON_NATIVE="$CFLAGS_NON_NATIVE" - F77FLAGS_O3_NON_NATIVE="$CFLAGS_NON_NATIVE" -else - F77FLAGS_03="$FCFLAGS" - F77FLAGS_NON_NATIVE="$FCFLAGS" - F77FLAGS_O3_NON_NATIVE="$FCFLAGS" -fi -AC_SUBST(F77FLAGS) -AC_SUBST(F77FLAGS_O3) -AC_SUBST(F77FLAGS_NON_NATIVE) -AC_SUBST(F77FLAGS_O3_NON_NATIVE) +SAGE_SPKG_COLLECT() ############################################################################### @@ -500,46 +409,6 @@ SAGE_CHECK_OSX_SUPPORTED() # Collect substitutions for build/make/Makefile.in ############################################################################### -# Python version -AC_ARG_WITH([python], -[AS_HELP_STRING([--with-python=3], - [build and use Python 3 (default)])]) - -case "$with_python" in - 3) SAGE_PYTHON_VERSION=3;; - 3*) AC_MSG_WARN([the only allowed value for --with-python is 3; specific Python 3.x versions cannot be requested using --with-python. For compatibility reasons, this is only a warning. Use './configure PYTHON3=/path/to/python3' to use a specific Python 3 binary for the Sage venv.]) - SAGE_PYTHON_VERSION=3;; - "") SAGE_PYTHON_VERSION=3;; - *) - AC_MSG_ERROR([the only allowed value for --with-python is 3. Support for Python 2 has been removed in Sage 9.2.]);; -esac -AC_SUBST(SAGE_PYTHON_VERSION) - -# $(TOOLCHAIN) variable containing prerequisites for the build -SAGE_TOOLCHAIN=gcc -if test "$SAGE_INSTALL_CCACHE" = yes ; then - SAGE_TOOLCHAIN="$SAGE_TOOLCHAIN ccache" -fi -AC_SUBST([SAGE_TOOLCHAIN]) - -AC_ARG_ENABLE([experimental-packages], - [AS_HELP_STRING([--enable-experimental-packages], - [allow installing experimental packages (default: no = ask for user confirmation for each package)])]) -AC_ARG_ENABLE([download-from-upstream-url], - [AS_HELP_STRING([--enable-download-from-upstream-url], - [allow downloading packages from their upstream URL if they cannot be found on the Sage mirrors])]) - -SAGE_SPKG_OPTIONS="" -AS_IF([test "x$enable_experimental_packages" = "xyes"], [ - AS_VAR_APPEND([SAGE_SPKG_OPTIONS], [" -y"]) -]) -AS_IF([test "x$enable_download_from_upstream_url" = "xyes"], [ - AS_VAR_APPEND([SAGE_SPKG_OPTIONS], [" -o"]) -]) -AC_SUBST([SAGE_SPKG_OPTIONS]) - -SAGE_SPKG_COLLECT() - # We need sage-env-config to exist before running this. # TODO: fix this in Trac #21524 touch "$SAGE_SRC/bin/sage-env-config" From 03b002a8beb5e757143ba142506382ebb8e9fe21 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Tue, 23 Jun 2020 14:02:14 +0200 Subject: [PATCH 037/713] disable native for gcc build --- build/pkgs/gcc/spkg-install.in | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/build/pkgs/gcc/spkg-install.in b/build/pkgs/gcc/spkg-install.in index 819a9caba59..f7c8db1de19 100644 --- a/build/pkgs/gcc/spkg-install.in +++ b/build/pkgs/gcc/spkg-install.in @@ -1,3 +1,7 @@ +# If we are building gcc ourselves, usually the system compiler isn't suitable. +export CFLAGS=$CFLAGS_NON_NATIVE +export CXXFLAGS=$CXXFLAGS_NON_NATIVE + # First, install any custom binaries or headers we created for macOS # workarounds for dir in bin include; do From 0660ab0573f27cd4c7bc2535e22f8f28ba812697 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Wed, 24 Jun 2020 08:02:52 +0200 Subject: [PATCH 038/713] partly undo reordering --- build/pkgs/gcc/spkg-configure.m4 | 27 ++------ build/pkgs/gfortran/spkg-configure.m4 | 19 +++-- configure.ac | 99 +++++++++++++++++---------- 3 files changed, 73 insertions(+), 72 deletions(-) diff --git a/build/pkgs/gcc/spkg-configure.m4 b/build/pkgs/gcc/spkg-configure.m4 index 6ea5260ab3b..9a2882e7e9e 100644 --- a/build/pkgs/gcc/spkg-configure.m4 +++ b/build/pkgs/gcc/spkg-configure.m4 @@ -49,32 +49,13 @@ AC_DEFUN([SAGE_CHECK_BROKEN_GCC], [ fi ]) -dnl Save the flags as configured by the user. -AC_DEFUN([SAGE_SAVE_ORIGINAL_FLAGS], [ - AC_SUBST(ORIGINAL_CFLAGS, "$CFLAGS") - AC_SUBST(ORIGINAL_CXXFLAGS, "$CXXFLAGS") - AC_SUBST(ORIGINAL_FCFLAGS, "$FCFLAGS") - AC_SUBST(ORIGINAL_F77FLAGS, "$F77FLAGS") -]) SAGE_SPKG_CONFIGURE_BASE([gcc], [ - AC_REQUIRE([SAGE_SAVE_ORIGINAL_FLAGS]) - - AC_REQUIRE([SAGE_CHECK_CONDA_COMPILERS]) - AC_REQUIRE([AC_PROG_CC]) AC_REQUIRE([AC_PROG_CPP]) AC_REQUIRE([AC_PROG_CXX]) - - AC_SUBST(CC) - AC_SUBST(FC) - AC_REQUIRE([AC_PROG_OBJC]) AC_REQUIRE([AC_PROG_OBJCXX]) - AC_SUBST(OBJC) - AC_SUBST(OBJCXX) - - AS_IF([test "x$CXX" = x], [AC_MSG_ERROR([a C++ compiler is missing])]) if test -f "$SAGE_LOCAL/bin/gcc"; then # Special value for SAGE_INSTALL_GCC if GCC is already installed @@ -269,15 +250,15 @@ SAGE_SPKG_CONFIGURE_BASE([gcc], [ AC_MSG_NOTICE(CFLAGS_O3_NON_NATIVE=$CFLAGS_O3_NON_NATIVE) # Copy to CXXFLAGS if this is not set. - if test "x$CXXFLAGS" = "x"; then + if test "x$ORIGINAL_CXXFLAGS" = "x"; then AC_SUBST(CXXFLAGS, "$CFLAGS") AC_SUBST(CXXFLAGS_O3, "$CFLAGS_O3") AC_SUBST(CXXFLAGS_NON_NATIVE, "$CFLAGS_NON_NATIVE") AC_SUBST(CXXFLAGS_O3_NON_NATIVE, "$CFLAGS_O3_NON_NATIVE") else - AC_SUBST(CXXFLAGS_03, "$CXXFLAGS") - AC_SUBST(CXXFLAGS_NON_NATIVE, "$CXXFLAGS") - AC_SUBST(CXXFLAGS_O3_NON_NATIVE, "$CXXFLAGS") + AC_SUBST(CXXFLAGS_03, "$ORIGINAL_CXXFLAGS") + AC_SUBST(CXXFLAGS_NON_NATIVE, "$ORIGINAL_CXXFLAGS") + AC_SUBST(CXXFLAGS_O3_NON_NATIVE, "$ORIGINAL_CXXFLAGS") fi ], , , [ diff --git a/build/pkgs/gfortran/spkg-configure.m4 b/build/pkgs/gfortran/spkg-configure.m4 index 09c0f99dbd2..0bd2aa0f0f1 100644 --- a/build/pkgs/gfortran/spkg-configure.m4 +++ b/build/pkgs/gfortran/spkg-configure.m4 @@ -1,9 +1,6 @@ SAGE_SPKG_CONFIGURE([gfortran], [ AC_REQUIRE([SAGE_SPKG_CONFIGURE_GCC]) - AC_REQUIRE([AC_PROG_FC]) - AC_SUBST(FC) - # Check that the Fortran compiler accepts free-format source code (as # opposed to the older fixed-format style from Fortran 77). # This helps verify the compiler works too, so if some idiot sets FC to @@ -26,27 +23,27 @@ SAGE_SPKG_CONFIGURE([gfortran], [ fi # Copy CFLAGS to FCFLAGS if this is not set. - if test "x$FCFLAGS" = "x"; then + if test "x$ORIGINAL_FCFLAGS" = "x"; then AC_SUBST(FCFLAGS, "$CFLAGS") AC_SUBST(FCFLAGS_O3, "$CFLAGS_O3") AC_SUBST(FCFLAGS_NON_NATIVE, "$CFLAGS_NON_NATIVE") AC_SUBST(FCFLAGS_O3_NON_NATIVE, "$CFLAGS_O3_NON_NATIVE") else - AC_SUBST(FCFLAGS_03, "$FCFLAGS") - AC_SUBST(FCFLAGS_NON_NATIVE, "$FCFLAGS") - AC_SUBST(FCFLAGS_O3_NON_NATIVE, "$FCFLAGS") + AC_SUBST(FCFLAGS_03, "$ORIGINAL_FCFLAGS") + AC_SUBST(FCFLAGS_NON_NATIVE, "$ORIGINAL_FCFLAGS") + AC_SUBST(FCFLAGS_O3_NON_NATIVE, "$ORIGINAL_FCFLAGS") fi # Copy FCFLAGS to F77FLAGS if this is not set. - if test "x$F77FLAGS" = "x"; then + if test "x$ORIGINAL_F77FLAGS" = "x"; then AC_SUBST(F77FLAGS, "$FCFLAGS") AC_SUBST(F77FLAGS_O3, "$FCFLAGS_O3") AC_SUBST(F77FLAGS_NON_NATIVE, "$FCFLAGS_NON_NATIVE") AC_SUBST(F77FLAGS_O3_NON_NATIVE, "$FCFLAGS_O3_NON_NATIVE") else - AC_SUBST(F77FLAGS_03, "$F77FLAGS") - AC_SUBST(F77FLAGS_NON_NATIVE, "$F77FLAGS") - AC_SUBST(F77FLAGS_O3_NON_NATIVE, "$F77FLAGS") + AC_SUBST(F77FLAGS_03, "$ORIGINAL_F77FLAGS") + AC_SUBST(F77FLAGS_NON_NATIVE, "$ORIGINAL_F77FLAGS") + AC_SUBST(F77FLAGS_O3_NON_NATIVE, "$ORIGINAL_F77FLAGS") fi ]) diff --git a/configure.ac b/configure.ac index 48f917fbb00..e29c740c1cb 100644 --- a/configure.ac +++ b/configure.ac @@ -316,51 +316,35 @@ AX_PROG_PERL_VERSION([5.8.0],[],[ AC_MSG_ERROR([Exiting, since AC_PACKAGE_NAME requires perl-5.8.0 or later]) ]) - - ############################################################################### -# Collect packages and check C/C++/Fortran compilers +# Check C/C++/Fortran compilers ############################################################################### -# Python version -AC_ARG_WITH([python], -[AS_HELP_STRING([--with-python=3], - [build and use Python 3 (default)])]) +# Save compiler flags as configured by the user. +AC_SUBST(ORIGINAL_CFLAGS, "$CFLAGS") +AC_SUBST(ORIGINAL_CXXFLAGS, "$CXXFLAGS") +AC_SUBST(ORIGINAL_FCFLAGS, "$FCFLAGS") +AC_SUBST(ORIGINAL_F77FLAGS, "$F77FLAGS") -case "$with_python" in - 3) SAGE_PYTHON_VERSION=3;; - 3*) AC_MSG_WARN([the only allowed value for --with-python is 3; specific Python 3.x versions cannot be requested using --with-python. For compatibility reasons, this is only a warning. Use './configure PYTHON3=/path/to/python3' to use a specific Python 3 binary for the Sage venv.]) - SAGE_PYTHON_VERSION=3;; - "") SAGE_PYTHON_VERSION=3;; - *) - AC_MSG_ERROR([the only allowed value for --with-python is 3. Support for Python 2 has been removed in Sage 9.2.]);; -esac -AC_SUBST(SAGE_PYTHON_VERSION) +SAGE_CHECK_CONDA_COMPILERS -# $(TOOLCHAIN) variable containing prerequisites for the build -SAGE_TOOLCHAIN=gcc -if test "$SAGE_INSTALL_CCACHE" = yes ; then - SAGE_TOOLCHAIN="$SAGE_TOOLCHAIN ccache" -fi -AC_SUBST([SAGE_TOOLCHAIN]) +AC_PROG_CC() +AC_PROG_CPP() +AC_PROG_CXX() +AC_PROG_FC() -AC_ARG_ENABLE([experimental-packages], - [AS_HELP_STRING([--enable-experimental-packages], - [allow installing experimental packages (default: no = ask for user confirmation for each package)])]) -AC_ARG_ENABLE([download-from-upstream-url], - [AS_HELP_STRING([--enable-download-from-upstream-url], - [allow downloading packages from their upstream URL if they cannot be found on the Sage mirrors])]) +AC_SUBST(CC) +AC_SUBST(FC) -SAGE_SPKG_OPTIONS="" -AS_IF([test "x$enable_experimental_packages" = "xyes"], [ - AS_VAR_APPEND([SAGE_SPKG_OPTIONS], [" -y"]) -]) -AS_IF([test "x$enable_download_from_upstream_url" = "xyes"], [ - AS_VAR_APPEND([SAGE_SPKG_OPTIONS], [" -o"]) -]) -AC_SUBST([SAGE_SPKG_OPTIONS]) +# On darwin, also set the objective C/C++ compilers +# Checking on all platforms doesn't hurt and stops +# configure from sending an error when run on non-darwin. +AC_PROG_OBJC() +AC_PROG_OBJCXX() +AC_SUBST(OBJC) +AC_SUBST(OBJCXX) -SAGE_SPKG_COLLECT() +AS_IF([test "x$CXX" = x], [AC_MSG_ERROR([a C++ compiler is missing])]) ############################################################################### @@ -404,11 +388,50 @@ AS_IF([echo "$ac_pwd" |grep " " >/dev/null], SAGE_CHECK_OSX_SUPPORTED() - ############################################################################### # Collect substitutions for build/make/Makefile.in ############################################################################### +# Python version +AC_ARG_WITH([python], +[AS_HELP_STRING([--with-python=3], + [build and use Python 3 (default)])]) + +case "$with_python" in + 3) SAGE_PYTHON_VERSION=3;; + 3*) AC_MSG_WARN([the only allowed value for --with-python is 3; specific Python 3.x versions cannot be requested using --with-python. For compatibility reasons, this is only a warning. Use './configure PYTHON3=/path/to/python3' to use a specific Python 3 binary for the Sage venv.]) + SAGE_PYTHON_VERSION=3;; + "") SAGE_PYTHON_VERSION=3;; + *) + AC_MSG_ERROR([the only allowed value for --with-python is 3. Support for Python 2 has been removed in Sage 9.2.]);; +esac +AC_SUBST(SAGE_PYTHON_VERSION) + +# $(TOOLCHAIN) variable containing prerequisites for the build +SAGE_TOOLCHAIN=gcc +if test "$SAGE_INSTALL_CCACHE" = yes ; then + SAGE_TOOLCHAIN="$SAGE_TOOLCHAIN ccache" +fi +AC_SUBST([SAGE_TOOLCHAIN]) + +AC_ARG_ENABLE([experimental-packages], + [AS_HELP_STRING([--enable-experimental-packages], + [allow installing experimental packages (default: no = ask for user confirmation for each package)])]) +AC_ARG_ENABLE([download-from-upstream-url], + [AS_HELP_STRING([--enable-download-from-upstream-url], + [allow downloading packages from their upstream URL if they cannot be found on the Sage mirrors])]) + +SAGE_SPKG_OPTIONS="" +AS_IF([test "x$enable_experimental_packages" = "xyes"], [ + AS_VAR_APPEND([SAGE_SPKG_OPTIONS], [" -y"]) +]) +AS_IF([test "x$enable_download_from_upstream_url" = "xyes"], [ + AS_VAR_APPEND([SAGE_SPKG_OPTIONS], [" -o"]) +]) +AC_SUBST([SAGE_SPKG_OPTIONS]) + +SAGE_SPKG_COLLECT() + # We need sage-env-config to exist before running this. # TODO: fix this in Trac #21524 touch "$SAGE_SRC/bin/sage-env-config" From 5eba95a00f6febd2a4877de342cb57534457fc46 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Fri, 19 Jun 2020 11:33:14 +0200 Subject: [PATCH 039/713] pari without native --- build/pkgs/cypari/spkg-install.in | 2 ++ build/pkgs/cysignals/spkg-install.in | 2 ++ build/pkgs/giac/spkg-install.in | 4 ++-- build/pkgs/gp2c/spkg-install.in | 3 +++ build/pkgs/lcalc/spkg-install.in | 2 ++ build/pkgs/pari/spkg-install.in | 2 +- build/pkgs/pari_jupyter/spkg-install.in | 2 ++ 7 files changed, 14 insertions(+), 3 deletions(-) diff --git a/build/pkgs/cypari/spkg-install.in b/build/pkgs/cypari/spkg-install.in index deba1bb42bb..47b5e3cc60a 100644 --- a/build/pkgs/cypari/spkg-install.in +++ b/build/pkgs/cypari/spkg-install.in @@ -1 +1,3 @@ +export CFLAGS=$CFLAGS_NON_NATIVE + cd src && sdh_pip_install . diff --git a/build/pkgs/cysignals/spkg-install.in b/build/pkgs/cysignals/spkg-install.in index 0894611a27e..72c3ec9211d 100644 --- a/build/pkgs/cysignals/spkg-install.in +++ b/build/pkgs/cysignals/spkg-install.in @@ -1,5 +1,7 @@ cd src +export CFLAGS=$CFLAGS_NON_NATIVE + # #29473: Override -Wp,-D_FORTIFY_SOURCE from Fedora's python3. export CPPFLAGS="$CPPFLAGS -Wp,-U_FORTIFY_SOURCE" diff --git a/build/pkgs/giac/spkg-install.in b/build/pkgs/giac/spkg-install.in index d6a931b3b10..30d570bde97 100644 --- a/build/pkgs/giac/spkg-install.in +++ b/build/pkgs/giac/spkg-install.in @@ -8,8 +8,8 @@ ############################################################# # If CFLAGS and CXXFLAGS are unset, giac looks to set -g -O2, # but if they are not empty, the -g -O2 is not added -CFLAGS="-g -O2 $CFLAGS" -CXXFLAGS="-g -O2 $CXXFLAGS" +CFLAGS=$CFLAGS_NON_NATIVE +CXXFLAGS=$CXXFLAGS_NON_NATIVE CPPFLAGS="-I$SAGE_LOCAL/include $CPPFLAGS" if [ `uname -m` = "ppc64" ]; then diff --git a/build/pkgs/gp2c/spkg-install.in b/build/pkgs/gp2c/spkg-install.in index 85b4e2d320a..c3608e87e3f 100644 --- a/build/pkgs/gp2c/spkg-install.in +++ b/build/pkgs/gp2c/spkg-install.in @@ -1,4 +1,7 @@ cd src + +export CFLAGS=$CFLAGS_NON_NATIVE + sdh_configure --with-paricfg=$SAGE_PARI_CFG sdh_make sdh_make_install diff --git a/build/pkgs/lcalc/spkg-install.in b/build/pkgs/lcalc/spkg-install.in index 9000051aa58..381b3f53042 100644 --- a/build/pkgs/lcalc/spkg-install.in +++ b/build/pkgs/lcalc/spkg-install.in @@ -4,6 +4,8 @@ if [ "$UNAME" = "Darwin" ]; then export LDFLAGS="$LDFLAGS -Wl,-headerpad_max_install_names" fi +CXXFLAGS=$CXXFLAGS_NON_NATIVE + # Using pari in a C++17 file with "using namespace std doesn't # work due to a conflict between std::rank and pari's rank CXXFLAGS=$(echo "${CXXFLAGS}" | sed "s/-std=c++17//g") diff --git a/build/pkgs/pari/spkg-install.in b/build/pkgs/pari/spkg-install.in index 3134aadb353..6ddea50d89f 100644 --- a/build/pkgs/pari/spkg-install.in +++ b/build/pkgs/pari/spkg-install.in @@ -112,7 +112,7 @@ if [ $MACOSX_VERSION -ge 14 ]; then export MACOSX_DEPLOYMENT_TARGET=10.9 fi -export CFLAGS=$CFLAGS_O3 +export CFLAGS=$CFLAGS_O3_NON_NATIVE if [ "$SAGE_DEBUG" = yes ]; then diff --git a/build/pkgs/pari_jupyter/spkg-install.in b/build/pkgs/pari_jupyter/spkg-install.in index deba1bb42bb..47b5e3cc60a 100644 --- a/build/pkgs/pari_jupyter/spkg-install.in +++ b/build/pkgs/pari_jupyter/spkg-install.in @@ -1 +1,3 @@ +export CFLAGS=$CFLAGS_NON_NATIVE + cd src && sdh_pip_install . From 1102ac17ca79aac13e6bc783fcf0081c94cfb9cd Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Sat, 20 Jun 2020 14:15:08 +0200 Subject: [PATCH 040/713] lcalc for real --- build/pkgs/lcalc/spkg-install.in | 1 + 1 file changed, 1 insertion(+) diff --git a/build/pkgs/lcalc/spkg-install.in b/build/pkgs/lcalc/spkg-install.in index 381b3f53042..e92fd335014 100644 --- a/build/pkgs/lcalc/spkg-install.in +++ b/build/pkgs/lcalc/spkg-install.in @@ -4,6 +4,7 @@ if [ "$UNAME" = "Darwin" ]; then export LDFLAGS="$LDFLAGS -Wl,-headerpad_max_install_names" fi +export CFLAGS=$CFLAGS_NON_NATIVE CXXFLAGS=$CXXFLAGS_NON_NATIVE # Using pari in a C++17 file with "using namespace std doesn't From d412f066fbe964a19b104fe5ec0610501073e3cd Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Fri, 26 Jun 2020 22:49:07 +0200 Subject: [PATCH 041/713] Revert "lcalc for real" This reverts commit 863862679f6a9cba3476b515dbbfe22ed31a6571. --- build/pkgs/lcalc/spkg-install.in | 1 - 1 file changed, 1 deletion(-) diff --git a/build/pkgs/lcalc/spkg-install.in b/build/pkgs/lcalc/spkg-install.in index e92fd335014..381b3f53042 100644 --- a/build/pkgs/lcalc/spkg-install.in +++ b/build/pkgs/lcalc/spkg-install.in @@ -4,7 +4,6 @@ if [ "$UNAME" = "Darwin" ]; then export LDFLAGS="$LDFLAGS -Wl,-headerpad_max_install_names" fi -export CFLAGS=$CFLAGS_NON_NATIVE CXXFLAGS=$CXXFLAGS_NON_NATIVE # Using pari in a C++17 file with "using namespace std doesn't From 453c97b3432f1c0d4760b26a8d2fa1842e3f88cc Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Fri, 26 Jun 2020 22:49:13 +0200 Subject: [PATCH 042/713] Revert "pari without native" This reverts commit 3a43cd163ae4961f3b5f34c3d02570671a409373. --- build/pkgs/cypari/spkg-install.in | 2 -- build/pkgs/cysignals/spkg-install.in | 2 -- build/pkgs/giac/spkg-install.in | 4 ++-- build/pkgs/gp2c/spkg-install.in | 3 --- build/pkgs/lcalc/spkg-install.in | 2 -- build/pkgs/pari/spkg-install.in | 2 +- build/pkgs/pari_jupyter/spkg-install.in | 2 -- 7 files changed, 3 insertions(+), 14 deletions(-) diff --git a/build/pkgs/cypari/spkg-install.in b/build/pkgs/cypari/spkg-install.in index 47b5e3cc60a..deba1bb42bb 100644 --- a/build/pkgs/cypari/spkg-install.in +++ b/build/pkgs/cypari/spkg-install.in @@ -1,3 +1 @@ -export CFLAGS=$CFLAGS_NON_NATIVE - cd src && sdh_pip_install . diff --git a/build/pkgs/cysignals/spkg-install.in b/build/pkgs/cysignals/spkg-install.in index 72c3ec9211d..0894611a27e 100644 --- a/build/pkgs/cysignals/spkg-install.in +++ b/build/pkgs/cysignals/spkg-install.in @@ -1,7 +1,5 @@ cd src -export CFLAGS=$CFLAGS_NON_NATIVE - # #29473: Override -Wp,-D_FORTIFY_SOURCE from Fedora's python3. export CPPFLAGS="$CPPFLAGS -Wp,-U_FORTIFY_SOURCE" diff --git a/build/pkgs/giac/spkg-install.in b/build/pkgs/giac/spkg-install.in index 30d570bde97..d6a931b3b10 100644 --- a/build/pkgs/giac/spkg-install.in +++ b/build/pkgs/giac/spkg-install.in @@ -8,8 +8,8 @@ ############################################################# # If CFLAGS and CXXFLAGS are unset, giac looks to set -g -O2, # but if they are not empty, the -g -O2 is not added -CFLAGS=$CFLAGS_NON_NATIVE -CXXFLAGS=$CXXFLAGS_NON_NATIVE +CFLAGS="-g -O2 $CFLAGS" +CXXFLAGS="-g -O2 $CXXFLAGS" CPPFLAGS="-I$SAGE_LOCAL/include $CPPFLAGS" if [ `uname -m` = "ppc64" ]; then diff --git a/build/pkgs/gp2c/spkg-install.in b/build/pkgs/gp2c/spkg-install.in index c3608e87e3f..85b4e2d320a 100644 --- a/build/pkgs/gp2c/spkg-install.in +++ b/build/pkgs/gp2c/spkg-install.in @@ -1,7 +1,4 @@ cd src - -export CFLAGS=$CFLAGS_NON_NATIVE - sdh_configure --with-paricfg=$SAGE_PARI_CFG sdh_make sdh_make_install diff --git a/build/pkgs/lcalc/spkg-install.in b/build/pkgs/lcalc/spkg-install.in index 381b3f53042..9000051aa58 100644 --- a/build/pkgs/lcalc/spkg-install.in +++ b/build/pkgs/lcalc/spkg-install.in @@ -4,8 +4,6 @@ if [ "$UNAME" = "Darwin" ]; then export LDFLAGS="$LDFLAGS -Wl,-headerpad_max_install_names" fi -CXXFLAGS=$CXXFLAGS_NON_NATIVE - # Using pari in a C++17 file with "using namespace std doesn't # work due to a conflict between std::rank and pari's rank CXXFLAGS=$(echo "${CXXFLAGS}" | sed "s/-std=c++17//g") diff --git a/build/pkgs/pari/spkg-install.in b/build/pkgs/pari/spkg-install.in index 6ddea50d89f..3134aadb353 100644 --- a/build/pkgs/pari/spkg-install.in +++ b/build/pkgs/pari/spkg-install.in @@ -112,7 +112,7 @@ if [ $MACOSX_VERSION -ge 14 ]; then export MACOSX_DEPLOYMENT_TARGET=10.9 fi -export CFLAGS=$CFLAGS_O3_NON_NATIVE +export CFLAGS=$CFLAGS_O3 if [ "$SAGE_DEBUG" = yes ]; then diff --git a/build/pkgs/pari_jupyter/spkg-install.in b/build/pkgs/pari_jupyter/spkg-install.in index 47b5e3cc60a..deba1bb42bb 100644 --- a/build/pkgs/pari_jupyter/spkg-install.in +++ b/build/pkgs/pari_jupyter/spkg-install.in @@ -1,3 +1 @@ -export CFLAGS=$CFLAGS_NON_NATIVE - cd src && sdh_pip_install . From f23e68c4c7cf3de2d09fe7d9c04080911f154652 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Sat, 27 Jun 2020 11:20:51 +0200 Subject: [PATCH 043/713] export SAGE_DEBUG, if this was enabled during configure --- build/bin/sage-build-env-config.in | 3 +++ 1 file changed, 3 insertions(+) diff --git a/build/bin/sage-build-env-config.in b/build/bin/sage-build-env-config.in index 019838a8863..87acb56e29a 100644 --- a/build/bin/sage-build-env-config.in +++ b/build/bin/sage-build-env-config.in @@ -51,6 +51,9 @@ export ORIGINAL_F77FLAGS="@ORIGINAL_F77FLAGS@" # Export SAGE_FAT_BINARY if this was enabled during configure. export SAGE_FAT_BINARY="@SAGE_FAT_BINARY@" +# Export SAGE_DEBUG if this was enabled during configure. +export SAGE_DEBUG="@SAGE_DEBUG@" + # This is usually blank if the system GMP is used, or $SAGE_LOCAL otherwise export SAGE_GMP_PREFIX="@SAGE_GMP_PREFIX@" export SAGE_GMP_INCLUDE="@SAGE_GMP_INCLUDE@" From e9fb1a37af6ac08c29dff5a9cb062eccf60f761a Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Tue, 7 Jul 2020 10:18:33 +0200 Subject: [PATCH 044/713] allow debuggin of individual packages --- build/bin/sage-build-env-config.in | 29 --------- build/make/Makefile.in | 93 +++++++++++++++++++++++++++ build/pkgs/gcc/spkg-configure.m4 | 53 +++++++-------- build/pkgs/gfortran/spkg-configure.m4 | 25 ------- 4 files changed, 116 insertions(+), 84 deletions(-) diff --git a/build/bin/sage-build-env-config.in b/build/bin/sage-build-env-config.in index 87acb56e29a..3773602ba42 100644 --- a/build/bin/sage-build-env-config.in +++ b/build/bin/sage-build-env-config.in @@ -22,38 +22,9 @@ # The configured CXX without special flags added that enable C++11 support export SAGE_CXX_WITHOUT_STD="@SAGE_CXX_WITHOUT_STD@" -# The configured compilation flags -export CFLAGS="@CFLAGS@" -export CXXFLAGS="@CXXFLAGS@" -export FCFLAGS="@FCFLAGS@" -export F77FLAGS="@F77FLAGS@" - -export CFLAGS_O3="@CFLAGS_O3@" -export CXXFLAGS_O3="@CXXFLAGS_O3@" -export FCFLAGS_O3="@FCFLAGS_O3@" -export F77FLAGS_O3="@F77FLAGS_O3@" - -export CFLAGS_NON_NATIVE="@CFLAGS_NON_NATIVE@" -export CXXFLAGS_NON_NATIVE="@CXXFLAGS_NON_NATIVE@" -export FCFLAGS_NON_NATIVE="@FCFLAGS_NON_NATIVE@" -export F77FLAGS_NON_NATIVE="@F77FLAGS_NON_NATIVE@" - -export CFLAGS_O3_NON_NATIVE="@CFLAGS_O3_NON_NATIVE@" -export CXXFLAGS_O3_NON_NATIVE="@CXXFLAGS_O3_NON_NATIVE@" -export FCFLAGS_O3_NON_NATIVE="@FCFLAGS_O3_NON_NATIVE@" -export F77FLAGS_O3_NON_NATIVE="@F77FLAGS_O3_NON_NATIVE@" - -export ORIGINAL_CFLAGS="@ORIGINAL_CFLAGS@" -export ORIGINAL_CXXFLAGS="@ORIGINAL_CXXFLAGS@" -export ORIGINAL_FCFLAGS="@ORIGINAL_FCFLAGS@" -export ORIGINAL_F77FLAGS="@ORIGINAL_F77FLAGS@" - # Export SAGE_FAT_BINARY if this was enabled during configure. export SAGE_FAT_BINARY="@SAGE_FAT_BINARY@" -# Export SAGE_DEBUG if this was enabled during configure. -export SAGE_DEBUG="@SAGE_DEBUG@" - # This is usually blank if the system GMP is used, or $SAGE_LOCAL otherwise export SAGE_GMP_PREFIX="@SAGE_GMP_PREFIX@" export SAGE_GMP_INCLUDE="@SAGE_GMP_INCLUDE@" diff --git a/build/make/Makefile.in b/build/make/Makefile.in index ff3b49948c9..3dd8da3eb8a 100644 --- a/build/make/Makefile.in +++ b/build/make/Makefile.in @@ -26,6 +26,99 @@ SAGE_SPKG_INST=installed endif endif +# Optimization flags. +# We allow debugging of individual packages. + +# Export SAGE_DEBUG if this was enabled during configure, +# but be respectful of the current settings. +SAGE_DEBUG=@SAGE_DEBUG@ +export SAGE_DEBUG + +# The configured compilation flags +# The flags are printed by +# ``build/pkgs/gcc/spkg-configure.m4`` for convenience. +# Any changes to the defaults should be applied there as well. +CFLAGS=@ORIGINAL_CFLAGS@ +CXXFLAGS=@ORIGINAL_CXXFLAGS@ +FCFLAGS=@ORIGINAL_FCFLAGS@ +F77FLAGS=@ORIGINAL_F77FLAGS@ +ORIGINAL_CFLAGS:=$(CFLAGS) +ORIGINAL_CXXFLAGS:=$(CXXFLAGS) +ORIGINAL_FCFLAGS:=$(FCFLAGS) +ORIGINAL_F77FLAGS:=$(F77FLAGS) +export ORIGINAL_CFLAGS, ORIGINAL_CXXFLAGS, ORIGINAL_FCFLAGS, ORIGINAL_F77FLAGS + +SAGE_MARCH=@SAGE_MARCH@ + +ifeq ($(ORIGINAL_CFLAGS), ) +# Evaluate SAGE_DEBUG: +ifeq ($(SAGE_DEBUG), yes) +CFLAGS_NON_NATIVE=-Og -g +CFLAGS_O3_NON_NATIVE=-Og -g +SAGE_MARCH= +else +ifeq ($(SAGE_DEBUG), no) +CFLAGS_NON_NATIVE=-O2 +CFLAGS_O3_NON_NATIVE=-O3 +else +CFLAGS_NON_NATIVE=-O2 -g +CFLAGS_O3_NON_NATIVE=-O3 -g +endif +endif +export CFLAGS=$(CFLAGS_NON_NATIVE) $(SAGE_MARCH) +CFLAGS_O3:=$(CFLAGS_O3_NON_NATIVE) $(SAGE_MARCH) +else +# Respect user environment variable. +CFLAGS=$(ORIGINAL_CFLAGS) +CFLAGS_O3=$(ORIGINAL_CFLAGS) +CFLAGS_NON_NATIVE=$(ORIGINAL_CFLAGS) +CFLAGS_O3_NON_NATIVE=$(ORIGINAL_CFLAGS) +endif +export CFLAGS_NON_NATIVE, CFLAGS_O3_NON_NATIVE, CFLAGS_O3, CFLAGS + +# Copy to CXXFLAGS if this is not set. +ifeq ($(ORIGINAL_CXXFLAGS), ) +CXXFLAGS=$(CFLAGS) +CXXFLAGS_O3=$(CFLAGS_O3) +CXXFLAGS_NON_NATIVE=$(CFLAGS_NON_NATIVE) +CXXFLAGS_O3_NON_NATIVE=$(CFLAGS_O3_NON_NATIVE) +else +CXXFLAGS=$(ORIGINAL_CXXFLAGS) +CXXFLAGS_O3=$(ORIGINAL_CXXFLAGS) +CXXFLAGS_NON_NATIVE=$(ORIGINAL_CXXFLAGS) +CXXFLAGS_O3_NON_NATIVE=$(ORIGINAL_CXXFLAGS) +endif +export CXXFLAGS_NON_NATIVE, CXXFLAGS_O3_NON_NATIVE, CXXFLAGS_O3, CXXFLAGS + +# Copy CFLAGS to FCFLAGS if this is not set. +ifeq ($(ORIGINAL_FCFLAGS), ) +FCFLAGS=$(CFLAGS) +FCFLAGS_O3=$(CFLAGS_O3) +FCFLAGS_NON_NATIVE=$(CFLAGS_NON_NATIVE) +FCFLAGS_O3_NON_NATIVE=$(CFLAGS_O3_NON_NATIVE) +else +FCFLAGS=$(ORIGINAL_FCFLAGS) +FCFLAGS_O3=$(ORIGINAL_FCFLAGS) +FCFLAGS_NON_NATIVE=$(ORIGINAL_FCFLAGS) +FCFLAGS_O3_NON_NATIVE=$(ORIGINAL_FCFLAGS) +endif +export FCFLAGS_NON_NATIVE, FCFLAGS_O3_NON_NATIVE, FCFLAGS_O3, FCFLAGS + +# Copy FCFLAGS to F77FLAGS if this is not set. +ifeq ($(ORIGINAL_F77FLAGS), ) +F77FLAGS=$(FCFLAGS) +F77FLAGS_O3=$(FCFLAGS_O3) +F77FLAGS_NON_NATIVE=$(FCFLAGS_NON_NATIVE) +F77FLAGS_O3_NON_NATIVE=$(FCFLAGS_O3_NON_NATIVE) +else +F77FLAGS=$(ORIGINAL_F77FLAGS) +F77FLAGS_O3=$(ORIGINAL_F77FLAGS) +F77FLAGS_NON_NATIVE=$(ORIGINAL_F77FLAGS) +F77FLAGS_O3_NON_NATIVE=$(ORIGINAL_F77FLAGS) +endif +export F77FLAGS_NON_NATIVE, F77FLAGS_O3_NON_NATIVE, F77FLAGS_O3, F77FLAGS + + # Directory to keep track of which packages are installed INST = $(SAGE_SPKG_INST) diff --git a/build/pkgs/gcc/spkg-configure.m4 b/build/pkgs/gcc/spkg-configure.m4 index 9a2882e7e9e..d84753a7004 100644 --- a/build/pkgs/gcc/spkg-configure.m4 +++ b/build/pkgs/gcc/spkg-configure.m4 @@ -208,39 +208,44 @@ SAGE_SPKG_CONFIGURE_BASE([gcc], [ # Determine which compiler flags should be set. if test "x$sage_use_march_native" = "xno"; then - march="" + SAGE_MARCH="" elif test "x$SAGE_FAT_BINARY" = "xyes"; then - march="" - elif test "x$SAGE_DEBUG" = "xyes"; then - march="" + SAGE_MARCH="" elif test "x$sage_spkg_install_gcc" =" xyes"; then - march=" -march=native" + SAGE_MARCH=" -march=native" else - AX_CHECK_COMPILE_FLAG("-march=native", [march=" -march=native"], [march=""], [], []) + AX_CHECK_COMPILE_FLAG("-march=native", [SAGE_MARCH=" -march=native"], [SAGE_MARCH=""], [], []) fi + AC_SUBST(SAGE_MARCH) + # We will only export SAGE_MARCH for now and redo this later. + # (Allow debugging of individual packages.) + # But we print the messages for convenience. + if test "x$SAGE_DEBUG" = "xyes"; then + SAGE_MARCH="" + fi if test "x$ORIGINAL_CFLAGS" = "x"; then # Evaluate SAGE_DEBUG: if test "x$SAGE_DEBUG" = "xyes" ; then - AC_SUBST(CFLAGS_NON_NATIVE, "-Og -g") - AC_SUBST(CFLAGS_O3_NON_NATIVE, "-Og -g") + CFLAGS_NON_NATIVE="-Og -g" + CFLAGS_O3_NON_NATIVE="-Og -g" else if test "x$SAGE_DEBUG" = "xno" ; then - AC_SUBST(CFLAGS_NON_NATIVE, "-O2") - AC_SUBST(CFLAGS_O3_NON_NATIVE, "-O3") + CFLAGS_NON_NATIVE="-O2" + CFLAGS_O3_NON_NATIVE="-O3" else - AC_SUBST(CFLAGS_NON_NATIVE, "-O2 -g") - AC_SUBST(CFLAGS_O3_NON_NATIVE, "-O3 -g") + CFLAGS_NON_NATIVE="-O2 -g" + CFLAGS_O3_NON_NATIVE="-O3 -g" fi fi - AC_SUBST(CFLAGS, "${CFLAGS_NON_NATIVE}${march}") - AC_SUBST(CFLAGS_O3, "${CFLAGS_O3_NON_NATIVE}${march}") + CFLAGS="${CFLAGS_NON_NATIVE}${SAGE_MARCH} + CFLAGS_O3="${CFLAGS_O3_NON_NATIVE}${SAGE_MARCH} else # Respect user environment variable. - AC_SUBST(CFLAGS, "${CFLAGS}") - AC_SUBST(CFLAGS_O3, "${CFLAGS}") - AC_SUBST(CFLAGS_NON_NATIVE, "${CFLAGS}") - AC_SUBST(CFLAGS_O3_NON_NATIVE, "${CFLAGS}") + CFLAGS="${CFLAGS} + CFLAGS_O3="${CFLAGS} + CFLAGS_NON_NATIVE="${CFLAGS} + CFLAGS_O3_NON_NATIVE="${CFLAGS} fi AC_MSG_NOTICE(ORIGINAL_CFLAGS=$ORIGINAL_CFLAGS) @@ -249,18 +254,6 @@ SAGE_SPKG_CONFIGURE_BASE([gcc], [ AC_MSG_NOTICE(CFLAGS_NON_NATIVE=$CFLAGS_NON_NATIVE) AC_MSG_NOTICE(CFLAGS_O3_NON_NATIVE=$CFLAGS_O3_NON_NATIVE) - # Copy to CXXFLAGS if this is not set. - if test "x$ORIGINAL_CXXFLAGS" = "x"; then - AC_SUBST(CXXFLAGS, "$CFLAGS") - AC_SUBST(CXXFLAGS_O3, "$CFLAGS_O3") - AC_SUBST(CXXFLAGS_NON_NATIVE, "$CFLAGS_NON_NATIVE") - AC_SUBST(CXXFLAGS_O3_NON_NATIVE, "$CFLAGS_O3_NON_NATIVE") - else - AC_SUBST(CXXFLAGS_03, "$ORIGINAL_CXXFLAGS") - AC_SUBST(CXXFLAGS_NON_NATIVE, "$ORIGINAL_CXXFLAGS") - AC_SUBST(CXXFLAGS_O3_NON_NATIVE, "$ORIGINAL_CXXFLAGS") - fi - ], , , [ # Trac #27907: Find location of crti.o from the system CC, in case we build our own gcc AC_MSG_CHECKING([for the location of crti.o]) diff --git a/build/pkgs/gfortran/spkg-configure.m4 b/build/pkgs/gfortran/spkg-configure.m4 index 0bd2aa0f0f1..d75bbca9be5 100644 --- a/build/pkgs/gfortran/spkg-configure.m4 +++ b/build/pkgs/gfortran/spkg-configure.m4 @@ -21,29 +21,4 @@ SAGE_SPKG_CONFIGURE([gfortran], [ if test "x$sage_spkg_install_gcc" = "xyes" -o x$SAGE_INSTALL_GCC = xexists; then sage_spkg_install_gfortran=no fi - - # Copy CFLAGS to FCFLAGS if this is not set. - if test "x$ORIGINAL_FCFLAGS" = "x"; then - AC_SUBST(FCFLAGS, "$CFLAGS") - AC_SUBST(FCFLAGS_O3, "$CFLAGS_O3") - AC_SUBST(FCFLAGS_NON_NATIVE, "$CFLAGS_NON_NATIVE") - AC_SUBST(FCFLAGS_O3_NON_NATIVE, "$CFLAGS_O3_NON_NATIVE") - else - AC_SUBST(FCFLAGS_03, "$ORIGINAL_FCFLAGS") - AC_SUBST(FCFLAGS_NON_NATIVE, "$ORIGINAL_FCFLAGS") - AC_SUBST(FCFLAGS_O3_NON_NATIVE, "$ORIGINAL_FCFLAGS") - fi - - # Copy FCFLAGS to F77FLAGS if this is not set. - if test "x$ORIGINAL_F77FLAGS" = "x"; then - AC_SUBST(F77FLAGS, "$FCFLAGS") - AC_SUBST(F77FLAGS_O3, "$FCFLAGS_O3") - AC_SUBST(F77FLAGS_NON_NATIVE, "$FCFLAGS_NON_NATIVE") - AC_SUBST(F77FLAGS_O3_NON_NATIVE, "$FCFLAGS_O3_NON_NATIVE") - else - AC_SUBST(F77FLAGS_03, "$ORIGINAL_F77FLAGS") - AC_SUBST(F77FLAGS_NON_NATIVE, "$ORIGINAL_F77FLAGS") - AC_SUBST(F77FLAGS_O3_NON_NATIVE, "$ORIGINAL_F77FLAGS") - fi - ]) From abc9c6ff5af2f770155a9aada3c1100a8fe01552 Mon Sep 17 00:00:00 2001 From: Marc Newman Date: Fri, 10 Jul 2020 17:19:05 +0200 Subject: [PATCH 045/713] Separate equation string construction from _repr_ method --- .../schemes/elliptic_curves/ell_generic.py | 77 +++++++++++-------- 1 file changed, 45 insertions(+), 32 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/ell_generic.py b/src/sage/schemes/elliptic_curves/ell_generic.py index 72d3796bd0d..abbe269c6e4 100644 --- a/src/sage/schemes/elliptic_curves/ell_generic.py +++ b/src/sage/schemes/elliptic_curves/ell_generic.py @@ -179,60 +179,73 @@ def _defining_params_(self): """ return (self.__base_ring, list(self.__ainvs)) - def _repr_(self): + def _equation_string(self): """ - String representation of elliptic curve. + String representation of the equation of elliptic curve. EXAMPLES:: - sage: E = EllipticCurve([1,2,3,4,5]); E._repr_() - 'Elliptic Curve defined by y^2 + x*y + 3*y = x^3 + 2*x^2 + 4*x + 5 over Rational Field' - - :: - - sage: R. = QQ['x'] - sage: K. = NumberField(x^3-17) - sage: EllipticCurve([a^2-3, -2/3*a + 3]) - Elliptic Curve defined by y^2 = x^3 + (a^2-3)*x + (-2/3*a+3) over Number Field in a - with defining polynomial x^3 - 17 + sage: E = EllipticCurve([1,2,3,4,5]); E._equation_string() + 'y^2 + x*y + 3*y = x^3 + 2*x^2 + 4*x + 5' """ b = self.ainvs() a = [z._coeff_repr() for z in b] - s = "Elliptic Curve defined by " - s += "y^2 " + s = "y^2" if a[0] == "-1": - s += "- x*y " + s += " - x*y" elif a[0] == '1': - s += "+ x*y " + s += " + x*y" elif b[0]: - s += "+ %s*x*y " % a[0] + s += " + %s*x*y" % a[0] if a[2] == "-1": - s += "- y " + s += " - y" elif a[2] == '1': - s += "+ y " + s += " + y" elif b[2]: - s += "+ %s*y " % a[2] - s += "= x^3 " + s += " + %s*y" % a[2] + s += " = x^3" if a[1] == "-1": - s += "- x^2 " + s += " - x^2" elif a[1] == '1': - s += "+ x^2 " + s += " + x^2" elif b[1]: - s += "+ %s*x^2 " % a[1] + s += " + %s*x^2" % a[1] if a[3] == "-1": - s += "- x " + s += " - x" elif a[3] == '1': - s += "+ x " + s += " + x" elif b[3]: - s += "+ %s*x " % a[3] + s += " + %s*x" % a[3] if a[4] == '-1': - s += "- 1 " + s += " - 1" elif a[4] == '1': - s += "+ 1 " + s += " + 1" elif b[4]: - s += "+ %s " % a[4] - s = s.replace("+ -","- ") - s += "over %s" % self.base_ring() + s += " + %s" % a[4] + return s.replace("+ -","- ") + + def _repr_(self): + """ + String representation of elliptic curve. + + EXAMPLES:: + + sage: E = EllipticCurve([1,2,3,4,5]); E._repr_() + 'Elliptic Curve defined by y^2 + x*y + 3*y = x^3 + 2*x^2 + 4*x + 5 over Rational Field' + + :: + + sage: R. = QQ['x'] + sage: K. = NumberField(x^3-17) + sage: EllipticCurve([a^2-3, -2/3*a + 3]) + Elliptic Curve defined by y^2 = x^3 + (a^2-3)*x + (-2/3*a+3) over Number Field in a + with defining polynomial x^3 - 17 + """ + b = self.ainvs() + a = [z._coeff_repr() for z in b] + s = "Elliptic Curve defined by " + s += self._equation_string() + s += " over %s" % self.base_ring() return s def _latex_(self): From 72d8cf3136c47305b466a0a8b620ec10811044af Mon Sep 17 00:00:00 2001 From: Marc Newman Date: Fri, 10 Jul 2020 17:19:41 +0200 Subject: [PATCH 046/713] Add prime_isogeny_graph method --- src/sage/schemes/elliptic_curves/ell_field.py | 202 ++++++++++++++++++ 1 file changed, 202 insertions(+) diff --git a/src/sage/schemes/elliptic_curves/ell_field.py b/src/sage/schemes/elliptic_curves/ell_field.py index f2c4a4cd544..a2eb64286d2 100644 --- a/src/sage/schemes/elliptic_curves/ell_field.py +++ b/src/sage/schemes/elliptic_curves/ell_field.py @@ -1253,3 +1253,205 @@ def hasse_invariant(self): E = self.short_weierstrass_model() f=(x**3+E.a4()*x+E.a6())**((p-1)//2) return f.coefficients(sparse=False)[p-1] + + def prime_isogeny_graph(self, l, directed=True, label_by_j=False): + """ + Return a graph representing the ``l``-degree ``K``-isogenies between + ``K``-isomorphism classes of elliptic curves for ``K = + self.base_field()``. + + INPUT: + + - ``l`` -- prime degree of isogenies. + + - ``directed`` (default True) -- return a directed graph if ``True``; + otherwise return an undirected graph. In the undirected case, the + in-degrees and out-degrees of the vertices must be balanced and + therefore the number of out-edges from the vertices corresponding to + j-invariants 0 and 1728 (if they are part of the graph) are reduced + to match the number of in-edges. + + - ``label_by_j`` (default False) -- if ``True`` label graph vertices by + the j-invariant corresponding to the isomorphism class of curves. If + the j-invariant is not unique in the isogeny class, append ``*`` to + it to indicate a twist. Otherwise, if ``False`` label vertices by the + equation of a representative curve. + + OUTPUT: + + A ``Graph`` or ``Digraph`` + + EXAMPLES: + + Ordinary curve over finite extension field of degree 2 + + sage: E = EllipticCurve(GF(59^2, "i", x^2 + 1), j=5) + sage: G = E.prime_isogeny_graph(5, directed=False, label_by_j=True) + sage: G + Graph on 20 vertices + sage: G.vertices() + ['1', + '12', + ... + 'i + 55'] + sage: G.edges() + [('1', '28*i + 11', None), + ('1', '31*i + 11', None), + ... + ('8', 'i + 1', None)] + + Supersingular curve over prime field + + sage: E = EllipticCurve(GF(419), j=1728) + sage: G3 = E.prime_isogeny_graph(3, directed=False, label_by_j=True) + sage: G3 + Graph on 27 vertices + sage: G3.vertices() + ['0', + '0*', + ... + '98*'] + sage: G3.edges() + [('0', '0*', None), + ('0', '13', None), + ... + ('48*', '98*', None)] + sage: G5 = E.prime_isogeny_graph(5, directed=False, label_by_j=True) + sage: G5 + Graph on 9 vertices + sage: G5.vertices() + ['13', '13*', '407', '407*', '52', '62', '62*', '98', '98*'] + sage: G5.edges() + [('13', '52', None), + ('13', '98', None), + ... + ('62*', '98*', None)] + + Supersingular curve over finite extension field of degree 2 + + sage: K = GF(431^2, "i", x^2 + 1) + sage: E = EllipticCurve(K, j=0) + sage: E.is_supersingular() + True + sage: G = E.prime_isogeny_graph(2, directed=True, label_by_j=True) + sage: G + Looped multi-digraph on 37 vertices + sage: G.vertices() + ['0', + '102', + ... + '87*i + 190'] + sage: G.edges() + [('0', '125', None), + ('0', '125', None), + ... + '81*i + 65', None)] + sage: H = E.prime_isogeny_graph(2, directed=False, label_by_j=True) + sage: H + Looped multi-graph on 37 vertices + sage: H.vertices() + ['0', + '102', + ... + '87*i + 190'] + sage: H.edges() + [('0', '125', None), + ('102', '125', None), + ... + ('81*i + 65', '87*i + 190', None)] + + Curve over a quadratic number field + + sage: K. = NumberField(x^2 - 2) + sage: E = EllipticCurve(K, [1,0,1,4, -6]) + sage: G2 = E.prime_isogeny_graph(2, directed=False) + sage: G2.vertices() + ['y^2 + x*y + y = x^3 + (-130*e-356)*x + (-2000*e-2038)', + 'y^2 + x*y + y = x^3 + (-36)*x + (-70)', + 'y^2 + x*y + y = x^3 + (130*e-356)*x + (2000*e-2038)', + 'y^2 + x*y + y = x^3 + 4*x + (-6)'] + sage: G2.edges() + [('y^2 + x*y + y = x^3 + (-130*e-356)*x + (-2000*e-2038)', + 'y^2 + x*y + y = x^3 + (-36)*x + (-70)', None), + ('y^2 + x*y + y = x^3 + (-36)*x + (-70)', + 'y^2 + x*y + y = x^3 + (130*e-356)*x + (2000*e-2038)', None), + ('y^2 + x*y + y = x^3 + (-36)*x + (-70)', + 'y^2 + x*y + y = x^3 + 4*x + (-6)', None)] + sage: G3 = E.prime_isogeny_graph(3, directed=False) + sage: G3.vertices() + ['y^2 + x*y + y = x^3 + (-1)*x', + 'y^2 + x*y + y = x^3 + (-171)*x + (-874)', + 'y^2 + x*y + y = x^3 + 4*x + (-6)'] + sage: G3.edges() + [('y^2 + x*y + y = x^3 + (-1)*x', + 'y^2 + x*y + y = x^3 + 4*x + (-6)', None), + ('y^2 + x*y + y = x^3 + (-171)*x + (-874)', + 'y^2 + x*y + y = x^3 + 4*x + (-6)', None)] + + TESTS: + + sage: E = EllipticCurve(GF(11), j=0) + sage: G0 = E.prime_isogeny_graph(2, directed=False) + sage: G0.is_directed() + False + sage: G1 = E.prime_isogeny_graph(2, directed=True) + sage: G1.is_directed() + True + sage: G2 = E.prime_isogeny_graph(2, label_by_j=False) + sage: G2.vertices() + ['y^2 = x^3 + 1', + 'y^2 = x^3 + 2', + 'y^2 = x^3 + 5*x', + 'y^2 = x^3 + 7*x'] + sage: G3 = E.prime_isogeny_graph(2, label_by_j=True) + sage: G3.vertices() + ['0', '0*', '1', '1*'] + + """ + + from warnings import warn + from sage.graphs.graph import DiGraph, Graph + from sage.matrix.all import Matrix + + curve_max = 1000 # warn users if things are getting big + + Es = [self] # list of curves in graph + A = [] # adjacency matrix + labels = [] # list of vertex labels + for (i, E) in enumerate(Es): + if 0 < curve_max and curve_max < len(Es): + warn('More than ' + str(curve_max) + ' curves.') + curve_max = 0 + + r = [0] * len(Es) # adjacency matrix row + for C in [I.codomain() for I in E.isogenies_prime_degree(l)]: + j = next((k for (k, F) in enumerate(Es) if C.is_isomorphic(F)), + -1) # index of curve isomorphic to codomain of isogeny + if j >= 0: + r[j] += 1 + else: + Es.append(C) + r.append(1) + + # If the graph is undirected, non-symmetric values in the adjacency + # matrix will result in Sage outputting different graphs depending + # on the vertex ordering. Therefore, scale down the non-loop + # out-edges of vertices corresponding to j-invariants 0 and 1728 so + # that the same isogeny graphs output are isomorphic as graphs + # regardless of the starting vertex. + if not directed and E.j_invariant() in [0, 1728]: + m = len(E.automorphisms()) / 2 #multiplicity of out-edges + r = [v if k == i else v / m for (k, v) in enumerate(r)] + + A.append(r) + if label_by_j: + s = str(E.j_invariant()) + while s in labels: s += "*" + labels.append(s) + else: + labels.append(E._equation_string()) + + A = Matrix([r + [0] * (len(A) - len(r)) for r in A]) + G = (DiGraph if directed else Graph)(A, format="adjacency_matrix") + G.relabel(labels) + return G From fb83a38dfec5b3c7ed34a0e663e82264ce899e55 Mon Sep 17 00:00:00 2001 From: Ivo Maffei Date: Wed, 12 Aug 2020 11:39:41 +0200 Subject: [PATCH 047/713] added generalised polygons and related private functions --- .../graphs/generators/distance_regular.pyx | 483 ++++++++++++++++++ src/sage/graphs/graph_generators.py | 6 + 2 files changed, 489 insertions(+) diff --git a/src/sage/graphs/generators/distance_regular.pyx b/src/sage/graphs/generators/distance_regular.pyx index 6906c387fba..d0e2b6b7614 100644 --- a/src/sage/graphs/generators/distance_regular.pyx +++ b/src/sage/graphs/generators/distance_regular.pyx @@ -1194,3 +1194,486 @@ def graph_from_GQ_spread(const int s, const int t): G = Graph(edges, format="list_of_edges") return G + +def GeneralisedDodecagonGraph(const int s, const int t): + r""" + Return the point-graph of a generalised dodecagon of order `(s,t)`. + + INPUT: + + - ``s, t`` -- integers; order of the generalised dodecagon + + EXAMPLES:: + + sage: G = graphs.GeneralisedDodecagonGraph(1, 5) # optional - gap_packages internet + sage: G.is_distance_regular(True) # optional - gap_packages internet + ([6, 5, 5, 5, 5, 5, None], [None, 1, 1, 1, 1, 1, 6]) + sage: H = graphs.GeneralisedDodecagonGraph(5, 1) # optional - gap_packages internet + sage: H.order() # optional - gap_packages internet + 23436 + sage: H.is_distance_regular(True) # long time (6 min); optional - gap_packages internet + ([10, 5, 5, 5, 5, 5, None], [None, 1, 1, 1, 1, 1, 2]) + + .. NOTE:: + + This function indirectly uses the GAP's AtlasRep package. + Thus you may need an internet connection and the optional Sage's + package ``gap_packages``. + + REFERENCES: + + See [BCN1989]_ pp. 200-205 for a discussion of distance-regular graphs from + generalised polygons. + + TESTS: + + Test all graphs of order `(1, q)`:: + + sage: G = graphs.GeneralisedDodecagonGraph(1, 4) # optional - gap_packages internet + sage: G.is_distance_regular(True) # optional - gap_packages internet + ([5, 4, 4, 4, 4, 4, None], [None, 1, 1, 1, 1, 1, 5]) + sage: G = graphs.GeneralisedDodecagonGraph(1, 3) # optional - gap_packages internet + sage: G.is_distance_regular(True) # optional - gap_packages internet + ([4, 3, 3, 3, 3, 3, None], [None, 1, 1, 1, 1, 1, 4]) + sage: G = graphs.GeneralisedDodecagonGraph(1, 2) # optional - gap_packages internet + sage: G.is_distance_regular(True) # optional - gap_packages internet + (3, 2, 2, 2, 2, 2, None], [None, 1, 1, 1, 1, 1, 3]) + sage: G = graphs.GeneralisedDodecagonGraph(1, 1) # optional - gap_packages internet + sage: G.is_distance_regular(True) # optional - gap_packages internet + (2, 1, 1, 1, 1, 1, None], [None, 1, 1, 1, 1, 1, 2]) + + Now test all graphs of order `(q, 1)`:: + + sage: G = graphs.GeneralisedDodecagonGraph(4, 1) # optional - gap_packages internet + sage: G.is_distance_regular(True) # optional - gap_packages internet + ([8, 4, 4, 4, 4, 4, None], [None, 1, 1, 1, 1, 1, 2]) + sage: G = graphs.GeneralisedDodecagonGraph(3, 1) # optional - gap_packages internet + sage: G.is_distance_regular(True) # optional - gap_packages internet + ([6, 3, 3, 3, 3, 3, None], [None, 1, 1, 1, 1, 1, 2]) + sage: G = graphs.GeneralisedDodecagonGraph(3, 1) # optional - gap_packages internet + sage: G.is_distance_regular(True) # optional - gap_packages internet + ([4, 2, 2, 2, 2, 2, None], [None, 1, 1, 1, 1, 1, 2]) + + """ + from sage.arith.misc import is_prime_power + + cdef int q = 0 + cdef int orderType = 0 + + # decide the type of graph + if s == 1: # (1, q) + q = t + elif t == 1: # (q, 1) + q = s + orderType = 1 + else: + raise ValueError(f"No generalised dodacagon of order ({s}, {t}) exists") + + if q == 1: # order (1, 1) + from sage.graphs.generators.basic import CycleGraph + return CycleGraph(12) + + if not is_prime_power(q): + raise ValueError(f"No generalised dodacagon of order ({s}, {t}) exists") + + if orderType == 0: + # incidence graph of hexagon (q,q) + H = GeneralisedHexagonGraph(q, q) + lines = _extract_lines(H) + + edges = [] + for l in lines: + for p in l: + sig_check() + edges.append((p, l)) + + G = Graph(edges, format='list_of_edges') + G.name("Generalised dodecagon of order (1, %d)"%q) + return G + + else: # orderType == 1 + # dual + H = GeneralisedDodecagonGraph(t, s) + G = _line_graph_generalised_polygon(H) + G.name("Generalised dodecagon of order (%s, %d)"%(s, t)) + return G + +def GeneralisedOctagonGraph(const int s, const int t): + r""" + Return the point-graph of a generalised octagon of order `(s,t)`. + + INPUT: + + - ``s, t`` -- integers; order of the generalised octagon + + EXAMPLES:: + + sage: G = graphs.GeneralisedOctagonGraph(1, 4) + sage: G.is_distance_regular(True) + ([5, 4, 4, 4, None], [None, 1, 1, 1, 5]) + sage: G = graphs.GeneralisedOctagonGraph(2, 4) # optional - gap_packages internet + sage: G.is_distance_regular(True) # optional - gap_packages internet + ([10, 8, 8, 8, None], [None, 1, 1, 1, 5]) + sage: G = graphs.GeneralisedOctagonGraph(5, 1) + sage: G.is_distance_regular(True) + ([10, 5, 5, 5, None], [None, 1, 1, 1, 2]) + + .. NOTE:: + + This function uses the GAP's AtlasRep package to build the graphs + of order `(2, 4)` or `(4, 2)`. For those graphs you need an internet + connection and Sage's optional package ``gap_packages``. + + REFERENCES: + + See [BCN1989]_ pp. 200-205 for a discussion of distance-regular graphs from + generalised polygons. + + TESTS:: + + sage: G = graphs.GeneralisedOctagonGraph(8, 16) + Traceback (most recent call last): + ... + NotImplementedError: Graph would be too big + sage: G = graphs.GeneralisedOctagonGraph(4, 16) + Traceback (most recent call last): + ... + ValueError: generalised octagons of order (q, q^2) exist only for q odd powers of 2 + + """ + from sage.arith.misc import is_prime_power + from sage.libs.gap.libgap import libgap + from sage.graphs.strongly_regular_db import strongly_regular_graph + + cdef int q = 0 + cdef int orderType = 0 + + if s == 1: # (1, q) + q = t + elif t == 1: # (q, 1) + q = s + orderType = 1 + elif s**2 == t: # (q, q^2) + q = s + (p, k) = is_prime_power(q, get_data=True) + + if p != 2 or k % 2 != 1: + raise ValueError(("generalised octagons of order (q, q^2) " + "exist only for q odd powers of 2")) + orderType = 2 + elif t**2 == s: # (q^2, q) + q = t + orderType = 1 + else: + raise ValueError(f"No generalised octagon of order ({s}, {t}) exists") + + if q == 1: # order (1, 1) + from sage.graphs.generators.basic import CycleGraph + return CycleGraph(8) + + if not is_prime_power(q): + raise ValueError(f"No generalised octagon of order ({s}, {t}) exists") + + if orderType == 0: + # incidence graph of generalised quadrangle (q, q) + + H = strongly_regular_graph((q+1) * (q*q + 1), q * (q+1), q - 1, q + 1, + check=False) + + lines = _extract_lines(H) + + edges = [] + for l in lines: + for p in l: + sig_check() + edges.append((p, l)) + + G = Graph(edges, format='list_of_edges') + G.name("Generalised octagon of order (1, %d)"%q) + return G + + elif orderType == 1: + # dual + H = GeneralisedOctagonGraph(t,s) + G = _line_graph_generalised_polygon(H) + G.name("Generalised octagon of order(%d, %d)"%(s, t)) + return G + else: + if q == 2: + group = libgap.AtlasGroup("2F4(2)", libgap.NrMovedPoints, 1755) + G = Graph(libgap.Orbit(group, [1, 73], libgap.OnSets), + format='list_of_edges') + G.name("Generalised octagon of order (2, 4)") + return G + else: + raise NotImplementedError("Graph would be too big") + + +def GeneralisedHexagonGraph(const int s, const int t): + r""" + Return the point-graph of a generalised octagon of order `(s,t)`. + + INPUT: + + - ``s, t`` -- integers; order of the generalised octagon + + EXAMPLES:: + + sage: G = graphs.GeneralisedHexagonGraph(5, 5) # optional - gap_packages internet + sage: G.is_distance_regular(True) # optional - gap_packages internet + ([30, 25, 25, None], [None, 1, 1, 6]) + sage: G = graphs.GeneralisedHexagonGraph(7, 1) + sage: G.is_distance_regular(True) + ([14, 7, 7, None], [None, 1, 1, 2]) + sage: graphs.GeneralisedHexagonGraph(1, 1) + Cycle graph: Graph on 6 vertices + + .. NOTE:: + + This function uses the GAP's AtlasRep package to build the graphs + of order `(q, q)`, `(q, q^3)` or `(q^3, q)`. For those graphs you need + an internet connection and Sage's optional package ``gap_packages``. + + REFERENCES: + + See [BCN1989]_ pp. 200-205 for a discussion of distance-regular graphs from + generalised polygons. + + TESTS:: + + sage: G = graphs.GeneralisedHexagonGraph(4, 4) # optional - gap_packages internet + sage: G.is_distance_regular(True) # optional - gap_packages internet + ([20, 16, 16, None], [None, 1, 1, 5]) + sage: G = graphs.GeneralisedHexagonGraph(3, 3) # optional - gap_packages internet + sage: G.is_distance_regular(True) # optional - gap_packages internet + ([12, 9, 9, None], [None, 1, 1, 4]) + sage: G = graphs.GeneralisedHexagonGraph(2, 2) # optional - gap_packages internet + sage: G.is_distance_regular(True) # optional - gap_packages internet + ([6, 4, 4, None], [None, 1, 1, 3]) + sage: G = graphs.GeneralisedHexagonGraph(2, 8) # optional - gap_packages internet + sage: G.is_distance_regular(True) # optional - gap_packages internet + ([18, 16, 16, None], [None, 1, 1, 9]) + + """ + from sage.arith.misc import is_prime_power + from sage.libs.gap.libgap import libgap + from sage.combinat.designs import design_catalog as designs + + cdef int q = 0 + cdef int orderType = 0 + + if s == 1: # (1, q) + q = t + elif t == 1: # (q, 1) + q = s + orderType = 1 + elif s == t: # (q, q) + q = s + orderType = 2 + elif s**3 == t: # (q, q^3) + q = s + orderType = 3 + elif t**3 == s: # (q^3, q) + q = t + orderType = 1 + else: + raise ValueError(f"No generalised octagon of order ({s}, {t}) exists") + + if q == 1: # order (1, 1) + from sage.graphs.generators.basic import CycleGraph + return CycleGraph(6) + + if not is_prime_power(q): + raise ValueError(f"No generalised octagon of order ({s}, {t}) exists") + + if orderType == 0: + # incident graph of generalised 3-gon of order (q, q) + PG2 = designs.ProjectiveGeometryDesign(2,1,q) + + edges = [] + for l in PG2.blocks(): + for p in l: + sig_check() + edges.append((p, tuple(l))) + + G = Graph(edges, format='list_of_edges') + G.name("Generalised hexagon of order (1, %d)"%q) + return G + + elif orderType == 1: + # dual graph + H = GeneralisedHexagonGraph(t, s) + G = _line_graph_generalised_polygon(H) + G.name("Generalised hexagon of order(%d, %d)"%(s, t)) + return G + + elif orderType == 2: + # we use the group G2(q) + # if q == 2, then G2(2) is isomorphic to U3(3).2 + if q == 2: + group = libgap.AtlasGroup("U3(3).2", libgap.NrMovedPoints, 63) + G = Graph(libgap.Orbit(group, [1, 19], libgap.OnSets), + format='list_of_edges') + G.name("Generalised hexagon of order (%d, %d)"%(q, q)) + return G + + elif q == 3: # we don't have permutation representation; so we build it + matrixRep = libgap.AtlasGroup("G2(3)", libgap.Position, 7) + e1 = vector(GF(3), [1, 0, 0, 0, 0, 0, 0]) + orb = libgap.Orbit(matrixRep, e1, libgap.OnLines) + group = libgap.Action(matrixRep, orb, libgap.OnLines) + + # now group is our permutation representation + G = Graph(libgap.Orbit(group, [1, 52], libgap.OnSets), + format='list_of_edges') + G.name("Generealised hexagon of order (%d, %d)"%(q, q)) + return G + + elif q <= 5: + n = 1365 if q == 4 else 3906 + p = 43 if q == 4 else 185 + group = libgap.AtlasGroup("G2(%d)"%q, libgap.NrMovedPoints, n) + + G = Graph(libgap.Orbit(group, [1, p], libgap.OnSets), + format='list_of_edges') + G.name("Generalised hexagon of order (%d, %d)"%(q, q)) + return G + + else: + raise NotImplementedError("Graph would be too big") + + elif orderType == 3: + if q > 3: + raise NotImplementedError("Graph would be too big") + + movedPoints = 819 if q==2 else 26572 + group = libgap.AtlasGroup("3D4(%d)"%q, libgap.NrMovedPoints, movedPoints) + + G = Graph(libgap.Orbit(group, [1, 2],libgap.OnSets), + format='list_of_edges') + G.name("Generalised hexagon of order (%d, %d)"%(q, q**3)) + return G + +def _extract_lines(G): + r""" + Return the set of lines from the point-graph of a generalised polygon. + + In particular, given a graph `G` we return the set of singular lines: + Let `(x,y)` be an edge, then `\{x,y\}^\bot^\bot` is a singular line. + We define `x^\bot =` neighbours of `x` and `x` for `x` a vertex and + `S^\bot =` intersection of `x^\bot` for all `x \in S`. + + INPUT: + + - ``G`` -- a graph + + OUTPUT: + + A list of tuples where each tuple represent a line through the vertices + contained in that line. + + EXAMPLES:: + + sage: from sage.graphs.generators.distance_regular import _extract_lines + sage: G = graphs.GeneralisedHexagonGraph(1, 8) + sage: lines = _extract_lines(G)) + sage: len(lines) + 657 + sage: type(lines) + + sage: line = lines[0] + sage: type(line) + + sage: line[0] in G # elements in line are vertices + True + + REFERENCES: + + See [BCN1989]_ pp. 200-205 for a discussion of distance-regular graphs from + generalised polygons. See also [BCN1989]_ pp. 28, 29 for some theory about + singular lines. + """ + + lines = [] + edges = set(G.edges(labels=False, sort=False)) + + while edges : + (x, y) = edges.pop() + + #compute line + botX = set(G.neighbors(x, closed=True)) + botY = set(G.neighbors(y, closed=True)) + bot1 = botX.intersection(botY) + + b = bot1.pop() + bot2 = frozenset(G.neighbors(b, closed=True)) + for v in bot1: + sig_check() + s = frozenset(G.neighbors(v, closed=True)) + bot2 = bot2.intersection(s) + + # now bot2 is a line + lines.append(tuple(bot2)) # we need tuple or GAP will complain later + + # remove already handled edges + for u, v in itertools.product(bot2, repeat=2): + try : + edges.remove((u, v)) + except KeyError: + pass # ignore this + #end while edges + + return lines + +def _line_graph_generalised_polygon(H): + r""" + Return the line-graph of the generalised polygon whose point-graph is `H`. + + In particular, return the line-graph of the incidence structure defined + by the singular lines of the graph `H`. + + See also :func:`sage.graphs.generators.distance_regular._extract_lines`. + + INPUT: + + - ``H`` -- a graph + + EXAMPLES:: + + sage: from sage.graphs.generators.distance_regular import \ + ....: _line_graph_generalised_polygon + sage: G = graphs.GeneralisedHexagonGraph(1, 8) + sage: H = _line_graph_generalised_polygon(G) + sage: H.is_distance_regular(True) + ([16, 8, 8, None], [None, 1, 1, 2]) + sage: G = graphs.GeneralisedHexagonGraph(3, 3) + sage: H = _line_graph_generalised_polygon(G) + sage: G.is_isomorphic(H) + True + + REFERENCES: + + See [BCN1989]_ pp. 200-205 for a discussion of distance-regular graphs from + generalised polygons. See also [BCN1989]_ pp. 28, 29 for some theory about + singular lines. + """ + lines = _extract_lines(H) + + # get a map (point -> all lines incident to point) + vToLines = {v: [] for v in H} + for l in lines: + for p in l: + sig_check() + vToLines[p].append(l) + + k = len(vToLines[lines[0][0]]) + + edges = [] + for v in vToLines: + lines = vToLines[v] + for l1, l2 in itertools.combinations(lines, 2): + sig_check() + edges.append((l1, l2)) + + G = Graph(edges, format="list_of_edges") + return G diff --git a/src/sage/graphs/graph_generators.py b/src/sage/graphs/graph_generators.py index d7aa72f4821..22f988e4139 100644 --- a/src/sage/graphs/graph_generators.py +++ b/src/sage/graphs/graph_generators.py @@ -241,6 +241,9 @@ def __append_to_doc(methods): "FurerGadget", "fusenes", "FuzzyBallGraph", + "GeneralisedDodecagonGraph", + "GeneralisedHexagonGraph", + "GeneralisedOctagonGraph", "GeneralizedPetersenGraph", "GoethalsSeidelGraph", "GrassmannGraph", @@ -2079,6 +2082,9 @@ def quadrangulations(self, order, minimum_degree=None, minimum_connectivity=None FriendshipGraph = staticmethod(families.FriendshipGraph) FurerGadget = staticmethod(families.FurerGadget) FuzzyBallGraph = staticmethod(families.FuzzyBallGraph) + GeneralisedDodecagonGraph = staticmethod(distance_regular.GeneralisedDodecagonGraph) + GeneralisedHexagonGraph = staticmethod(distance_regular.GeneralisedHexagonGraph) + GeneralisedOctagonGraph = staticmethod(distance_regular.GeneralisedOctagonGraph) GeneralizedPetersenGraph = staticmethod(families.GeneralizedPetersenGraph) GoethalsSeidelGraph = staticmethod(families.GoethalsSeidelGraph) GrassmannGraph = staticmethod(distance_regular.GrassmannGraph) From 66d7fa350de80a77ce78922f669bfa68d3bb255e Mon Sep 17 00:00:00 2001 From: Ivo Maffei Date: Wed, 12 Aug 2020 11:47:10 +0200 Subject: [PATCH 048/713] fix doctests --- src/sage/graphs/generators/distance_regular.pyx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/sage/graphs/generators/distance_regular.pyx b/src/sage/graphs/generators/distance_regular.pyx index d0e2b6b7614..1724d53fd82 100644 --- a/src/sage/graphs/generators/distance_regular.pyx +++ b/src/sage/graphs/generators/distance_regular.pyx @@ -1237,10 +1237,10 @@ def GeneralisedDodecagonGraph(const int s, const int t): ([4, 3, 3, 3, 3, 3, None], [None, 1, 1, 1, 1, 1, 4]) sage: G = graphs.GeneralisedDodecagonGraph(1, 2) # optional - gap_packages internet sage: G.is_distance_regular(True) # optional - gap_packages internet - (3, 2, 2, 2, 2, 2, None], [None, 1, 1, 1, 1, 1, 3]) + ([3, 2, 2, 2, 2, 2, None], [None, 1, 1, 1, 1, 1, 3]) sage: G = graphs.GeneralisedDodecagonGraph(1, 1) # optional - gap_packages internet sage: G.is_distance_regular(True) # optional - gap_packages internet - (2, 1, 1, 1, 1, 1, None], [None, 1, 1, 1, 1, 1, 2]) + ([2, 1, 1, 1, 1, 1, None], [None, 1, 1, 1, 1, 1, 2]) Now test all graphs of order `(q, 1)`:: @@ -1250,7 +1250,7 @@ def GeneralisedDodecagonGraph(const int s, const int t): sage: G = graphs.GeneralisedDodecagonGraph(3, 1) # optional - gap_packages internet sage: G.is_distance_regular(True) # optional - gap_packages internet ([6, 3, 3, 3, 3, 3, None], [None, 1, 1, 1, 1, 1, 2]) - sage: G = graphs.GeneralisedDodecagonGraph(3, 1) # optional - gap_packages internet + sage: G = graphs.GeneralisedDodecagonGraph(2, 1) # optional - gap_packages internet sage: G.is_distance_regular(True) # optional - gap_packages internet ([4, 2, 2, 2, 2, 2, None], [None, 1, 1, 1, 1, 1, 2]) @@ -1331,7 +1331,7 @@ def GeneralisedOctagonGraph(const int s, const int t): TESTS:: - sage: G = graphs.GeneralisedOctagonGraph(8, 16) + sage: G = graphs.GeneralisedOctagonGraph(8, 64) Traceback (most recent call last): ... NotImplementedError: Graph would be too big @@ -1576,7 +1576,7 @@ def _extract_lines(G): sage: from sage.graphs.generators.distance_regular import _extract_lines sage: G = graphs.GeneralisedHexagonGraph(1, 8) - sage: lines = _extract_lines(G)) + sage: lines = _extract_lines(G) sage: len(lines) 657 sage: type(lines) From e45c62d24192f906e8395a721f969a44dc5cb11b Mon Sep 17 00:00:00 2001 From: Ivo Maffei Date: Wed, 12 Aug 2020 12:16:16 +0200 Subject: [PATCH 049/713] sketch general function with conditionals for drg module --- .../graphs/generators/distance_regular.pyx | 113 ++++++++++++++++++ 1 file changed, 113 insertions(+) diff --git a/src/sage/graphs/generators/distance_regular.pyx b/src/sage/graphs/generators/distance_regular.pyx index 1724d53fd82..c63fbdf09f3 100644 --- a/src/sage/graphs/generators/distance_regular.pyx +++ b/src/sage/graphs/generators/distance_regular.pyx @@ -1677,3 +1677,116 @@ def _line_graph_generalised_polygon(H): G = Graph(edges, format="list_of_edges") return G + +def _intersection_array_grom_graph(G): + r""" + Return the intersection array of the graph `G`. + If `G` is not distance-regular, then return ``False``. + + This is a simple wrapper around + :meth:`sage.graphs.distances_all_pairs.is_distance_regular` to return a list + instead of a pair of lists + + INPUT: + + - G -- a graph + + EXAMPLES:: + + TESTS:: + + """ + + t = G.is_distance_regular(True) + if t is False: + return False + + return t[0][:-1] + t[1][1:] + +def distance_regular_graph(list arr, existence=False, check=True): + + # check if drg module is installed + try: + import drg + from drg import InfeasibleError + drgModule = True + except ModuleNotFoundError: + drgModule = False + + def result(G): + if check: + array = _intersection_array_from_graph(G) + if array != arr: + raise RuntimeError(("Sage built the wrong distance-regular " + f"graph; expected {arr}, result {array}")) + return G + + def is_iterable(obj): + try: + iter(obj) + return True + except TypeError: + return False + + n = len(arr) + d = n // 2 + + #check that arr makes sense: + if drgModule: + try: + parameters = drg.DRGParameters(arr[:d],arr[d:]) + except (AssertionError, InfeasibleError, TypeError) as err: + if existence: return False + raise EmptySetError(("No distance-regular graphs with " + f"parameters {arr} exists; reason: {err}")) + else: + #implement basic checks + + # handle diameter < 3 + if d == 1 and arr[1] == 1: + if existence: + return True + from sage.graphs.generators.basic import CompleteGraph + return result(CompleteGraph(arr[0] + 1)) + + if d == 2: + from sage.graphs.strongly_regular_db import strongly_regular_graph + + k = arr[0] + mu = arr[3] + l = k - arr[1] - 1 # a1 = k - b1 - c1 + v = (k * (k-l-1)) // mu + k + 1 + + if existence: + return strongly_regular_graph(v, k, l, mu, existence=True) + return result(strongly_regular_graph(v, k, l, mu)) + + t = tuple(arr) + if t in _sporadic_graph_database: + if existence: + return True + return result(_sporadic_graph_database[t]()) + + for (f, g) in _infinite_families: + t = f(arr) + if t is not False: + if existence: + return True + + G = g(*t) if is_iterable(t) else g(t) + return result(G) + + #now try drg feasibility + if drgModule: + try: + parameters.check_feasible() + except (InfeasibleError, TypeError, AssertionError) as err: + if existence: + return False + raise EmptySetError(("No distance-regular graphs with " + f"parameters {arr} exists; reason: {err}")) + + if existence: + return Unknown + raise RuntimeError( + f"No distance-regular graph with intersection array {arr} known") From 0268857754680f0004815867349baf6c446b8eea Mon Sep 17 00:00:00 2001 From: Ivo Maffei Date: Wed, 12 Aug 2020 19:25:14 +0200 Subject: [PATCH 050/713] fixed most sporadic graphs; added some docstring; added method to graphs --- .../graphs/generators/distance_regular.pyx | 108 +++++++++++++++--- src/sage/graphs/graph_generators.py | 1 + 2 files changed, 93 insertions(+), 16 deletions(-) diff --git a/src/sage/graphs/generators/distance_regular.pyx b/src/sage/graphs/generators/distance_regular.pyx index be2bc40423f..5a8c011ec5d 100644 --- a/src/sage/graphs/generators/distance_regular.pyx +++ b/src/sage/graphs/generators/distance_regular.pyx @@ -29,6 +29,7 @@ AUTHORS: # https://www.gnu.org/licenses/ # **************************************************************************** +from sage.coding import codes_catalog as codes from sage.graphs.graph import Graph from sage.libs.gap.libgap import libgap from sage.modules.free_module import VectorSpace @@ -307,7 +308,6 @@ def LargeWittGraph(): This construction is taken from http://mathworld.wolfram.com/LargeWittGraph.html """ - from sage.coding import codes_catalog as codes import itertools C = codes.GolayCode(GF(2), extended=True) @@ -401,8 +401,6 @@ def distance_3_doubly_truncated_Golay_code_graph(): Description and construction of this graph are taken from [BCN1989]_ p. 364. """ - from sage.coding import codes_catalog as codes - G = codes.GolayCode(GF(2),extended=False).punctured([0,1]).cosetGraph() v = G.vertices(sort=False)[0] it = G.breadth_first_search(v, distance=3, report_distance=True) @@ -435,7 +433,6 @@ def shortened_00_11_binary_Golay_code_graph(): Description and construction of this graph can be found in [BCN1989]_ p. 365. """ - from sage.coding import codes_catalog as codes from sage.coding.linear_code import LinearCode code = codes.GolayCode(GF(2), False) @@ -474,7 +471,6 @@ def shortened_000_111_extended_binary_Golay_code_graph(): Description and construction of this graph can be found in [BCN1989]_ p. 365. """ - from sage.coding import codes_catalog as codes from sage.coding.linear_code import LinearCode code = codes.GolayCode(GF(2)) @@ -800,7 +796,7 @@ def HermitianFormsGraph(const int n, const int q): .. NOTE:: If ``q`` does not satisfy the requirements, then this function - will raise a ``ValueError``. This function needs the additional + will raise a ``ValueError``. This function needs the additional package MeatAxe. Install it with ``sage -i meataxe``. REFERENCES: @@ -1720,12 +1716,12 @@ def _line_graph_generalised_polygon(H): G = Graph(edges, format="list_of_edges") return G -def _intersection_array_grom_graph(G): +def _intersection_array_from_graph(G): r""" Return the intersection array of the graph `G`. If `G` is not distance-regular, then return ``False``. - This is a simple wrapper around + This is a simple wrapper around :meth:`sage.graphs.distances_all_pairs.is_distance_regular` to return a list instead of a pair of lists @@ -1745,7 +1741,85 @@ def _intersection_array_grom_graph(G): return t[0][:-1] + t[1][1:] +# given functions f,g +# returns function (f.g) +# f is expected to have only 1 input +#def _compose(f, g): +# return lambda *x: f(g(*x)) + +# dictionary intersection_array (as tuple) -> construction +# of spordaic distance-regular graphs +from sage.graphs.generators.smallgraphs import (FosterGraph, BiggsSmithGraph, + CoxeterGraph, LivingstoneGraph, + WellsGraph, GossetGraph) +from sage.graphs.generators.platonic_solids import DodecahedralGraph +_sporadic_graph_database = { + (3, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 3) : FosterGraph, + (7, 6, 4, 4, 4, 1, 1, 1, 1, 1, 1, 2, 4, 4, 6, 7) : IvanovIvanovFaradjevGraph, + (3, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3) : BiggsSmithGraph, + #(22, 21, 20, 16, 6, 2, 1, 1, 2, 6, 16, 20, 21, 22) : _compose(bipartite_double_graph, truncated_binary_Golay_code_graph), + #(23, 22, 21, 20, 3, 2, 1, 1, 2, 3, 20, 21, 22, 23) : _compose(bipartite_double_graph, binary_Golay_code_graph), + (21, 20, 16, 6, 2, 1, 1, 2, 6, 16, 20, 21) : \ + shortened_00_11_binary_Golay_code_graph, + (21, 20, 16, 9, 2, 1, 1, 2, 3, 16, 20, 21) : \ + shortened_000_111_extended_binary_Golay_code_graph, + (22, 21, 20, 3, 2, 1, 1, 2, 3, 20, 21, 22) : \ + codes.GolayCode(GF(2), extended=False).shortened([0]).cosetGraph, + (3, 2, 1, 1, 1, 1, 1, 1, 2, 3) : DodecahedralGraph, + (22, 20, 18, 2, 1, 1, 2, 9, 20, 22) : \ + codes.GolayCode(GF(3)).shortened([0]).cosetGraph, + #(7, 6, 6, 1, 1, 1, 1, 6, 6, 7) : _compose(bipartite_double_graph, graphs.HoffmanSingletonGraph), + #(10, 9, 8, 2, 1, 1, 2, 8, 9, 10) : _compose(bipartite_double_graph, graphs.SimsGewirtzGraph), + #(16, 15, 12, 4, 1, 1, 4, 12, 15, 16) : lambda : bipartite_double_graph(graphs.strongly_regular_graph(77,16,0)), + #(22, 21, 16, 6, 1, 1, 6, 16, 21, 22) : _compose(bipartite_double_graph, graphs.HigmanSimsGraph), + (3, 2, 2, 1, 1, 1, 1, 2) : CoxeterGraph, + (6, 5, 5, 4, 1, 1, 2, 6) : LintSchrijverGraph, + (7, 6, 4, 4, 1, 1, 1, 6) : DoublyTruncatedWittGraph, + (9, 8, 6, 3, 1, 1, 3, 8) : distance_3_doubly_truncated_Golay_code_graph, + (10, 8, 8, 2, 1, 1, 4, 5) : J2Graph, + (11, 10, 6, 1, 1, 1, 5, 11) : LivingstoneGraph, + (5, 4, 1, 1, 1, 1, 4, 5) : WellsGraph, + (6, 4, 2, 1, 1, 1, 4, 6) : FosterGraph3S6, + (10, 6, 4, 1, 1, 2, 6, 10) : ConwaySmith_for_3S7, + (20, 18, 4, 1, 1, 2, 18, 20) : \ + codes.GolayCode(GF(3), extended=False).shortened([0]).cosetGraph, + (45, 32, 12, 1, 1, 6, 32, 45) : locally_GQ42_distance_transitive_graph, + (117, 80, 24, 1, 1, 12, 80, 117) : graph_3O73, + (22, 21, 20, 1, 2, 6): \ + codes.GolayCode(GF(2), extended=False).punctured([0]).cosetGraph, + (23, 22, 21, 1, 2, 3): codes.GolayCode(GF(2), extended=False).cosetGraph, + (24, 23, 22, 21, 1, 2, 3, 24): codes.GolayCode(GF(2)).cosetGraph, + (12,11,10,7,1,2,5,12): LeonardGraph, + (15,14,10,3,1,5,12,15): cocliques_HoffmannSingleton, + (27,10,1,1,10,27): GossetGraph, + (30,28,24,1,3,15): LargeWittGraph, + (15,14,12,1,1,9): TruncatedWittGraph, + (24,22,20,1,2,12): codes.GolayCode(GF(3)).cosetGraph, + (21,20,16,1,2,12): \ + codes.GolayCode(GF(2), extended=False).punctured([0, 1]).cosetGraph +} + def distance_regular_graph(list arr, existence=False, check=True): + r""" + Return a distance-regular graph with the intersection array given. + + INPUT: + + - ``arr`` -- list; intersection array of the graph + + - ``existence`` -- boolean + + - ``check`` -- boolean (optional); if ``True``, then checks that the result + of this function has the given intersection array. Default: ``True`` + + EXAMPLES:: + + REFERENCES: + + TESTS:: + """ + from sage.misc.unknown import Unknown + from sage.categories.sets_cat import EmptySetError # check if drg module is installed try: @@ -1772,8 +1846,8 @@ def distance_regular_graph(list arr, existence=False, check=True): n = len(arr) d = n // 2 - - #check that arr makes sense: + + # check that arr makes sense: if drgModule: try: parameters = drg.DRGParameters(arr[:d],arr[d:]) @@ -1783,6 +1857,7 @@ def distance_regular_graph(list arr, existence=False, check=True): f"parameters {arr} exists; reason: {err}")) else: #implement basic checks + pass # handle diameter < 3 if d == 1 and arr[1] == 1: @@ -1790,15 +1865,15 @@ def distance_regular_graph(list arr, existence=False, check=True): return True from sage.graphs.generators.basic import CompleteGraph return result(CompleteGraph(arr[0] + 1)) - + if d == 2: from sage.graphs.strongly_regular_db import strongly_regular_graph - + k = arr[0] mu = arr[3] l = k - arr[1] - 1 # a1 = k - b1 - c1 v = (k * (k-l-1)) // mu + k + 1 - + if existence: return strongly_regular_graph(v, k, l, mu, existence=True) return result(strongly_regular_graph(v, k, l, mu)) @@ -1809,15 +1884,16 @@ def distance_regular_graph(list arr, existence=False, check=True): return True return result(_sporadic_graph_database[t]()) + r""" for (f, g) in _infinite_families: t = f(arr) if t is not False: if existence: return True - + G = g(*t) if is_iterable(t) else g(t) return result(G) - + """ #now try drg feasibility if drgModule: try: @@ -1827,7 +1903,7 @@ def distance_regular_graph(list arr, existence=False, check=True): return False raise EmptySetError(("No distance-regular graphs with " f"parameters {arr} exists; reason: {err}")) - + if existence: return Unknown raise RuntimeError( diff --git a/src/sage/graphs/graph_generators.py b/src/sage/graphs/graph_generators.py index 22f988e4139..e0d44a7728b 100644 --- a/src/sage/graphs/graph_generators.py +++ b/src/sage/graphs/graph_generators.py @@ -2073,6 +2073,7 @@ def quadrangulations(self, order, minimum_degree=None, minimum_connectivity=None CubeGraph = staticmethod(families.CubeGraph) CubeConnectedCycle = staticmethod(families.CubeConnectedCycle) DipoleGraph = staticmethod(families.DipoleGraph) + distance_regular_graph = staticmethod(distance_regular.distance_regular_graph) DorogovtsevGoltsevMendesGraph = staticmethod(families.DorogovtsevGoltsevMendesGraph) DoubleGrassmannGraph = staticmethod(distance_regular.DoubleGrassmannGraph) DoubleOddGraph = staticmethod(distance_regular.DoubleOddGraph) From da6e768ffde18db3137a0cde1ee23aed08debec8 Mon Sep 17 00:00:00 2001 From: Sebastian Date: Thu, 13 Aug 2020 07:32:17 +0200 Subject: [PATCH 051/713] 30346: initial version --- src/sage/knots/link.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/sage/knots/link.py b/src/sage/knots/link.py index 9ab236bd7c3..290cdfa1c5c 100644 --- a/src/sage/knots/link.py +++ b/src/sage/knots/link.py @@ -2585,6 +2585,12 @@ def homfly_polynomial(self, var1='L', var2='M', normalization='lm'): sage: L2.homfly_polynomial('a', 'z', 'az') a*z^-1 - a^-1*z^-1 + Check that :trac:`30346` is fixed:: + + sage: L = Link([]) + sage: L.homfly_polynomial() + 1 + REFERENCES: - :wikipedia:`HOMFLY_polynomial` @@ -2604,6 +2610,8 @@ def homfly_polynomial(self, var1='L', var2='M', normalization='lm'): return fact s = '{}'.format(self.number_of_components()) ogc = self.oriented_gauss_code() + if not ogc[0]: + return L.one() for comp in ogc[0]: s += ' {}'.format(len(comp)) for cr in comp: From 17a0b907e52f1429037cefe7c7804cebccec5d87 Mon Sep 17 00:00:00 2001 From: Ivo Maffei Date: Fri, 14 Aug 2020 15:11:07 +0200 Subject: [PATCH 052/713] completed sporadic database; added more docstrings; added basic checks --- .../graphs/generators/distance_regular.pyx | 109 +++++++++++------- 1 file changed, 69 insertions(+), 40 deletions(-) diff --git a/src/sage/graphs/generators/distance_regular.pyx b/src/sage/graphs/generators/distance_regular.pyx index 5a8c011ec5d..0325dc6edb3 100644 --- a/src/sage/graphs/generators/distance_regular.pyx +++ b/src/sage/graphs/generators/distance_regular.pyx @@ -9,9 +9,15 @@ For a survey on distance-regular graph see [BCN1989]_ or [VDKT2016]_. EXAMPLES:: - sage: G = graphs.cocliques_HoffmannSingleton() - sage: G.is_distance_regular() - True + sage: G = graphs.cocliques_HoffmannSingleton() + sage: G.is_distance_regular() + True + sage: H = graphs.distance_regular_graph([15, 14, 10, 3, 1, 5, 12, 15]) + sage: H == G + True + sage: G = graphs.distance_regular([27, 10, 1, 1, 10, 27]) + sage: G.is_distance_regular(True) + ([27, 10, 1, None], [None, 1, 10, 27]) AUTHORS: @@ -1741,24 +1747,24 @@ def _intersection_array_from_graph(G): return t[0][:-1] + t[1][1:] -# given functions f,g -# returns function (f.g) -# f is expected to have only 1 input -#def _compose(f, g): -# return lambda *x: f(g(*x)) - # dictionary intersection_array (as tuple) -> construction # of spordaic distance-regular graphs from sage.graphs.generators.smallgraphs import (FosterGraph, BiggsSmithGraph, CoxeterGraph, LivingstoneGraph, - WellsGraph, GossetGraph) + WellsGraph, GossetGraph, + HoffmanSingletonGraph, + SimsGewirtzGraph, + HigmanSimsGraph) from sage.graphs.generators.platonic_solids import DodecahedralGraph +from sage.graphs.strongly_regular_db import strongly_regular_graph _sporadic_graph_database = { (3, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 3) : FosterGraph, (7, 6, 4, 4, 4, 1, 1, 1, 1, 1, 1, 2, 4, 4, 6, 7) : IvanovIvanovFaradjevGraph, (3, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3) : BiggsSmithGraph, - #(22, 21, 20, 16, 6, 2, 1, 1, 2, 6, 16, 20, 21, 22) : _compose(bipartite_double_graph, truncated_binary_Golay_code_graph), - #(23, 22, 21, 20, 3, 2, 1, 1, 2, 3, 20, 21, 22, 23) : _compose(bipartite_double_graph, binary_Golay_code_graph), + (22, 21, 20, 16, 6, 2, 1, 1, 2, 6, 16, 20, 21, 22) : lambda : \ + codes.GolayCode(GF(2), False).punctured([0]).cosetGraph().bipartite_double(), + (23, 22, 21, 20, 3, 2, 1, 1, 2, 3, 20, 21, 22, 23) : lambda : \ + codes.GolayCode(GF(2), False).cosetGraph().bipartite_double(), (21, 20, 16, 6, 2, 1, 1, 2, 6, 16, 20, 21) : \ shortened_00_11_binary_Golay_code_graph, (21, 20, 16, 9, 2, 1, 1, 2, 3, 16, 20, 21) : \ @@ -1768,10 +1774,11 @@ _sporadic_graph_database = { (3, 2, 1, 1, 1, 1, 1, 1, 2, 3) : DodecahedralGraph, (22, 20, 18, 2, 1, 1, 2, 9, 20, 22) : \ codes.GolayCode(GF(3)).shortened([0]).cosetGraph, - #(7, 6, 6, 1, 1, 1, 1, 6, 6, 7) : _compose(bipartite_double_graph, graphs.HoffmanSingletonGraph), - #(10, 9, 8, 2, 1, 1, 2, 8, 9, 10) : _compose(bipartite_double_graph, graphs.SimsGewirtzGraph), - #(16, 15, 12, 4, 1, 1, 4, 12, 15, 16) : lambda : bipartite_double_graph(graphs.strongly_regular_graph(77,16,0)), - #(22, 21, 16, 6, 1, 1, 6, 16, 21, 22) : _compose(bipartite_double_graph, graphs.HigmanSimsGraph), + (7, 6, 6, 1, 1, 1, 1, 6, 6, 7) : HoffmanSingletonGraph().bipartite_double, + (10, 9, 8, 2, 1, 1, 2, 8, 9, 10) : SimsGewirtzGraph().bipartite_double, + (16, 15, 12, 4, 1, 1, 4, 12, 15, 16) : \ + strongly_regular_graph(77, 16, 0, check=False).bipartite_double, + (22, 21, 16, 6, 1, 1, 6, 16, 21, 22) : HigmanSimsGraph().bipartite_double, (3, 2, 2, 1, 1, 1, 1, 2) : CoxeterGraph, (6, 5, 5, 4, 1, 1, 2, 6) : LintSchrijverGraph, (7, 6, 4, 4, 1, 1, 1, 6) : DoublyTruncatedWittGraph, @@ -1789,13 +1796,13 @@ _sporadic_graph_database = { codes.GolayCode(GF(2), extended=False).punctured([0]).cosetGraph, (23, 22, 21, 1, 2, 3): codes.GolayCode(GF(2), extended=False).cosetGraph, (24, 23, 22, 21, 1, 2, 3, 24): codes.GolayCode(GF(2)).cosetGraph, - (12,11,10,7,1,2,5,12): LeonardGraph, - (15,14,10,3,1,5,12,15): cocliques_HoffmannSingleton, - (27,10,1,1,10,27): GossetGraph, - (30,28,24,1,3,15): LargeWittGraph, - (15,14,12,1,1,9): TruncatedWittGraph, - (24,22,20,1,2,12): codes.GolayCode(GF(3)).cosetGraph, - (21,20,16,1,2,12): \ + (12, 11, 10, 7, 1, 2, 5, 12): LeonardGraph, + (15, 14, 10, 3, 1, 5, 12, 15): cocliques_HoffmannSingleton, + (27, 10, 1, 1, 10, 27): GossetGraph, + (30, 28, 24, 1, 3, 15): LargeWittGraph, + (15, 14, 12, 1, 1, 9): TruncatedWittGraph, + (24, 22, 20, 1, 2, 12): codes.GolayCode(GF(3)).cosetGraph, + (21, 20, 16, 1, 2, 12): \ codes.GolayCode(GF(2), extended=False).punctured([0, 1]).cosetGraph } @@ -1807,16 +1814,45 @@ def distance_regular_graph(list arr, existence=False, check=True): - ``arr`` -- list; intersection array of the graph - - ``existence`` -- boolean + - ``existence`` -- boolean (optional); instead of building the graph return: + * ``True`` - if a graph with the given intersection array exists; + * ``False`` - if there is no graph with the given intersection array; + * ``Unknown`` - if Sage doesn't know if such a graph exists. - ``check`` -- boolean (optional); if ``True``, then checks that the result of this function has the given intersection array. Default: ``True`` EXAMPLES:: + sage: graphs.distance_regular_graph([21,20,16,1,2,12], existence=True) + True + sage: G = graphs.distance_regular_graph([12,11,10,7,1,2,5,12], check=False) + sage: G.is_distance_regular(True) + ([12, 11, 10, 7, None], [None, 1, 2, 5, 12]) + + Not all distance-regular graphs can be built with this function:: + + sage: G = graphs.DoubleOddGraph(3) + sage: G.is_distance_regular(True) + ([3, 2, 2, 1, 1, None], [None, 1, 1, 2, 2, 3]) + sage: graphs.distance_regular_graph([3, 2, 2, 1, 1, 1, 1, 2, 2, 3]) + Traceback (most recent call last): + ... + RuntimeError: No distance-regular graph with intersection array [3, 2, 2, 1, 1, 1, 1, 2, 2, 3] known + REFERENCES: + See [BCN1989]_ and [VDKT2016]_. + TESTS:: + + sage: graphs.distance_regular_graph([3, 2, 2, 1, 1, 1, 1, 2, 2, 3], + ....: existence=True) + Unknown + sage: graphs.distance_regular_graph([3, 2, 2, 1, 2, 1, 1, 2, 2, 3], + ....: existence=True) + False + """ from sage.misc.unknown import Unknown from sage.categories.sets_cat import EmptySetError @@ -1854,10 +1890,15 @@ def distance_regular_graph(list arr, existence=False, check=True): except (AssertionError, InfeasibleError, TypeError) as err: if existence: return False raise EmptySetError(("No distance-regular graphs with " - f"parameters {arr} exists; reason: {err}")) + f"parameters {arr} exists; error: {err}")) else: - #implement basic checks - pass + # basic checks + if len(arr) % 2 == 1 or any([i <= 0 for i in arr]) or \ + any([x != int(x) for x in arr]) or \ + any([(arr[i] - arr[i+1]) < 0 for i in range(d)]) or \ + any([(arr[d+i+1] - arr[d+i]) < 0 for i in range(d)]): + raise EmptySetError(("No distance-regular graphs with " + f"parameters {arr} exists")) # handle diameter < 3 if d == 1 and arr[1] == 1: @@ -1867,8 +1908,6 @@ def distance_regular_graph(list arr, existence=False, check=True): return result(CompleteGraph(arr[0] + 1)) if d == 2: - from sage.graphs.strongly_regular_db import strongly_regular_graph - k = arr[0] mu = arr[3] l = k - arr[1] - 1 # a1 = k - b1 - c1 @@ -1884,17 +1923,7 @@ def distance_regular_graph(list arr, existence=False, check=True): return True return result(_sporadic_graph_database[t]()) - r""" - for (f, g) in _infinite_families: - t = f(arr) - if t is not False: - if existence: - return True - - G = g(*t) if is_iterable(t) else g(t) - return result(G) - """ - #now try drg feasibility + # now try drg feasibility if drgModule: try: parameters.check_feasible() From bd5ef0ab1280d60d0e764c5ed3e4232a621f0775 Mon Sep 17 00:00:00 2001 From: Ivo Maffei Date: Fri, 14 Aug 2020 15:32:44 +0200 Subject: [PATCH 053/713] fixed docstring and doctests --- src/sage/graphs/generators/distance_regular.pyx | 13 ++++++++----- src/sage/graphs/graph_generators.py | 1 + 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/sage/graphs/generators/distance_regular.pyx b/src/sage/graphs/generators/distance_regular.pyx index 0325dc6edb3..37f499c0a60 100644 --- a/src/sage/graphs/generators/distance_regular.pyx +++ b/src/sage/graphs/generators/distance_regular.pyx @@ -15,7 +15,7 @@ EXAMPLES:: sage: H = graphs.distance_regular_graph([15, 14, 10, 3, 1, 5, 12, 15]) sage: H == G True - sage: G = graphs.distance_regular([27, 10, 1, 1, 10, 27]) + sage: G = graphs.distance_regular_graph([27, 10, 1, 1, 10, 27]) sage: G.is_distance_regular(True) ([27, 10, 1, None], [None, 1, 10, 27]) @@ -1815,9 +1815,12 @@ def distance_regular_graph(list arr, existence=False, check=True): - ``arr`` -- list; intersection array of the graph - ``existence`` -- boolean (optional); instead of building the graph return: - * ``True`` - if a graph with the given intersection array exists; - * ``False`` - if there is no graph with the given intersection array; - * ``Unknown`` - if Sage doesn't know if such a graph exists. + + - ``True`` - if a graph with the given intersection array exists; + + - ``False`` - if there is no graph with the given intersection array; + + - ``Unknown`` - if Sage doesn't know if such a graph exists. - ``check`` -- boolean (optional); if ``True``, then checks that the result of this function has the given intersection array. Default: ``True`` @@ -1832,7 +1835,7 @@ def distance_regular_graph(list arr, existence=False, check=True): Not all distance-regular graphs can be built with this function:: - sage: G = graphs.DoubleOddGraph(3) + sage: G = graphs.DoubleOddGraph(2) sage: G.is_distance_regular(True) ([3, 2, 2, 1, 1, None], [None, 1, 1, 2, 2, 3]) sage: graphs.distance_regular_graph([3, 2, 2, 1, 1, 1, 1, 2, 2, 3]) diff --git a/src/sage/graphs/graph_generators.py b/src/sage/graphs/graph_generators.py index e0d44a7728b..71c508d198a 100644 --- a/src/sage/graphs/graph_generators.py +++ b/src/sage/graphs/graph_generators.py @@ -230,6 +230,7 @@ def __append_to_doc(methods): "cospectral_graphs", "CubeGraph", "CubeConnectedCycle", + "distance_regular_graph", "DorogovtsevGoltsevMendesGraph", "DoubleGrassmannGraph", "DoubleOddGraph", From 2ff56300b6ba53c0588f30e789f30108bf0fb179 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Sun, 16 Aug 2020 09:13:04 +0200 Subject: [PATCH 054/713] postpone `march=native` to later --- build/make/Makefile.in | 39 ++++++++-------------------- build/pkgs/eclib/spkg-install.in | 4 +-- build/pkgs/gap/spkg-install.in | 3 --- build/pkgs/gcc/spkg-configure.m4 | 41 +++++++----------------------- build/pkgs/gcc/spkg-install.in | 4 --- build/pkgs/mpfr/spkg-install.in | 2 +- build/pkgs/r/spkg-install.in | 4 +-- src/doc/en/developer/packaging.rst | 8 +----- 8 files changed, 25 insertions(+), 80 deletions(-) diff --git a/build/make/Makefile.in b/build/make/Makefile.in index e8090428466..f4c57840547 100644 --- a/build/make/Makefile.in +++ b/build/make/Makefile.in @@ -48,75 +48,56 @@ ORIGINAL_FCFLAGS:=$(FCFLAGS) ORIGINAL_F77FLAGS:=$(F77FLAGS) export ORIGINAL_CFLAGS, ORIGINAL_CXXFLAGS, ORIGINAL_FCFLAGS, ORIGINAL_F77FLAGS -SAGE_MARCH=@SAGE_MARCH@ - ifeq ($(ORIGINAL_CFLAGS), ) # Evaluate SAGE_DEBUG: ifeq ($(SAGE_DEBUG), yes) -CFLAGS_NON_NATIVE=-Og -g -CFLAGS_O3_NON_NATIVE=-Og -g -SAGE_MARCH= +CFLAGS=-Og -g +CFLAGS_O3=-Og -g else ifeq ($(SAGE_DEBUG), no) -CFLAGS_NON_NATIVE=-O2 -CFLAGS_O3_NON_NATIVE=-O3 +CFLAGS=-O2 +CFLAGS_O3=-O3 else -CFLAGS_NON_NATIVE=-O2 -g -CFLAGS_O3_NON_NATIVE=-O3 -g +CFLAGS=-O2 -g +CFLAGS_O3=-O3 -g endif endif -export CFLAGS=$(CFLAGS_NON_NATIVE) $(SAGE_MARCH) -CFLAGS_O3:=$(CFLAGS_O3_NON_NATIVE) $(SAGE_MARCH) else # Respect user environment variable. CFLAGS=$(ORIGINAL_CFLAGS) CFLAGS_O3=$(ORIGINAL_CFLAGS) -CFLAGS_NON_NATIVE=$(ORIGINAL_CFLAGS) -CFLAGS_O3_NON_NATIVE=$(ORIGINAL_CFLAGS) endif -export CFLAGS_NON_NATIVE, CFLAGS_O3_NON_NATIVE, CFLAGS_O3, CFLAGS +export CFLAGS_O3, CFLAGS # Copy to CXXFLAGS if this is not set. ifeq ($(ORIGINAL_CXXFLAGS), ) CXXFLAGS=$(CFLAGS) CXXFLAGS_O3=$(CFLAGS_O3) -CXXFLAGS_NON_NATIVE=$(CFLAGS_NON_NATIVE) -CXXFLAGS_O3_NON_NATIVE=$(CFLAGS_O3_NON_NATIVE) else CXXFLAGS=$(ORIGINAL_CXXFLAGS) CXXFLAGS_O3=$(ORIGINAL_CXXFLAGS) -CXXFLAGS_NON_NATIVE=$(ORIGINAL_CXXFLAGS) -CXXFLAGS_O3_NON_NATIVE=$(ORIGINAL_CXXFLAGS) endif -export CXXFLAGS_NON_NATIVE, CXXFLAGS_O3_NON_NATIVE, CXXFLAGS_O3, CXXFLAGS +export CXXFLAGS_O3, CXXFLAGS # Copy CFLAGS to FCFLAGS if this is not set. ifeq ($(ORIGINAL_FCFLAGS), ) FCFLAGS=$(CFLAGS) FCFLAGS_O3=$(CFLAGS_O3) -FCFLAGS_NON_NATIVE=$(CFLAGS_NON_NATIVE) -FCFLAGS_O3_NON_NATIVE=$(CFLAGS_O3_NON_NATIVE) else FCFLAGS=$(ORIGINAL_FCFLAGS) FCFLAGS_O3=$(ORIGINAL_FCFLAGS) -FCFLAGS_NON_NATIVE=$(ORIGINAL_FCFLAGS) -FCFLAGS_O3_NON_NATIVE=$(ORIGINAL_FCFLAGS) endif -export FCFLAGS_NON_NATIVE, FCFLAGS_O3_NON_NATIVE, FCFLAGS_O3, FCFLAGS +export FCFLAGS_O3, FCFLAGS # Copy FCFLAGS to F77FLAGS if this is not set. ifeq ($(ORIGINAL_F77FLAGS), ) F77FLAGS=$(FCFLAGS) F77FLAGS_O3=$(FCFLAGS_O3) -F77FLAGS_NON_NATIVE=$(FCFLAGS_NON_NATIVE) -F77FLAGS_O3_NON_NATIVE=$(FCFLAGS_O3_NON_NATIVE) else F77FLAGS=$(ORIGINAL_F77FLAGS) F77FLAGS_O3=$(ORIGINAL_F77FLAGS) -F77FLAGS_NON_NATIVE=$(ORIGINAL_F77FLAGS) -F77FLAGS_O3_NON_NATIVE=$(ORIGINAL_F77FLAGS) endif -export F77FLAGS_NON_NATIVE, F77FLAGS_O3_NON_NATIVE, F77FLAGS_O3, F77FLAGS +export F77FLAGS_O3, F77FLAGS # Directory to keep track of which packages are installed diff --git a/build/pkgs/eclib/spkg-install.in b/build/pkgs/eclib/spkg-install.in index 1611cfdd27d..9ccc0420e34 100644 --- a/build/pkgs/eclib/spkg-install.in +++ b/build/pkgs/eclib/spkg-install.in @@ -1,5 +1,5 @@ -CFLAGS="$CFLAGS_O3_NON_NATIVE" -CXXFLAGS="$CXXFLAGS_O3_NON_NATIVE" +CFLAGS="$CFLAGS_O3" +CXXFLAGS="$CXXFLAGS_O3" export CFLAGS CXXFLAGS diff --git a/build/pkgs/gap/spkg-install.in b/build/pkgs/gap/spkg-install.in index c986e59da6d..56e95338df1 100644 --- a/build/pkgs/gap/spkg-install.in +++ b/build/pkgs/gap/spkg-install.in @@ -4,9 +4,6 @@ cd src -export CFLAGS=$CFLAGS_NON_NATIVE -export CXXFLAGS=$CXXFLAGS_NON_NATIVE - GAP_BUILD_ROOT="$(pwd)" GAP_ROOT="$SAGE_LOCAL/share/gap" DESTDIR_GAP_ROOT="$SAGE_DESTDIR$GAP_ROOT" diff --git a/build/pkgs/gcc/spkg-configure.m4 b/build/pkgs/gcc/spkg-configure.m4 index de3148b78ae..0cedc4b61de 100644 --- a/build/pkgs/gcc/spkg-configure.m4 +++ b/build/pkgs/gcc/spkg-configure.m4 @@ -151,10 +151,6 @@ SAGE_SPKG_CONFIGURE_BASE([gcc], [ # Install our own GCC if the system-provided one is newer than 9.x. # See https://trac.sagemath.org/ticket/29456 SAGE_SHOULD_INSTALL_GCC([$CXX is g++ version $GXX_VERSION, which is too recent for this version of Sage]) - ], - [4.[[8-9]].*|5.[[0-1]].*], [ - # GCC less than 5.1 is not ready for AVX512. - sage_use_march_native=no ]) fi @@ -206,53 +202,34 @@ SAGE_SPKG_CONFIGURE_BASE([gcc], [ ]) fi - # Determine which compiler flags should be set. - if test "x$sage_use_march_native" = "xno"; then - SAGE_MARCH="" - elif test "x$SAGE_FAT_BINARY" = "xyes"; then - SAGE_MARCH="" - elif test "x$sage_spkg_install_gcc" =" xyes"; then - SAGE_MARCH=" -march=native" - else - AX_CHECK_COMPILE_FLAG("-march=native", [SAGE_MARCH=" -march=native"], [SAGE_MARCH=""], [], []) - fi - AC_SUBST(SAGE_MARCH) - - # We will only export SAGE_MARCH for now and redo this later. + # We redo this later. # (Allow debugging of individual packages.) # But we print the messages for convenience. - if test "x$SAGE_DEBUG" = "xyes"; then - SAGE_MARCH="" - fi if test "x$ORIGINAL_CFLAGS" = "x"; then # Evaluate SAGE_DEBUG: if test "x$SAGE_DEBUG" = "xyes" ; then - CFLAGS_NON_NATIVE="-Og -g" - CFLAGS_O3_NON_NATIVE="-Og -g" + CFLAGS="-Og -g" + CFLAGS_O3="-Og -g" else if test "x$SAGE_DEBUG" = "xno" ; then - CFLAGS_NON_NATIVE="-O2" - CFLAGS_O3_NON_NATIVE="-O3" + CFLAGS="-O2" + CFLAGS_O3="-O3" else - CFLAGS_NON_NATIVE="-O2 -g" - CFLAGS_O3_NON_NATIVE="-O3 -g" + CFLAGS="-O2 -g" + CFLAGS_O3="-O3 -g" fi fi - CFLAGS="${CFLAGS_NON_NATIVE}${SAGE_MARCH} - CFLAGS_O3="${CFLAGS_O3_NON_NATIVE}${SAGE_MARCH} + CFLAGS="${CFLAGS_NON_NATIVE} + CFLAGS_O3="${CFLAGS_O3_NON_NATIVE} else # Respect user environment variable. CFLAGS="${CFLAGS} CFLAGS_O3="${CFLAGS} - CFLAGS_NON_NATIVE="${CFLAGS} - CFLAGS_O3_NON_NATIVE="${CFLAGS} fi AC_MSG_NOTICE(ORIGINAL_CFLAGS=$ORIGINAL_CFLAGS) AC_MSG_NOTICE(CFLAGS=$CFLAGS) AC_MSG_NOTICE(CFLAGS_O3=$CFLAGS_O3) - AC_MSG_NOTICE(CFLAGS_NON_NATIVE=$CFLAGS_NON_NATIVE) - AC_MSG_NOTICE(CFLAGS_O3_NON_NATIVE=$CFLAGS_O3_NON_NATIVE) ], , , [ # Trac #27907: Find location of crti.o from the system CC, in case we build our own gcc diff --git a/build/pkgs/gcc/spkg-install.in b/build/pkgs/gcc/spkg-install.in index f7c8db1de19..819a9caba59 100644 --- a/build/pkgs/gcc/spkg-install.in +++ b/build/pkgs/gcc/spkg-install.in @@ -1,7 +1,3 @@ -# If we are building gcc ourselves, usually the system compiler isn't suitable. -export CFLAGS=$CFLAGS_NON_NATIVE -export CXXFLAGS=$CXXFLAGS_NON_NATIVE - # First, install any custom binaries or headers we created for macOS # workarounds for dir in bin include; do diff --git a/build/pkgs/mpfr/spkg-install.in b/build/pkgs/mpfr/spkg-install.in index 36663a48392..5e062603669 100644 --- a/build/pkgs/mpfr/spkg-install.in +++ b/build/pkgs/mpfr/spkg-install.in @@ -36,7 +36,7 @@ mpfr_configure() if [ "$SAGE_DEBUG" = yes ]; then # Disable optimization, add debug symbols: - required_cflags=$CFLAGS_NON_NATIVE + required_cflags=$CFLAGS echo >&2 "Warning: Building MPFR with SAGE_DEBUG=yes disables optimization." else # Add debug symbols by default, enable optimization, but do not (yet) diff --git a/build/pkgs/r/spkg-install.in b/build/pkgs/r/spkg-install.in index c63ff768f5b..e262131aa30 100644 --- a/build/pkgs/r/spkg-install.in +++ b/build/pkgs/r/spkg-install.in @@ -11,8 +11,8 @@ CPPFLAGS="$CPPFLAGS" # "for type of 'hidden' Fortran character lengths" # on ubuntu-bionic-minimal, ubuntu-eoan/focal-minimal, debian-buster/bullseye/sid-minimal, # linuxmint-19.3-minimal, archlinux-latest-minimal -CFLAGS="$CFLAGS_NON_NATIVE -fPIC" -FCFLAGS="$FCFLAGS_NON_NATIVE -fPIC" +CFLAGS="$CFLAGS -fPIC" +FCFLAGS="$FCFLAGS -fPIC" export CFLAGS CPPFLAGS FCFLAGS LDFLAGS diff --git a/src/doc/en/developer/packaging.rst b/src/doc/en/developer/packaging.rst index b065cf8bfb7..2e8a7a626b8 100644 --- a/src/doc/en/developer/packaging.rst +++ b/src/doc/en/developer/packaging.rst @@ -249,7 +249,7 @@ something like the following to install it: fi At build time :envvar:`CFLAGS`, :envvar:`CXXFLAGS`, :envvar:`FCFLAGS`, -and :envvar:`F77FLAGS` are usually set to ``-g -O2 -march=native`` +and :envvar:`F77FLAGS` are usually set to ``-g -O2`` (according to `debugging options <../installation/source.html#sage-debug>`_ and whether building `fat binaries <../installation/source.html#sage-fat-binary>`_). @@ -258,15 +258,9 @@ Slightly modified versions are available: .. CODE-BLOCK:: bash - # No ``-march=native``. - export CFLAGS=$CFLAGS_NON_NATIVE - # ``-O3`` instead of ``-O2``. export CFLAGS=$CFLAGS_O3 - # No ``-march=native`` and ``-O3`` instead of ``-O2``. - export CFLAGS=$CFLAGS_O3_NON_NATIVE - # Use flags as set by the user, possibly empty. export CFLAGS=$ORIGINAL_CFLAGS From 100937041c921f307262ae55e3431dade8e31b9e Mon Sep 17 00:00:00 2001 From: Ivo Maffei Date: Tue, 18 Aug 2020 11:38:38 +0200 Subject: [PATCH 055/713] added all code for classical parameters and most docs; there are bugs to fix --- .../graphs/generators/distance_regular.pyx | 400 +++++++++++++++++- 1 file changed, 382 insertions(+), 18 deletions(-) diff --git a/src/sage/graphs/generators/distance_regular.pyx b/src/sage/graphs/generators/distance_regular.pyx index 37f499c0a60..c6ec0697abe 100644 --- a/src/sage/graphs/generators/distance_regular.pyx +++ b/src/sage/graphs/generators/distance_regular.pyx @@ -1747,6 +1747,354 @@ def _intersection_array_from_graph(G): return t[0][:-1] + t[1][1:] + +cdef enum ClassicalParametersGraph: + NonExisting = 0, + Johnson, + Hamming, + HalvedCube, + UnitaryDualPolar, + HermitianForms, + GeneralisedHexagon, + Grassmann, + OrthogonalDualPolar1, + SymplecticDualPolar, + OrthogonalDualPolar2, + UnitaryDualPolar1, + UnitaryDualPolar2, + Ustimenko, + BilinearForms, + AlternatingForms, + LieE77, + AffineE6 + +def is_classical_parameters_graph(list array): + r""" + Return a tuple of parameters representing the array given. If such no tuple + can be produced, it returns ``False``. + + Given an intersection array, if it represents a family of distance-regular + graphs with classical parameters, then this function returns a tuple + consisting of the parameters `(d, b, \alpha, \beta)` and a fourth parameter + which is the enum ``CalssicalParametersGraph`` indicating the family with + the given itersection array. + If the array doesn't belong to any classical parameter graph, then this + function returns ``False``. + If the array belongs to a sporadic graph rather than a family of graphs, + then the function returns ``False``. This is to reduce the overlap with + ``sage.graphs.generators.distance_regular._sporadic_graph_database``. + + + .. NOTE:: + + The array given as an input is expected to be an intersection array. + If this is not the case, then some exception may be raised. + + INPUT: + + - ``array`` -- list; an intersection array + + OUTPUT: + + ``False`` or a tuple ``(d, b, alpha, beta, gamma)``. + + EXAMPLES:: + + sage: from sage.graphs.generators.distance_regular import \ + ....: is_classical_parameters_graph + sage: G = graphs.HammingGraph(5, 4) + sage: G.is_distance_regular(True) + ([15, 12, 9, 6, 3, None], [None, 1, 2, 3, 4, 5]) + sage: is_classical_parameters_graph([15, 12, 9, 6, 3, 1, 2, 3, 4, 5]) + (5, 1, 0, 3, 2) + + REFERENCES: + + See [BCN1989]_ chapter 9 for a discussion of distance-regular graphs with + classical parameters. See [BCN1989]_ chapter 6.2 for a method to compute + the classical parameters of a graph. See also [VDKT2016]_ section 3.1.1. + + TESTS:: + + sage: from sage.graphs.generators.distance_regular import \ + ....: is_classical_parameters_graph + sage: is_classical_parameters_graph([68, 64, 1, 17]) # srg not drg + False + sage: G = gaphs.GossetGraph() # sporadic classical parameters graph + sage: G.is_distance_regular(True) + ([27, 10, 1, None], [None, 1, 10, 27]) + sage: is_classical_parameters_graph([27, 10, 1, 1, 10, 27]) + False + + """ + from sage.functions.log import log + from sage.rings.integer_ring import ZZ + from sage.arith.misc import is_prime_power + from sage.combinat.q_analogues import q_binomial + + def integral_log(const int x, const int b): + # compute log_b(x) if is not a positive iteger, return -1 + if x <= 0: + return -1 + k = log(x, b) + if k in ZZ and k > 0: + return int(k) + return -1 + + def check_parameters(int d, int b, int alpha, int beta, list arr): + bs = [(q_binomial(d, 1, b) - q_binomial(i, 1, b)) * \ + (beta - alpha * q_binomial(i, 1, b)) for i in range(d)] + cs = [q_binomial(i, 1, b) * (1 + alpha*q_binomial(i-1, 1, b)) + for i in range(1, d+1)] + print(bs + cs) + return arr == bs + cs + + def b_(i): + if i == d: + return 0 + return array[i] + + def c_(i): + if i == 0: + return 0 + return array[d - 1 + i] + + def a_(i): + return b_(0) - b_(i) - c_(i) + + if len(array) % 2 != 0 : + return False + + d = len(array) // 2 + if d < 3: + return False + + # if array is classical parameters, then we have 2 cases: + # 1. a_i != \lambda c_i for 2<= i <= d + # 2. a_i == \lambda c_i for 0<= i <= d + if all(a_(i) == a_(1) * c_(i) for i in range(2, d+1)): + case = 2 + elif all(a_(i) != a_(1) * c_(i) for i in range(2, d+1)): + case = 1 + else: + return False + + print(f"case : {case}") + if case == 1: + # b = (a_2 c_3 - c_2 a_3) / (a_1 c_3 - a_3) + num = a_(2) * c_(3) - c_(2) * a_(3) + den = a_(1) * c_(3) - a_(3) + b = num / den + if b in {0, -1}: + return False + + alpha = c_(2) / (1 + b) - 1 + beta = b_(0) / q_binomial(d, 1, b) + + else: # case 2 + # b is either c_2 - 1 or - a_1 - 1 + # try c_2 - 1 + valid = True + b = c_(2) - 1 + if b in {0, -1}: + valid = False + else: + alpha = c_(2) / (1 + b) - 1 + beta = b_(0) / q_binomial(d, 1, b) + valid = check_parameters(d, b, alpha, beta, array) + + if not valid: # must have -a_1 - 1 + b = -a_(1) - 1 + alpha = c_(2) / (1 + b) - 1 + beta = a_(1) + 1 - alpha*(q_binomial(d, 1, b) - 1) + print(f"b = {b} and alpha = {alpha} and beta = {beta}") + + if not check_parameters(d, b, alpha, beta, array): + print("check params failed") + return False + + gamma = ClassicalParametersGraph.NonExisting + + if b == 1 : + if alpha == 1 and beta >= d: # since beta+d = n >= 2*d + # Johnson Graph + gamma = ClassicalParametersGraph.Johnson + elif alpha == 0: + # Hamming Graph + gamma = ClassicalParametersGraph.Hamming + elif alpha == 2 and (beta == 2*d + 1 or beta == 2*d - 1): + # Halved cube graph + gamma = ClassicalParametersGraph.HalvedCube + else : + return False # no other (unbounbded) drg exists with b = 1 + + elif b < 0 and is_prime_power(-b): + if alpha + 1 == (1 + b*b) / (1 + b) and \ + beta + 1 == (1 - b**(d+1)) / (1 + b): + # U(2d,r) + gamma = ClassicalParametersGraph.UnitaryDualPolar1 + elif alpha + 1 == b and beta + 1 == - (b**d): + gamma = ClassicalParametersGraph.HermitianForms + elif d == 3 and alpha + 1 == 1 / (1+b) and \ + beta + 1 == q_binomial(3, 1, -b): + gamma = ClassicalParametersGraph.GeneralisedHexagon + else: + return False + + if gamma != ClassicalParametersGraph.NonExisting: + return (d, b, alpha, beta, gamma) + + # all remaining cases need b to be a prime power + (p, k) = is_prime_power(b, get_data=True) + if k == 0: # b not a prime power + return False + r = p**(k//2) # will be used later + + if alpha == b and integral_log((beta+1) * (b-1) + 1, b) >= d + 1: + # we checked that beta + 1 = (b^(n-d+1) - 1)/(b - 1) for n >= 2d + # Grassmann graph + gamma = ClassicalParametersGraph.Grassmann + + elif alpha == 0 and beta * beta in {1, b, b * b, b**3, b**4}: + # checked beta in {b^0, b^(0.5), b, b^(1.5), b^2} + # dual polar graphs + if beta == 1: + gamma = ClassicalParametersGraph.OrthogonalDualPolar1 + elif beta == b: + gamma = ClassicalParametersGraph.SymplecticDualPolar + elif beta == b * b: + gamma = ClassicalParametersGraph.OrthogonalDualPolar2 + else: + if k % 2 == 1: + return False # we need b a square + if beta == r**3: + gamma = ClassicalParametersGraph.UnitaryDualPolar1 + elif beta == r: + gamma = ClassicalParametersGraph.UnitaryDualPolar2 + + elif k % 2 == 0 and alpha + 1 == q_binomial(3, 1, r) and \ + beta + 1 in {q_binomial(2*d + 2, 1, r), + q_binomial(2*d + 1, 1, r)}: + gamma = ClassicalParametersGraph.Ustimenko + + elif alpha + 1 == b and integral_log(beta + 1, b) >= d: + gamma = ClassicalParametersGraph.BilinearForms + + elif k % 2 == 0 and alpha + 1 == b and \ + beta + 1 in {r**(2*d - 1),r**(2*d + 1)}: + gamma = ClassicalParametersGraph.AlternatingForms + + elif d == 3 and k % 4 == 0 and alpha + 1 == q_binomial(5, 1, p**(k//4)) and \ + beta + 1 == q_binomial(10, 1, p**(k//4)): + gamma = ClassicalParametersGraph.LieE77 + + elif d == 3 and k % 4 == 0 and alpha + 1 == b and beta + 1 == (p**(k//4))**9: + gamma = ClassicalParametersGraph.AffineE6 + + if gamma == ClassicalParametersGraph.NonExisting: + return False + return (d, b, alpha, beta, gamma) + +def graph_with_classical_parameters(int d, int b, alpha_in, beta_in, int gamma): + r""" + Return the graph with the classical parameters given. + + The last parameter ``gamma`` is meant to be an element of the enum + ``ClassicalParametersGraph`` used to identify the family of graphs to + construct. + In particular this function doesn't build any sporadic graph. + To build such a graph use + :func:`sage.graphs.generators.distance_regular.distance_regular_graph`. + + INPUT: + + - ``d, b, alpha_in, beta_in`` -- numbers; the parameters of the graph; + ``d`` and ``b`` must be integers + + - ``gamma`` -- element of the enum ``ClassicalParametersGraph`` + + EXAMPLES:: + + REFERENCES: + + TESTS:: + """ + from sage.rings.rational import Rational + from sage.functions.log import log + from sage.functions.other import sqrt + from sage.graphs.generators.families import JohnsonGraph, HammingGraph + from sage.graphs.generators.classical_geometries import \ + UnitaryDualPolarGraph, OrthogonalDualPolarGraph, SymplecticDualPolarGraph + + alpha = Rational(alpha_in) + beta = Rational(beta_in) + if alpha.is_integer(): + alpha = int(alpha) + if beta.is_integer(): + beta = int(beta) + + if gamma == ClassicalParametersGraph.Johnson: + return JohnsonGraph(beta + d, d) + + elif gamma == ClassicalParametersGraph.Hamming: + return HammingGraph(d, beta + 1) + + elif gamma == ClassicalParametersGraph.HalvedCube: + a = 0 if beta == 2*d + 1 else 1 + return HalfCube(beta + a) + + elif gamma == ClassicalParametersGraph.UnitaryDualPolar: + return UnitaryDualPolarGraph(2 * d, -b) + + elif gamma == ClassicalParametersGraph.HermitianForms: + return HermitianFormsGraph(d,(-b)**2) + + elif gamma == ClassicalParametersGraph.GeneralisedHexagon: + q = -b + return GeneralisedHexagonGraph(q, q**3) + + elif gamma == ClassicalParametersGraph.Grassmann: + n = int(log((beta+1) * (b-1) + 1, b)) + d -1 + return GrassmannGraph(b, n, d) + + elif gamma == ClassicalParametersGraph.OrthogonalDualPolar1: + return OrthogonalDualPolarGraph(1, d, b) + + elif gamma == ClassicalParametersGraph.SymplecticDualPolar: + return SymplecticDualPolarGraph(2 * d, b) + + elif gamma == ClassicalParametersGraph.OrthogonalDualPolar2: + return OrthogonalDualPolarGraph(-1, d, b) + + elif gamma == ClassicalParametersGraph.UnitaryDualPolar1: + r = int(sqrt(b)) + return UnitaryDualPolarGraph(2*d + 1, r) + + elif gamma == ClassicalParametersGraph.UnitaryDualPolar2: + r = int(sqrt(b)) + return UnitaryDualPolarGraph(2 * d, r) + + elif gamma == ClassicalParametersGraph.Ustimenko: + q = int(sqrt(b)) + m = int(log((beta+1) * (q-1) + 1, q)) - 1 + UstimenkoGraph(m, q) + + elif gamma == ClassicalParametersGraph.BilinearForms: + e = int(log(beta + 1, b)) + return BilinearFormsGraph(d, e, b) + + elif gamma == ClassicalParametersGraph.AlternatingForms: + q = int(sqrt(b)) + a = 0 if beta + 1 == q**(2*d - 1) else 1 + return AlternatingFormsGraph(2*d + a, q) + + elif gamma == ClassicalParametersGraph.LieE77 or \ + gamma == ClassicalParametersGraph.AffineE6: + raise NotImplementedError("Graph would be too big") + + raise ValueError("Incorrect family of graphs") + + # dictionary intersection_array (as tuple) -> construction # of spordaic distance-regular graphs from sage.graphs.generators.smallgraphs import (FosterGraph, BiggsSmithGraph, @@ -1769,16 +2117,19 @@ _sporadic_graph_database = { shortened_00_11_binary_Golay_code_graph, (21, 20, 16, 9, 2, 1, 1, 2, 3, 16, 20, 21) : \ shortened_000_111_extended_binary_Golay_code_graph, - (22, 21, 20, 3, 2, 1, 1, 2, 3, 20, 21, 22) : \ - codes.GolayCode(GF(2), extended=False).shortened([0]).cosetGraph, + (22, 21, 20, 3, 2, 1, 1, 2, 3, 20, 21, 22) : lambda : \ + codes.GolayCode(GF(2), extended=False).shortened([0]).cosetGraph(), (3, 2, 1, 1, 1, 1, 1, 1, 2, 3) : DodecahedralGraph, - (22, 20, 18, 2, 1, 1, 2, 9, 20, 22) : \ - codes.GolayCode(GF(3)).shortened([0]).cosetGraph, - (7, 6, 6, 1, 1, 1, 1, 6, 6, 7) : HoffmanSingletonGraph().bipartite_double, - (10, 9, 8, 2, 1, 1, 2, 8, 9, 10) : SimsGewirtzGraph().bipartite_double, - (16, 15, 12, 4, 1, 1, 4, 12, 15, 16) : \ - strongly_regular_graph(77, 16, 0, check=False).bipartite_double, - (22, 21, 16, 6, 1, 1, 6, 16, 21, 22) : HigmanSimsGraph().bipartite_double, + (22, 20, 18, 2, 1, 1, 2, 9, 20, 22) : lambda : \ + codes.GolayCode(GF(3)).shortened([0]).cosetGraph(), + (7, 6, 6, 1, 1, 1, 1, 6, 6, 7) : lambda : \ + HoffmanSingletonGraph().bipartite_double(), + (10, 9, 8, 2, 1, 1, 2, 8, 9, 10) : lambda : \ + SimsGewirtzGraph().bipartite_double(), + (16, 15, 12, 4, 1, 1, 4, 12, 15, 16) : lambda : \ + strongly_regular_graph(77, 16, 0, check=False).bipartite_double(), + (22, 21, 16, 6, 1, 1, 6, 16, 21, 22) : lambda : \ + HigmanSimsGraph().bipartite_double(), (3, 2, 2, 1, 1, 1, 1, 2) : CoxeterGraph, (6, 5, 5, 4, 1, 1, 2, 6) : LintSchrijverGraph, (7, 6, 4, 4, 1, 1, 1, 6) : DoublyTruncatedWittGraph, @@ -1788,24 +2139,28 @@ _sporadic_graph_database = { (5, 4, 1, 1, 1, 1, 4, 5) : WellsGraph, (6, 4, 2, 1, 1, 1, 4, 6) : FosterGraph3S6, (10, 6, 4, 1, 1, 2, 6, 10) : ConwaySmith_for_3S7, - (20, 18, 4, 1, 1, 2, 18, 20) : \ - codes.GolayCode(GF(3), extended=False).shortened([0]).cosetGraph, + (20, 18, 4, 1, 1, 2, 18, 20) : lambda : \ + codes.GolayCode(GF(3), extended=False).shortened([0]).cosetGraph(), (45, 32, 12, 1, 1, 6, 32, 45) : locally_GQ42_distance_transitive_graph, (117, 80, 24, 1, 1, 12, 80, 117) : graph_3O73, - (22, 21, 20, 1, 2, 6): \ - codes.GolayCode(GF(2), extended=False).punctured([0]).cosetGraph, - (23, 22, 21, 1, 2, 3): codes.GolayCode(GF(2), extended=False).cosetGraph, - (24, 23, 22, 21, 1, 2, 3, 24): codes.GolayCode(GF(2)).cosetGraph, + (22, 21, 20, 1, 2, 6): lambda : \ + codes.GolayCode(GF(2), extended=False).punctured([0]).cosetGraph(), + (23, 22, 21, 1, 2, 3): lambda : \ + codes.GolayCode(GF(2), extended=False).cosetGraph(), + (24, 23, 22, 21, 1, 2, 3, 24): lambda : codes.GolayCode(GF(2)).cosetGraph(), (12, 11, 10, 7, 1, 2, 5, 12): LeonardGraph, (15, 14, 10, 3, 1, 5, 12, 15): cocliques_HoffmannSingleton, (27, 10, 1, 1, 10, 27): GossetGraph, (30, 28, 24, 1, 3, 15): LargeWittGraph, (15, 14, 12, 1, 1, 9): TruncatedWittGraph, - (24, 22, 20, 1, 2, 12): codes.GolayCode(GF(3)).cosetGraph, - (21, 20, 16, 1, 2, 12): \ - codes.GolayCode(GF(2), extended=False).punctured([0, 1]).cosetGraph + (24, 22, 20, 1, 2, 12): lambda : codes.GolayCode(GF(3)).cosetGraph(), + (21, 20, 16, 1, 2, 12): lambda : \ + codes.GolayCode(GF(2), extended=False).punctured([0, 1]).cosetGraph() } +_infinite_families_database = [ + (is_classical_parameters_graph, graph_with_classical_parameters)] + def distance_regular_graph(list arr, existence=False, check=True): r""" Return a distance-regular graph with the intersection array given. @@ -1926,6 +2281,15 @@ def distance_regular_graph(list arr, existence=False, check=True): return True return result(_sporadic_graph_database[t]()) + for (f, g) in _infinite_families_database: + t = f(arr) + if t is not False: + if existence: + return True + + G = g(*t) if is_iterable(t) else g(t) + return result(G) + # now try drg feasibility if drgModule: try: From 1d075156ba7b8675219d47e8da8b984e60e08803 Mon Sep 17 00:00:00 2001 From: Ivo Maffei Date: Tue, 18 Aug 2020 13:40:04 +0200 Subject: [PATCH 056/713] finished docstrings; fixed bugs --- .../graphs/generators/distance_regular.pyx | 119 ++++++++++++------ 1 file changed, 80 insertions(+), 39 deletions(-) diff --git a/src/sage/graphs/generators/distance_regular.pyx b/src/sage/graphs/generators/distance_regular.pyx index c6ec0697abe..a5c293862e2 100644 --- a/src/sage/graphs/generators/distance_regular.pyx +++ b/src/sage/graphs/generators/distance_regular.pyx @@ -1773,12 +1773,12 @@ def is_classical_parameters_graph(list array): Return a tuple of parameters representing the array given. If such no tuple can be produced, it returns ``False``. - Given an intersection array, if it represents a family of distance-regular - graphs with classical parameters, then this function returns a tuple - consisting of the parameters `(d, b, \alpha, \beta)` and a fourth parameter - which is the enum ``CalssicalParametersGraph`` indicating the family with + Given an intersection array, if it represents a family of distance-regular + graphs with classical parameters, then this function returns a tuple + consisting of the parameters `(d, b, \alpha, \beta)` and a fourth parameter + which is the enum ``CalssicalParametersGraph`` indicating the family with the given itersection array. - If the array doesn't belong to any classical parameter graph, then this + If the array doesn't belong to any classical parameter graph, then this function returns ``False``. If the array belongs to a sporadic graph rather than a family of graphs, then the function returns ``False``. This is to reduce the overlap with @@ -1825,7 +1825,6 @@ def is_classical_parameters_graph(list array): ([27, 10, 1, None], [None, 1, 10, 27]) sage: is_classical_parameters_graph([27, 10, 1, 1, 10, 27]) False - """ from sage.functions.log import log from sage.rings.integer_ring import ZZ @@ -1846,29 +1845,28 @@ def is_classical_parameters_graph(list array): (beta - alpha * q_binomial(i, 1, b)) for i in range(d)] cs = [q_binomial(i, 1, b) * (1 + alpha*q_binomial(i-1, 1, b)) for i in range(1, d+1)] - print(bs + cs) return arr == bs + cs def b_(i): if i == d: return 0 return array[i] - + def c_(i): if i == 0: return 0 return array[d - 1 + i] - + def a_(i): return b_(0) - b_(i) - c_(i) if len(array) % 2 != 0 : return False - + d = len(array) // 2 if d < 3: return False - + # if array is classical parameters, then we have 2 cases: # 1. a_i != \lambda c_i for 2<= i <= d # 2. a_i == \lambda c_i for 0<= i <= d @@ -1879,7 +1877,6 @@ def is_classical_parameters_graph(list array): else: return False - print(f"case : {case}") if case == 1: # b = (a_2 c_3 - c_2 a_3) / (a_1 c_3 - a_3) num = a_(2) * c_(3) - c_(2) * a_(3) @@ -1887,7 +1884,7 @@ def is_classical_parameters_graph(list array): b = num / den if b in {0, -1}: return False - + alpha = c_(2) / (1 + b) - 1 beta = b_(0) / q_binomial(d, 1, b) @@ -1907,14 +1904,12 @@ def is_classical_parameters_graph(list array): b = -a_(1) - 1 alpha = c_(2) / (1 + b) - 1 beta = a_(1) + 1 - alpha*(q_binomial(d, 1, b) - 1) - print(f"b = {b} and alpha = {alpha} and beta = {beta}") if not check_parameters(d, b, alpha, beta, array): - print("check params failed") return False - + gamma = ClassicalParametersGraph.NonExisting - + if b == 1 : if alpha == 1 and beta >= d: # since beta+d = n >= 2*d # Johnson Graph @@ -1954,7 +1949,7 @@ def is_classical_parameters_graph(list array): # we checked that beta + 1 = (b^(n-d+1) - 1)/(b - 1) for n >= 2d # Grassmann graph gamma = ClassicalParametersGraph.Grassmann - + elif alpha == 0 and beta * beta in {1, b, b * b, b**3, b**4}: # checked beta in {b^0, b^(0.5), b, b^(1.5), b^2} # dual polar graphs @@ -1971,15 +1966,15 @@ def is_classical_parameters_graph(list array): gamma = ClassicalParametersGraph.UnitaryDualPolar1 elif beta == r: gamma = ClassicalParametersGraph.UnitaryDualPolar2 - + elif k % 2 == 0 and alpha + 1 == q_binomial(3, 1, r) and \ beta + 1 in {q_binomial(2*d + 2, 1, r), q_binomial(2*d + 1, 1, r)}: gamma = ClassicalParametersGraph.Ustimenko - + elif alpha + 1 == b and integral_log(beta + 1, b) >= d: gamma = ClassicalParametersGraph.BilinearForms - + elif k % 2 == 0 and alpha + 1 == b and \ beta + 1 in {r**(2*d - 1),r**(2*d + 1)}: gamma = ClassicalParametersGraph.AlternatingForms @@ -1990,7 +1985,7 @@ def is_classical_parameters_graph(list array): elif d == 3 and k % 4 == 0 and alpha + 1 == b and beta + 1 == (p**(k//4))**9: gamma = ClassicalParametersGraph.AffineE6 - + if gamma == ClassicalParametersGraph.NonExisting: return False return (d, b, alpha, beta, gamma) @@ -2000,10 +1995,10 @@ def graph_with_classical_parameters(int d, int b, alpha_in, beta_in, int gamma): Return the graph with the classical parameters given. The last parameter ``gamma`` is meant to be an element of the enum - ``ClassicalParametersGraph`` used to identify the family of graphs to + ``ClassicalParametersGraph`` used to identify the family of graphs to construct. In particular this function doesn't build any sporadic graph. - To build such a graph use + To build such a graph use :func:`sage.graphs.generators.distance_regular.distance_regular_graph`. INPUT: @@ -2015,9 +2010,51 @@ def graph_with_classical_parameters(int d, int b, alpha_in, beta_in, int gamma): EXAMPLES:: + sage: from sage.graphs.generators.distance_regular import * + sage: graph_with_classical_parameters(3, 1, 1, 3, 1) + Johnson graph with parameters 6,3: Graph on 20 vertices + + The last parameter is very important as it takes precedence. + This function will not check that the other four parameters match the correct + family. Use + :func:`sage.graphs.generators.distance_regular.is_classical_parameters_graph` + to check the parameters:: + + sage: from sage.graphs.generators.distance_regular import * + sage: graph_with_classical_parameters(3, 1, 1, 3, 2) + Hamming Graph with parameters 3,4: Graph on 64 vertices + sage: G = _; G.is_distance_regular(True) + ([9, 6, 3, None], [None, 1, 2, 3]) + sage: is_classical_parameters_graph([9, 6, 3, 1, 2, 3]) + (3, 1, 0, 3, 2) + + Two families of graphs are not implemented yet:: + + sage: from sage.graphs.generators.distance_regular import * + sage: graph_with_classical_parameters(3, 16, 15, 511, 17) + Traceback (most recent call last): + ... + NotImplementedError: Graph would be too big + sage: graph_with_classical_parameters(3, 16, 30, 1022, 16) + Traceback (most recent call last): + ... + NotImplementedError: Graph would be too big + REFERENCES: + See [BCN1989]_ chapter 9 for a discussion of distance-regular graphs with + classical parameters. See also [VDKT2016]_ section 3.1.1. + TESTS:: + + sage: graph_with_classical_parameters(3, 1, 2, 3, 3) + Half 4 Cube: Graph on 8 vertices + sage: graph_with_classical_parameters(3, 2, 0, 2, 9) + Symplectic Dual Polar Graph DSp(6, 2): Graph on 135 vertices + sage: graph_with_classical_parameters(3, 2, 2, 14, 7) + Grassmann graph J_2(6, 3): Graph on 1395 vertices + sage: graph_with_classical_parameters(3, -2, -2, 6, 6) + Generalised hexagon of order (2, 8): Graph on 819 vertices """ from sage.rings.rational import Rational from sage.functions.log import log @@ -2035,54 +2072,54 @@ def graph_with_classical_parameters(int d, int b, alpha_in, beta_in, int gamma): if gamma == ClassicalParametersGraph.Johnson: return JohnsonGraph(beta + d, d) - + elif gamma == ClassicalParametersGraph.Hamming: return HammingGraph(d, beta + 1) - + elif gamma == ClassicalParametersGraph.HalvedCube: a = 0 if beta == 2*d + 1 else 1 return HalfCube(beta + a) - + elif gamma == ClassicalParametersGraph.UnitaryDualPolar: return UnitaryDualPolarGraph(2 * d, -b) - + elif gamma == ClassicalParametersGraph.HermitianForms: return HermitianFormsGraph(d,(-b)**2) - + elif gamma == ClassicalParametersGraph.GeneralisedHexagon: q = -b return GeneralisedHexagonGraph(q, q**3) - + elif gamma == ClassicalParametersGraph.Grassmann: n = int(log((beta+1) * (b-1) + 1, b)) + d -1 return GrassmannGraph(b, n, d) - + elif gamma == ClassicalParametersGraph.OrthogonalDualPolar1: return OrthogonalDualPolarGraph(1, d, b) - + elif gamma == ClassicalParametersGraph.SymplecticDualPolar: return SymplecticDualPolarGraph(2 * d, b) - + elif gamma == ClassicalParametersGraph.OrthogonalDualPolar2: return OrthogonalDualPolarGraph(-1, d, b) - + elif gamma == ClassicalParametersGraph.UnitaryDualPolar1: r = int(sqrt(b)) return UnitaryDualPolarGraph(2*d + 1, r) - + elif gamma == ClassicalParametersGraph.UnitaryDualPolar2: r = int(sqrt(b)) return UnitaryDualPolarGraph(2 * d, r) - + elif gamma == ClassicalParametersGraph.Ustimenko: q = int(sqrt(b)) m = int(log((beta+1) * (q-1) + 1, q)) - 1 UstimenkoGraph(m, q) - + elif gamma == ClassicalParametersGraph.BilinearForms: e = int(log(beta + 1, b)) return BilinearFormsGraph(d, e, b) - + elif gamma == ClassicalParametersGraph.AlternatingForms: q = int(sqrt(b)) a = 0 if beta + 1 == q**(2*d - 1) else 1 @@ -2210,7 +2247,11 @@ def distance_regular_graph(list arr, existence=False, check=True): sage: graphs.distance_regular_graph([3, 2, 2, 1, 2, 1, 1, 2, 2, 3], ....: existence=True) False - + sage: graphs.distance_regular_graph([18, 16, 16, 1, 1, 9]) # optional - internet gap_packages + Generalised hexagon of order (2, 8): Graph on 819 vertices + sage: graphs.distance_regular_graph([14, 12, 10, 8, 6, 4, 2, + ....: 1, 2, 3, 4, 5, 6, 7]) + Hamming Graph with parameters 7,3: Graph on 2187 vertices """ from sage.misc.unknown import Unknown from sage.categories.sets_cat import EmptySetError From ed501d320c014109deff60579851ca9f05b0a8e9 Mon Sep 17 00:00:00 2001 From: Ivo Maffei Date: Tue, 18 Aug 2020 14:10:06 +0200 Subject: [PATCH 057/713] fix another bug --- src/sage/graphs/generators/distance_regular.pyx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/sage/graphs/generators/distance_regular.pyx b/src/sage/graphs/generators/distance_regular.pyx index a5c293862e2..cf985654877 100644 --- a/src/sage/graphs/generators/distance_regular.pyx +++ b/src/sage/graphs/generators/distance_regular.pyx @@ -1820,7 +1820,7 @@ def is_classical_parameters_graph(list array): ....: is_classical_parameters_graph sage: is_classical_parameters_graph([68, 64, 1, 17]) # srg not drg False - sage: G = gaphs.GossetGraph() # sporadic classical parameters graph + sage: G = graphs.GossetGraph() # sporadic classical parameters graph sage: G.is_distance_regular(True) ([27, 10, 1, None], [None, 1, 10, 27]) sage: is_classical_parameters_graph([27, 10, 1, 1, 10, 27]) @@ -1902,6 +1902,9 @@ def is_classical_parameters_graph(list array): if not valid: # must have -a_1 - 1 b = -a_(1) - 1 + if b in {0, -1}: + return False # even this case is invalid + alpha = c_(2) / (1 + b) - 1 beta = a_(1) + 1 - alpha*(q_binomial(d, 1, b) - 1) From c64c5ecb4cd1526d234d6daffd5ab14f49c21921 Mon Sep 17 00:00:00 2001 From: Ivo Maffei Date: Fri, 21 Aug 2020 12:18:38 +0200 Subject: [PATCH 058/713] added psuedo partition graphs --- .../graphs/generators/distance_regular.pyx | 153 +++++++++++++++++- 1 file changed, 152 insertions(+), 1 deletion(-) diff --git a/src/sage/graphs/generators/distance_regular.pyx b/src/sage/graphs/generators/distance_regular.pyx index cf985654877..0e767d12f64 100644 --- a/src/sage/graphs/generators/distance_regular.pyx +++ b/src/sage/graphs/generators/distance_regular.pyx @@ -2134,6 +2134,153 @@ def graph_with_classical_parameters(int d, int b, alpha_in, beta_in, int gamma): raise ValueError("Incorrect family of graphs") +def is_pseudo_partition_graph(list arr): + r""" + Return `(m, a)` if the intersection array given satisfy: + `b_i = (m - i)(1 + a(m - 1 - i))` for `0 \leq i < d` + `c_i = i(1 + a(i - 1))` for `0 \leq i < d` + `c_d = (2d + 2 - m) d (1 + a(d - 1))` where `d` is the diameter of the graph. + + If such pair `(m, a)` doesn't exist or the diameter is less than 3, then + this function returns ``False``. + + These graphs are called pseudo partition graphs in [BCN1989]_ chapter 6.3. + + INPUT: + + - ``arr`` -- list; intersection array + + OUTPUT: + + A pair `(m, a)` of integers or ``False`` if such pair doesn't exist. + + EXAMPLES:: + + sage: from sage.graphs.generators.distance_regular import * + sage: is_pseudo_partition_graph([36, 25, 16, 1, 4, 18]) + (6, 1) + sage: pseudo_partition_graph(6, 1) + Folded Johnson graph with parameters 12,6: Graph on 462 vertices + sage: _.is_distance_regular(True) + ([36, 25, 16, None], [None, 1, 4, 18]) + + REFERENCE: + + See [BCN1989]_ pp. 197, 198 or [VDKT2016]_ pp. 38, 39. + + TESTS:: + + sage: from sage.graphs.generators.distance_regular import * + sage: is_pseudo_partition_graph([]) + False + sage: is_pseudo_partition_graph([36, 25, 16, 1, 0, 18]) + False + sage: is_pseudo_partition_graph([217, 156, 105, 1, 12, 33]) + (7, 5) + sage: pseudo_partition_graph(7, 5) + Traceback (most recent call last): + ... + ValueError: No known graph exists + """ + d = len(arr) + if d % 2 != 0: + return False + + d = d // 2 + + if d < 3 : + return False + + # c_2 = 2 (1+a) + c2 = arr[d+1] + if c2 % 2 != 0: + return False + a = c2//2 - 1 + + cd = arr[2*d - 1] + K = d * (1+a * (d-1)) + if cd % K != 0: + return False + + gamma = cd // K + m = 2*d + 2 - gamma + + # we must have m = 2*d or 2*d +1 + if m not in {2*d, 2*d + 1}: + return False + + newArr = [(m-i) * (1 + a * (m-1-i)) for i in range(d)] + \ + [i * (1 + a * (i-1)) for i in range(1, d)] + \ + [(2*d + 2 - m) * d * (1 + a * (d-1))] + + if arr == newArr: + return (m, a) + + return False + +def pseudo_partition_graph(int m, int a): + r""" + Return a pseudo partition graph with the given parameters. + + A graph is a pseudo partition graph if it is distance-regular with + diameter at least 3 and whose intersection numbers satisfy: + `b_i = (m - i)(1 + a(m - 1 - i))` for `0 \leq i < d` + `c_i = i(1 + a(i - 1))` for `0 \leq i < d` + `c_d = (2d + 2 - m) d (1 + a(d - 1))` where `d` is the diameter of the graph. + + INPUT: + + - ``m, a`` -- integers; parameters of the graph + + EXAMPLES:: + + sage: from sage.graphs.generators.distance_regular import * + sage: pseudo_partition_graph(6, 1) + Folded Johnson graph with parameters 12,6: Graph on 462 vertices + + Not all graphs built with this function are psuedo partition graphs as + intended by + :func:`sage.graphs.generators.distance_regular.is_pseudo_partition_graph`, + since that function requires the diameter to be at least 3:: + + sage: from sage.graphs.generators.distance_regular import * + sage: pseudo_partition_graph(3, 1) + Folded Johnson graph with parameters 6,3: Graph on 10 vertices + sage: G=_; G.is_distance_regular(True) + ([9, None], [None, 1]) + sage: is_pseudo_partition_graph([9, 1]) + False + + REFERENCES: + + See [BCN1989]_ pp. 197, 198 or [VDKT2016]_ pp. 38, 39 for a discussion of + known pseudo partition graphs. + + TESTS:: + + sage: from sage.graphs.generators.distance_regular import * + sage: pseudo_partition_graph(3, 3) + Traceback (most recent call last): + ... + ValueError: No known graph exists + sage: pseudo_partition_graph(8, 0).is_distance_regular(True) + ([8, 7, 6, 5, None], [None, 1, 2, 3, 8]) + sage: pseudo_partition_graph(6, 2).is_distance_regular(True) + ([66, 45, 28, None], [None, 1, 6, 30]) + """ + from sage.graphs.generators.families import JohnsonGraph, FoldedCubeGraph + from sage.graphs.bipartite_graph import BipartiteGraph + + if a == 0: + return FoldedCubeGraph(m) + elif a == 1: + return JohnsonGraph(2 * m, m).folded_graph() + elif a == 2: + return BipartiteGraph(FoldedCubeGraph(2 * m)).project_left() + + raise ValueError("No known graph exists") + + # dictionary intersection_array (as tuple) -> construction # of spordaic distance-regular graphs @@ -2199,7 +2346,9 @@ _sporadic_graph_database = { } _infinite_families_database = [ - (is_classical_parameters_graph, graph_with_classical_parameters)] + (is_classical_parameters_graph, graph_with_classical_parameters), + (is_pseudo_partition_graph, pseudo_partition_graph) +] def distance_regular_graph(list arr, existence=False, check=True): r""" @@ -2255,6 +2404,8 @@ def distance_regular_graph(list arr, existence=False, check=True): sage: graphs.distance_regular_graph([14, 12, 10, 8, 6, 4, 2, ....: 1, 2, 3, 4, 5, 6, 7]) Hamming Graph with parameters 7,3: Graph on 2187 vertices + sage: graphs.distance_regular_graph([66, 45, 28, 1, 6, 30]) + Graph on 1024 vertices """ from sage.misc.unknown import Unknown from sage.categories.sets_cat import EmptySetError From db9525a6405e57f62a04779d0ecacbaee9b98390 Mon Sep 17 00:00:00 2001 From: Ivo Maffei Date: Sat, 22 Aug 2020 12:02:03 +0200 Subject: [PATCH 059/713] initial sketch for near polygon functions --- .../graphs/generators/distance_regular.pyx | 196 ++++++++++++++++++ 1 file changed, 196 insertions(+) diff --git a/src/sage/graphs/generators/distance_regular.pyx b/src/sage/graphs/generators/distance_regular.pyx index 0e767d12f64..cb39eebd351 100644 --- a/src/sage/graphs/generators/distance_regular.pyx +++ b/src/sage/graphs/generators/distance_regular.pyx @@ -2280,6 +2280,202 @@ def pseudo_partition_graph(int m, int a): raise ValueError("No known graph exists") +cdef enum NearPolygonGraph: + RegularPolygon, + GeneralisedPolygon, + OddGraph + +def new_is_near_polygon(array): + r""" + Return a pair + """ + + + if len(array) % 2 != 0: + return False + + d = len(array) // 2 + + k = array[0] + l = k - array[1] - 1 + + if l < 0: + return False + + if any(array[i] != k - (l + 1)*array[d - 1 + i] for i in range(1, d)) or \ + k - (l + 1)*array[2*d - 1] != 0: + return False + + # additional checks + if k < (l+1) * array[2*d - 1] or k % (l + 1) != 0: + return False + + # check if it is known example + if k == (l+1) * array[2*d - 1] and \ + all(array[d + i] == 1 for i in range(d-1)) and \ + l + 1 > 1 and array[2*d - 1] - 1 > 1: + # generalised polygon + s = l+1 + t = array[2*d - 1] - 1 + + # check we know the pair (s, t) + if s == 1 and t == 1: + # normal polygon + return (NearPolygonGraph.RegularPolygon, d) + + if (d, s, t) in {(3, 2, 2), (3, 3, 3), (3, 4, 4), (3, 5, 5), (3, 2, 8), + (3, 8, 2), (3, 3, 27), (3, 27, 3), (4, 2, 4), + (6, 1, 2), (6, 1, 3), (6, 1, 4), (6, 1, 5), (6, 2, 1), + (6, 3, 1), (6, 4, 1), (6, 5, 1)}: + # we know the gen pol + return (NearPolygonGraph.GenralisedPolygon, d, s, t) + + if d == 3 and (s == 1 or t == 1) and is_pime_power(s * t): + # we know it + return (NearPolygonGraph.GenralisedPolygon, d, s, t) + + if d == 4 and (s == 1 or t == 1): + q = s * t + if strongly_regular_graph((q+1) * (q*q + 1), q * (q+1), q-1, q+1, + existence=True): + # we know it + return (NearPolygonGraph.GenralisedPolygon, d, s, t) + + # otherwise not known generalised polygon + return False + + n = 2 * d if k == (l+1) * array[2*d - 1] else 2*d + 1 + + if l == 0 and k == d + 1 and n == 2*d + 1 and \ + all(array[d + i] == (i + 2) // 2 for i in range(d)): + # odd graph + return (NearPolygonGraph.OddGraph, n) + + + +def is_near_polygon(list arr): + r""" + Checks if the intersection array could be of a near polygon. if so returns a parameter l, otherwise -1 + + p199 theorem 6.4.1: + a dist-reg graph with int. arr [b_0,...,b_{d-1}, c_1, ..., c_d] is a regular near polygon + iff there is no induced subgraph K_{1,1,2} and there is l s.t. b_i = k - (l+1)*c_i for i = 0,..,d-1. + + In particular, if it is a near polygon, then is a near 2d-gon if k = (l+1)*c_d and a near (2d+1)-gon otherwise + """ + def is_generalised_2d_gon(a,d): + #c_1,...,c_{d-1} = 1 + for i in range(1,d): + if a[d+i-1] != 1: return False + + t = a[2*d-1] -1 #c_d-1 + + # b_0 = s(t+1) + if a[0] % (t+1) != 0: return False + s = a[0] // (t+1) + + if not is_prime_power(s*t):#this also rules out (1,1) + return False + + if d == 3: + if s == 1 or t == 1: + #order (1,q) or (q,1) + return True + elif s == t and s in {2,3,4,5}: + #order (q,q) + return True + elif (s==t**3 or t == s**3) and s*t in {16,81}: + #order (q,q^3) or (q^3,q); q is 2 or 3 + return True + return False + elif d == 4: + if s== 1 or t == 1: + #order (1,q) or (q,1) + q= s*t + if strongly_regular_graph((q+1)*(q*q+1),q*(q+1),q-1,q+1,existence=True): + return True + elif s==t*t or s*s == t and s*t == 8: + #order (q,q^2) we can only do q=2 + return True + return False + elif d == 6: + if (s==1 or t == 1) and s*t in {2,3,4,5}: + #order (1,q); rely on hexagon (q,q) + return True + + return False + + #gamma indicates what graph we have + # gamma -> graph + # 0 -> gen polygon + # 1 -> polygon + # 2 -> Odd graph + # 3 -> 2xGrassmann + # 4 -> 2xodd + # 5 -> folded cube + + d = len(arr) + if d % 2 != 0: + return False + + d = d // 2 + if d < 3: + return False + + k = arr[0] + l = k - arr[1] - 1 # b_1 = k - (l+1) + if l < 0: return False + + #for i = 0 we have b_0 = k - (l+1)*c_0 = k since c_0 = 0 always + for i in range(1,d): + if arr[i] != k - (l+1)*arr[d+i-1]: #b_i = k - (l+1)c_i + return False + + #chek k >= (l+1)c_d + if k < (l+1)*arr[2*d-1]: + return False + + #now find if we can build this graph + n = 2*d if k == (l+1)*arr[2*d-1] else 2*d+1 + cs = arr[d:] + + #is generalised polygon? + if is_generalised_2d_gon(arr,d): + t = arr[2*d-1]-1 + s = arr[0]//(t+1) + return (0,(d,s,t)) + + if l != 0: return False#classial parameters; don't deal it here + + #now l==0 + if k== 2 and cs == ([1 for i in range(1,d)]+[2*d+2-n]): + #polygon + return (1,n) + + if n == 2*d+1 and k == d+1 and cs == [(i+1)//2 for i in range(1,d+1)]: + #odd graph + return (2,d+1) + + if ( n == 2*d and d%2 == 1 and is_prime_power(cs[2]-1) and + k == q_binomial((d-1)//2+1, 1,cs[2]-1) and#cs[2]=c_3 = q_binom(2,1,q) = q+1 + cs == [q_binomial((i+1)//2,1,cs[2]-1) for i in range(1,d+1)]): + #double grassman + e = (d-1)//2 + q = cs[2]-1 + return (3,(q,e)) + + if ( n==2*d and d%2 == 1 and k -1 == (d-1)//2 and + cs == [(i+1)//2 for i in range(1,d+1)] ): + #double odd + e = (d-1)//2 + return (4,e) + + if k == n and cs == ([i for i in range(1,d)]+[d*(2*d+2-n)]): + #Folded cube + return (5,n) + + return False + # dictionary intersection_array (as tuple) -> construction From 085d36e9b07549eae631804285256bcbc4b04222 Mon Sep 17 00:00:00 2001 From: Ivo Maffei Date: Sat, 22 Aug 2020 12:49:17 +0200 Subject: [PATCH 060/713] fixed existence checks without drg module --- src/sage/graphs/generators/distance_regular.pyx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/sage/graphs/generators/distance_regular.pyx b/src/sage/graphs/generators/distance_regular.pyx index 37f499c0a60..8a0a05ca3cf 100644 --- a/src/sage/graphs/generators/distance_regular.pyx +++ b/src/sage/graphs/generators/distance_regular.pyx @@ -1898,8 +1898,9 @@ def distance_regular_graph(list arr, existence=False, check=True): # basic checks if len(arr) % 2 == 1 or any([i <= 0 for i in arr]) or \ any([x != int(x) for x in arr]) or \ - any([(arr[i] - arr[i+1]) < 0 for i in range(d)]) or \ - any([(arr[d+i+1] - arr[d+i]) < 0 for i in range(d)]): + any([(arr[i] - arr[i + 1]) < 0 for i in range(d - 1)]) or \ + any([(arr[d + i + 1] - arr[d + i]) < 0 for i in range(d - 1)]): + if existence: return False raise EmptySetError(("No distance-regular graphs with " f"parameters {arr} exists")) From be629db0f5fd3ac920612013882382231f91c3d8 Mon Sep 17 00:00:00 2001 From: n-vi Date: Mon, 24 Aug 2020 17:56:51 +0300 Subject: [PATCH 061/713] fix try 1 --- .../rings/padics/local_generic_element.pyx | 68 +++++++++++++++++-- 1 file changed, 61 insertions(+), 7 deletions(-) diff --git a/src/sage/rings/padics/local_generic_element.pyx b/src/sage/rings/padics/local_generic_element.pyx index fc993c71b26..b75e4e6d429 100644 --- a/src/sage/rings/padics/local_generic_element.pyx +++ b/src/sage/rings/padics/local_generic_element.pyx @@ -202,9 +202,10 @@ cdef class LocalGenericElement(CommutativeRingElement): def slice(self, i, j, k = 1, lift_mode='simple'): r""" Returns the sum of the `p^{i + l \cdot k}` terms of the series - expansion of this element, for `i + l \cdot k` between ``i`` and - ``j-1`` inclusive, and nonnegative integers `l`. Behaves analogously to - the slice function for lists. + expansion of this element, where p is the uniformizer, + for `i + l \cdot k` between ``i`` and ``j-1`` inclusive, and + nonnegative integers `l`. Behaves analogously to the slice + function for lists. INPUT: @@ -298,6 +299,34 @@ cdef class LocalGenericElement(CommutativeRingElement): sage: b.slice(0,9,2) 5^2 + O(5^8) + Test that slices also work over unramified extensions:: + + sage: T. = Qp(5).extension(x^2-2) + sage: a = T(3*5^-1 + 1 + (3*t + 4)*5^2) + sage: a.slice(0, 1) + 1 + sage: a.slice(-3, 4) + 3*5^-1 + 1 + (3*t + 4)*5^2 + + Test that slices also work over 2-step extensions (unramified followed by eisenstein):: + + sage: F. = Qp(5).extension(x^2-3) + sage: H. = F[] + sage: T. = F.extension((4*5^-2 + 2*5^-1 + 4 + (2*a + 2)*5 + 3*a*5^3 + 4*5^4 + + 3*5^5 + (2*a + 2)*5^8 + (4*a + 3)*5^9 + 2*a*5^10 + (3*a + 3)*5^11 + (3*a + 1)*5^12 + + (3*a + 2)*5^13 + 4*5^14 + (2*a + 4)*5^15 + (4*a + 1)*5^16 + (a + 1)*5^17 + + O(5^18))*y^2 + (a + 2*a*5 + a*5^2 + 4*a*5^3 + (2*a + 4)*5^4 + (3*a + 4)*5^5 + + (a + 1)*5^6 + a*5^7 + (2*a + 4)*5^8 + 3*5^9 + 2*5^10 + 5^12 + (4*a + 2)*5^13 + 5^14 + + 5^15 + 3*a*5^16 + (a + 2)*5^17 + 4*5^18 + (3*a + 1)*5^19 + O(5^20))*y + (2*a + + 2)*5^-1 + 3 + 5 + a*5^2 + (4*a + 2)*5^3 + (4*a + 1)*5^4 + (3*a + 4)*5^5 + + (4*a + 4)*5^6 + (3*a + 2)*5^7 + (4*a + 4)*5^8 + 3*5^9 + (a + 3)*5^10 + (4*a + 3)*5^11 + + 5^12 + (2*a + 2)*5^14 + 4*a*5^15 + (2*a + 2)*5^16 + (4*a + 4)*5^17 + O(5^18)) + sage: b = T(3*w^-36 + (2*a + 2)*w^-23) + sage: b.slice(-25,2) + (2*a + 2)*w^-23 + sage: b.slice(0, 1) + 0 + Verify that :trac:`14106` has been fixed:: sage: R = Zp(5,7) @@ -340,13 +369,38 @@ cdef class LocalGenericElement(CommutativeRingElement): pk = self.parent().uniformizer_pow(k) # the p-power of the first term ppow = self.parent().uniformizer_pow(i) + + # classify the type of padic field + if isinstance(self.parent(), pAdicExtensionGeneric): + ext_type = self.parent()._extension_type() + # base fields are treated as Eisenstein extensions. + else: + ext_type = "Eisenstein" # construct the return value ans = self.parent().zero() - for c in islice(self.expansion(lift_mode=lift_mode), - int(start), int(stop), int(k)): - ans += ppow * c - ppow *= pk + if ext_type == "Eisenstein": + # one-step extension + if self.parent().absolute_degree() == self.parent().relative_degree(): + for c in islice(self.expansion(lift_mode=lift_mode), + int(start), int(stop), int(k)): + ans += ppow * c + ppow *= pk + generator = 0 + # two-step extension (unramified and then eisenstein) + generator = self.parent().base_ring().gen() + elif ext_type == "Unramified": # one-step extension + generator = self.parent().gen() + # sage currently supports only Eisenstein / unramified expansions. + else: + raise NotImplementedError + if generator != 0: + for c in islice(self.expansion(lift_mode=lift_mode), + int(start), int(stop), int(k)): + genpow = 1 + for d in c: + ans += d * genpow * ppow + genpow *= generator # fix the precision of the return value if j < ans.precision_absolute() or self.precision_absolute() < ans.precision_absolute(): From 6de518753fe5d4f8ffe367ca60b9a4efe3da60d1 Mon Sep 17 00:00:00 2001 From: n-vi Date: Tue, 25 Aug 2020 12:40:35 +0300 Subject: [PATCH 062/713] fix try 2 --- .../rings/padics/local_generic_element.pyx | 52 +++++++++++-------- 1 file changed, 29 insertions(+), 23 deletions(-) diff --git a/src/sage/rings/padics/local_generic_element.pyx b/src/sage/rings/padics/local_generic_element.pyx index b75e4e6d429..c9d1409b242 100644 --- a/src/sage/rings/padics/local_generic_element.pyx +++ b/src/sage/rings/padics/local_generic_element.pyx @@ -206,6 +206,9 @@ cdef class LocalGenericElement(CommutativeRingElement): for `i + l \cdot k` between ``i`` and ``j-1`` inclusive, and nonnegative integers `l`. Behaves analogously to the slice function for lists. + Currently implemented for one-step eisenstein or unramified padic + extensions, or for two-step (unramified and then eisenstein) + extensions. INPUT: @@ -301,31 +304,29 @@ cdef class LocalGenericElement(CommutativeRingElement): Test that slices also work over unramified extensions:: - sage: T. = Qp(5).extension(x^2-2) + sage: F = Qp(5) + sage: H. = F[] + sage: T. = F.extension(x^2-2) sage: a = T(3*5^-1 + 1 + (3*t + 4)*5^2) sage: a.slice(0, 1) - 1 + 1 + O(5) sage: a.slice(-3, 4) - 3*5^-1 + 1 + (3*t + 4)*5^2 + 3*5^-1 + 1 + (3*t + 4)*5^2 + O(5^4) + sage: a.slice(-1, 6, 3) + 3*5^-1 + (3*t + 4)*5^2 + O(5^6) Test that slices also work over 2-step extensions (unramified followed by eisenstein):: - sage: F. = Qp(5).extension(x^2-3) - sage: H. = F[] - sage: T. = F.extension((4*5^-2 + 2*5^-1 + 4 + (2*a + 2)*5 + 3*a*5^3 + 4*5^4 + - 3*5^5 + (2*a + 2)*5^8 + (4*a + 3)*5^9 + 2*a*5^10 + (3*a + 3)*5^11 + (3*a + 1)*5^12 + - (3*a + 2)*5^13 + 4*5^14 + (2*a + 4)*5^15 + (4*a + 1)*5^16 + (a + 1)*5^17 + - O(5^18))*y^2 + (a + 2*a*5 + a*5^2 + 4*a*5^3 + (2*a + 4)*5^4 + (3*a + 4)*5^5 + - (a + 1)*5^6 + a*5^7 + (2*a + 4)*5^8 + 3*5^9 + 2*5^10 + 5^12 + (4*a + 2)*5^13 + 5^14 + - 5^15 + 3*a*5^16 + (a + 2)*5^17 + 4*5^18 + (3*a + 1)*5^19 + O(5^20))*y + (2*a + - 2)*5^-1 + 3 + 5 + a*5^2 + (4*a + 2)*5^3 + (4*a + 1)*5^4 + (3*a + 4)*5^5 + - (4*a + 4)*5^6 + (3*a + 2)*5^7 + (4*a + 4)*5^8 + 3*5^9 + (a + 3)*5^10 + (4*a + 3)*5^11 - + 5^12 + (2*a + 2)*5^14 + 4*a*5^15 + (2*a + 2)*5^16 + (4*a + 4)*5^17 + O(5^18)) - sage: b = T(3*w^-36 + (2*a + 2)*w^-23) - sage: b.slice(-25,2) - (2*a + 2)*w^-23 - sage: b.slice(0, 1) - 0 + sage: F = Qp(5) + sage: H. = F[] + sage: T. = F.extension(x^2-3) + sage: D. = T[] + sage: W. = T.extension((4*5^-2 + 2*5^-1 + 4 + (2*t + 2)*5 + 3*t*5^3 + 4*5^4 + 3*5^5 + (2*t + 2)*5^8 + (4*t + 3)*5^9 + 2*t*5^10 + (3*t + 3)*5^11 + (3*t + 1)*5^12 + (3*t + 2)*5^13 + 4*5^14 + (2*t + 4)*5^15 + (4*t + 1)*5^16 + (t + 1)*5^17 + O(5^18))*y^2 + (t + 2*t*5 + t*5^2 + 4*t*5^3 + (2*t + 4)*5^4 + (3*t + 4)*5^5 + (t + 1)*5^6 + t*5^7 + (2*t + 4)*5^8 + 3*5^9 + 2*5^10 + 5^12 + (4*t + 2)*5^13 + 5^14 + 5^15 + 3*t*5^16 + (t + 2)*5^17 + 4*5^18 + (3*t + 1)*5^19 + O(5^20))*y + (2*t + 2)*5^-1 + 3 + 5 + t*5^2 + (4*t + 2)*5^3 + (4*t + 1)*5^4 + (3*t + 4)*5^5 + (4*t + 4)*5^6 + (3*t + 2)*5^7 + (4*t + 4)*5^8 + 3*5^9 + (t + 3)*5^10 + (4*t + 3)*5^11 + 5^12 + (2*t + 2)*5^14 + 4*t*5^15 + (2*t + 2)*5^16 + (4*t + 4)*5^17 + O(5^18)) + sage: a = W(3*w^-36 + (2*t + 2)*w^-23) + sage: a.slice(-25,2) + (2*t + 2)*w^-23 + O(w^2) + sage: a.slice(0, 1) + O(w) Verify that :trac:`14106` has been fixed:: @@ -339,6 +340,8 @@ cdef class LocalGenericElement(CommutativeRingElement): 2*5^2 + 2*5^3 + O(5^5) """ + from sage.rings.padics.padic_extension_generic import pAdicExtensionGeneric + if i is None: i = self.valuation() if j is None or j is infinity: @@ -373,14 +376,14 @@ cdef class LocalGenericElement(CommutativeRingElement): # classify the type of padic field if isinstance(self.parent(), pAdicExtensionGeneric): ext_type = self.parent()._extension_type() - # base fields are treated as Eisenstein extensions. + # base fields are treated here as Eisenstein extensions. else: ext_type = "Eisenstein" # construct the return value ans = self.parent().zero() if ext_type == "Eisenstein": - # one-step extension + # one-step eisenstein extension if self.parent().absolute_degree() == self.parent().relative_degree(): for c in islice(self.expansion(lift_mode=lift_mode), int(start), int(stop), int(k)): @@ -388,8 +391,10 @@ cdef class LocalGenericElement(CommutativeRingElement): ppow *= pk generator = 0 # two-step extension (unramified and then eisenstein) - generator = self.parent().base_ring().gen() - elif ext_type == "Unramified": # one-step extension + else: + generator = self.parent().base_ring().gen() + # one-step unramified extension + elif ext_type == "Unramified": generator = self.parent().gen() # sage currently supports only Eisenstein / unramified expansions. else: @@ -401,6 +406,7 @@ cdef class LocalGenericElement(CommutativeRingElement): for d in c: ans += d * genpow * ppow genpow *= generator + ppow *= pk # fix the precision of the return value if j < ans.precision_absolute() or self.precision_absolute() < ans.precision_absolute(): From 79760fffbf82424c8534a7f605c4c71482d9af1d Mon Sep 17 00:00:00 2001 From: Ivo Maffei Date: Tue, 25 Aug 2020 12:30:44 +0200 Subject: [PATCH 063/713] completed is_near_polygon --- .../graphs/generators/distance_regular.pyx | 180 ++++-------------- 1 file changed, 42 insertions(+), 138 deletions(-) diff --git a/src/sage/graphs/generators/distance_regular.pyx b/src/sage/graphs/generators/distance_regular.pyx index cb39eebd351..c0b26f9c6cd 100644 --- a/src/sage/graphs/generators/distance_regular.pyx +++ b/src/sage/graphs/generators/distance_regular.pyx @@ -2283,13 +2283,20 @@ def pseudo_partition_graph(int m, int a): cdef enum NearPolygonGraph: RegularPolygon, GeneralisedPolygon, - OddGraph + OddGraph, + DoubleOdd, + DoubleGrassmann, + FoldedCube, + HammingGraph, + DualPolarGraph def new_is_near_polygon(array): r""" Return a pair """ - + from sage.arith.misc import is_prime_power + from sage.combinat.q_analogues import q_binomial + from sage.functions.log import log if len(array) % 2 != 0: return False @@ -2302,8 +2309,8 @@ def new_is_near_polygon(array): if l < 0: return False - if any(array[i] != k - (l + 1)*array[d - 1 + i] for i in range(1, d)) or \ - k - (l + 1)*array[2*d - 1] != 0: + if any(array[i] != k - (l+1) * array[d - 1 + i] for i in range(1, d)) or \ + k - (l+1) * array[2*d - 1] != 0: return False # additional checks @@ -2323,23 +2330,18 @@ def new_is_near_polygon(array): # normal polygon return (NearPolygonGraph.RegularPolygon, d) - if (d, s, t) in {(3, 2, 2), (3, 3, 3), (3, 4, 4), (3, 5, 5), (3, 2, 8), + if (d == 3 and (s == 1 or t == 1) and is_prime_power(s * t)) or \ + (d, s, t) in {(3, 2, 2), (3, 3, 3), (3, 4, 4), (3, 5, 5), (3, 2, 8), (3, 8, 2), (3, 3, 27), (3, 27, 3), (4, 2, 4), (6, 1, 2), (6, 1, 3), (6, 1, 4), (6, 1, 5), (6, 2, 1), (6, 3, 1), (6, 4, 1), (6, 5, 1)}: - # we know the gen pol - return (NearPolygonGraph.GenralisedPolygon, d, s, t) - - if d == 3 and (s == 1 or t == 1) and is_pime_power(s * t): - # we know it - return (NearPolygonGraph.GenralisedPolygon, d, s, t) + return (NearPolygonGraph.GeneralisedPolygon, d, s, t) if d == 4 and (s == 1 or t == 1): q = s * t if strongly_regular_graph((q+1) * (q*q + 1), q * (q+1), q-1, q+1, existence=True): - # we know it - return (NearPolygonGraph.GenralisedPolygon, d, s, t) + return (NearPolygonGraph.GeneralisedPolygon, d, s, t) # otherwise not known generalised polygon return False @@ -2348,136 +2350,38 @@ def new_is_near_polygon(array): if l == 0 and k == d + 1 and n == 2*d + 1 and \ all(array[d + i] == (i + 2) // 2 for i in range(d)): - # odd graph return (NearPolygonGraph.OddGraph, n) + if l == 0 and k == n and all(array[d - 1 + i] == i for i in range(1, d)) \ + and array[2*d - 1] == d * (2*d + 2 - n): + return (NearPolygonGraph.FoldedCube, n) + + if l == 0 and n == 2 * d and d % 2 == 1 and (d-1) // 2 + 1 == k and \ + all(array[d - 1 + i] == (i+1) // 2 for i in range(1, d + 1)): + return (NearPolygonGraph.DoubleOdd, k - 1) + + if l == 0 and n == 2 * d and d % 2 == 1 and \ + is_prime_power(array[d + 2] - 1) and \ + all(array[d - 1 + i] == q_binomial((i+1) // 2, 1, array[d + 2] - 1) + for i in range(1, d+1)) and \ + k == q_binomial((d-1) // 2 + 1, 1, array[d + 2] - 1): + return (NearPolygonGraph.DoubleGrassmann, array[d + 2] - 1, (d-1) // 2) + + if n == 2 * d and k == (l+1) * d and \ + all(array[d - 1 + i] == i for i in range(1, d + 1)): + return (NearPolygonGraph.HammingGraph, d, l + 2) + + if n == 2 * d and is_prime_power(array[d + 1] - 1) and \ + (l + 1) in [(array[d + 1] - 1) ** e for e in [0, 0.5, 1, 1.5, 2]] and \ + k == (l+1) * q_binomial(d, 1, array[d + 1] - 1) and \ + all(array[d - 1 + i] == q_binomial(i, 1, array[d + 1] - 1) + for i in range(1, d + 1)): + return (NearPolygonGraph.DualPolarGraph, d, array[d + 1] - 1, + log(l + 1, array[d + 1] - 1)) - -def is_near_polygon(list arr): - r""" - Checks if the intersection array could be of a near polygon. if so returns a parameter l, otherwise -1 - - p199 theorem 6.4.1: - a dist-reg graph with int. arr [b_0,...,b_{d-1}, c_1, ..., c_d] is a regular near polygon - iff there is no induced subgraph K_{1,1,2} and there is l s.t. b_i = k - (l+1)*c_i for i = 0,..,d-1. - - In particular, if it is a near polygon, then is a near 2d-gon if k = (l+1)*c_d and a near (2d+1)-gon otherwise - """ - def is_generalised_2d_gon(a,d): - #c_1,...,c_{d-1} = 1 - for i in range(1,d): - if a[d+i-1] != 1: return False - - t = a[2*d-1] -1 #c_d-1 - - # b_0 = s(t+1) - if a[0] % (t+1) != 0: return False - s = a[0] // (t+1) - - if not is_prime_power(s*t):#this also rules out (1,1) - return False - - if d == 3: - if s == 1 or t == 1: - #order (1,q) or (q,1) - return True - elif s == t and s in {2,3,4,5}: - #order (q,q) - return True - elif (s==t**3 or t == s**3) and s*t in {16,81}: - #order (q,q^3) or (q^3,q); q is 2 or 3 - return True - return False - elif d == 4: - if s== 1 or t == 1: - #order (1,q) or (q,1) - q= s*t - if strongly_regular_graph((q+1)*(q*q+1),q*(q+1),q-1,q+1,existence=True): - return True - elif s==t*t or s*s == t and s*t == 8: - #order (q,q^2) we can only do q=2 - return True - return False - elif d == 6: - if (s==1 or t == 1) and s*t in {2,3,4,5}: - #order (1,q); rely on hexagon (q,q) - return True - - return False - - #gamma indicates what graph we have - # gamma -> graph - # 0 -> gen polygon - # 1 -> polygon - # 2 -> Odd graph - # 3 -> 2xGrassmann - # 4 -> 2xodd - # 5 -> folded cube - - d = len(arr) - if d % 2 != 0: - return False - - d = d // 2 - if d < 3: - return False - - k = arr[0] - l = k - arr[1] - 1 # b_1 = k - (l+1) - if l < 0: return False - - #for i = 0 we have b_0 = k - (l+1)*c_0 = k since c_0 = 0 always - for i in range(1,d): - if arr[i] != k - (l+1)*arr[d+i-1]: #b_i = k - (l+1)c_i - return False - - #chek k >= (l+1)c_d - if k < (l+1)*arr[2*d-1]: - return False - - #now find if we can build this graph - n = 2*d if k == (l+1)*arr[2*d-1] else 2*d+1 - cs = arr[d:] - - #is generalised polygon? - if is_generalised_2d_gon(arr,d): - t = arr[2*d-1]-1 - s = arr[0]//(t+1) - return (0,(d,s,t)) - - if l != 0: return False#classial parameters; don't deal it here - - #now l==0 - if k== 2 and cs == ([1 for i in range(1,d)]+[2*d+2-n]): - #polygon - return (1,n) - - if n == 2*d+1 and k == d+1 and cs == [(i+1)//2 for i in range(1,d+1)]: - #odd graph - return (2,d+1) - - if ( n == 2*d and d%2 == 1 and is_prime_power(cs[2]-1) and - k == q_binomial((d-1)//2+1, 1,cs[2]-1) and#cs[2]=c_3 = q_binom(2,1,q) = q+1 - cs == [q_binomial((i+1)//2,1,cs[2]-1) for i in range(1,d+1)]): - #double grassman - e = (d-1)//2 - q = cs[2]-1 - return (3,(q,e)) - - if ( n==2*d and d%2 == 1 and k -1 == (d-1)//2 and - cs == [(i+1)//2 for i in range(1,d+1)] ): - #double odd - e = (d-1)//2 - return (4,e) - - if k == n and cs == ([i for i in range(1,d)]+[d*(2*d+2-n)]): - #Folded cube - return (5,n) - + # otherwise we don't know the near polygon return False - - # dictionary intersection_array (as tuple) -> construction # of spordaic distance-regular graphs from sage.graphs.generators.smallgraphs import (FosterGraph, BiggsSmithGraph, From 003ed56de3477dba2d631fcdb4e4b04276940c6d Mon Sep 17 00:00:00 2001 From: Ivo Maffei Date: Tue, 25 Aug 2020 18:05:05 +0200 Subject: [PATCH 064/713] completed near polygon graphs; testing --- .../graphs/generators/distance_regular.pyx | 164 +++++++++++++++--- 1 file changed, 142 insertions(+), 22 deletions(-) diff --git a/src/sage/graphs/generators/distance_regular.pyx b/src/sage/graphs/generators/distance_regular.pyx index c0b26f9c6cd..9c5a5a2f012 100644 --- a/src/sage/graphs/generators/distance_regular.pyx +++ b/src/sage/graphs/generators/distance_regular.pyx @@ -2281,7 +2281,7 @@ def pseudo_partition_graph(int m, int a): raise ValueError("No known graph exists") cdef enum NearPolygonGraph: - RegularPolygon, + RegularPolygon = 0, GeneralisedPolygon, OddGraph, DoubleOdd, @@ -2290,9 +2290,31 @@ cdef enum NearPolygonGraph: HammingGraph, DualPolarGraph -def new_is_near_polygon(array): +def is_near_polygon(array): r""" - Return a pair + Return a tuple of parameters which identify the near polygon graph with + the given intersection array. If such tuple doesn't exist, return ``False``. + + Note that ``array`` may be the intersection array of a near polygon, but if + such graph has diameter less than 3, then this function will return + ``False``. + + INPUT: + + - ``array`` -- list; intersection array + + OUTPUT: + + The tuple has the form ``(id, params)`` where ``id`` is a value of the + enum `NearPolygonGraph` which identify a family of graphs and ``params`` + are all parameters needed to construct the final graph. + + EXAMPLES:: + + REFERECES: + + TESTS:: + """ from sage.arith.misc import is_prime_power from sage.combinat.q_analogues import q_binomial @@ -2303,58 +2325,65 @@ def new_is_near_polygon(array): d = len(array) // 2 + if d < 3: + print("diameter < 3") + return False + k = array[0] l = k - array[1] - 1 if l < 0: + print("lambda < 0") return False if any(array[i] != k - (l+1) * array[d - 1 + i] for i in range(1, d)) or \ - k - (l+1) * array[2*d - 1] != 0: + k < (l+1) * array[2*d - 1]: + print("no near poly") return False # additional checks if k < (l+1) * array[2*d - 1] or k % (l + 1) != 0: + print("fail additional checks") return False # check if it is known example if k == (l+1) * array[2*d - 1] and \ all(array[d + i] == 1 for i in range(d-1)) and \ - l + 1 > 1 and array[2*d - 1] - 1 > 1: + (l + 1 > 1 or array[2*d - 1] - 1 > 1): # last 2 reject regular polygons # generalised polygon s = l+1 t = array[2*d - 1] - 1 - # check we know the pair (s, t) - if s == 1 and t == 1: - # normal polygon - return (NearPolygonGraph.RegularPolygon, d) - if (d == 3 and (s == 1 or t == 1) and is_prime_power(s * t)) or \ (d, s, t) in {(3, 2, 2), (3, 3, 3), (3, 4, 4), (3, 5, 5), (3, 2, 8), - (3, 8, 2), (3, 3, 27), (3, 27, 3), (4, 2, 4), + (3, 8, 2), (3, 3, 27), (3, 27, 3), (4, 2, 4), (4, 4, 2), (6, 1, 2), (6, 1, 3), (6, 1, 4), (6, 1, 5), (6, 2, 1), (6, 3, 1), (6, 4, 1), (6, 5, 1)}: - return (NearPolygonGraph.GeneralisedPolygon, d, s, t) - + return (NearPolygonGraph.GeneralisedPolygon, (d, s, t)) + if d == 4 and (s == 1 or t == 1): q = s * t if strongly_regular_graph((q+1) * (q*q + 1), q * (q+1), q-1, q+1, existence=True): - return (NearPolygonGraph.GeneralisedPolygon, d, s, t) - + return (NearPolygonGraph.GeneralisedPolygon, (d, s, t)) + # otherwise not known generalised polygon + print("not known gen poly") return False n = 2 * d if k == (l+1) * array[2*d - 1] else 2*d + 1 + if k == 2 and l == 0 and all(array[d + i] == 1 for i in range(d - 1)) and \ + array[2*d - 1] in {1, 2}: + return (NearPolygonGraph.RegularPolygon, 2*d + 2 - array[2*d - 1]) + if l == 0 and k == d + 1 and n == 2*d + 1 and \ all(array[d + i] == (i + 2) // 2 for i in range(d)): - return (NearPolygonGraph.OddGraph, n) + return (NearPolygonGraph.OddGraph, d + 1) if l == 0 and k == n and all(array[d - 1 + i] == i for i in range(1, d)) \ and array[2*d - 1] == d * (2*d + 2 - n): - return (NearPolygonGraph.FoldedCube, n) + return (NearPolygonGraph.FoldedCube, k) if l == 0 and n == 2 * d and d % 2 == 1 and (d-1) // 2 + 1 == k and \ all(array[d - 1 + i] == (i+1) // 2 for i in range(1, d + 1)): @@ -2365,23 +2394,114 @@ def new_is_near_polygon(array): all(array[d - 1 + i] == q_binomial((i+1) // 2, 1, array[d + 2] - 1) for i in range(1, d+1)) and \ k == q_binomial((d-1) // 2 + 1, 1, array[d + 2] - 1): - return (NearPolygonGraph.DoubleGrassmann, array[d + 2] - 1, (d-1) // 2) + return (NearPolygonGraph.DoubleGrassmann, (array[d + 2] - 1, (d-1) // 2)) if n == 2 * d and k == (l+1) * d and \ all(array[d - 1 + i] == i for i in range(1, d + 1)): - return (NearPolygonGraph.HammingGraph, d, l + 2) + return (NearPolygonGraph.HammingGraph, (d, l + 2)) if n == 2 * d and is_prime_power(array[d + 1] - 1) and \ (l + 1) in [(array[d + 1] - 1) ** e for e in [0, 0.5, 1, 1.5, 2]] and \ k == (l+1) * q_binomial(d, 1, array[d + 1] - 1) and \ all(array[d - 1 + i] == q_binomial(i, 1, array[d + 1] - 1) for i in range(1, d + 1)): - return (NearPolygonGraph.DualPolarGraph, d, array[d + 1] - 1, - log(l + 1, array[d + 1] - 1)) - + return (NearPolygonGraph.DualPolarGraph, (d, array[d + 1] - 1, + log(l + 1, array[d + 1] - 1))) + # otherwise we don't know the near polygon + print("not known near poly") return False +def near_polygon_graph(family, params): + r""" + Return the near polygon graph with the given parameters. + + The input is expected to be the result of the function + :func:`sage.graphs.generators.distance_regular.is_near_polygon`. + + INPUT: + + - ``family`` -- int; an element of the enum ``NearPolygonGraph``. + + - ``params`` -- int or tuple; the paramters needed to construct a graph + of the family ``family``. + + EXAMPLES:: + + REFERENCES: + + See [BCN1989]_ pp. 198-206 for some theory about near polygons as well as + a list of known examples. + + TESTS:: + """ + + if family == NearPolygonGraph.RegularPolygon: + from sage.graphs.generators.basic import CycleGraph + return CycleGraph(params) + + if family == NearPolygonGraph.GeneralisedPolygon: + d, s, t = params + if d == 3: + return GeneralisedHexagonGraph(s, t) + if d == 4: + return GeneralisedOctagonGraph(s, t) + if d == 6: + return GeneralisedDodecagonGraph(s, t) + + if family == NearPolygonGraph.OddGraph: + from sage.graphs.generators.families import OddGraph + return OddGraph(params) + + if family == NearPolygonGraph.DoubleOdd: + return DoubleOddGraph(params) + + if family == NearPolygonGraph.DoubleGrassmann: + return DoubleGrassmannGraph(*params) + + if family == NearPolygonGraph.FoldedCube: + from sage.graphs.generators.families import FoldedCubeGraph + return FoldedCubeGraph(params) + + if family == NearPolygonGraph.HammingGraph: + from sage.graphs.generators.families import HammingGraph + return HammingGraph(*params) + + if family == NearPolygonGraph.DualPolarGraph: + from sage.graphs.generators.classical_geometries import ( + UnitaryDualPolarGraph, + OrthogonalDualPolarGraph, + SymplecticDualPolarGraph) + + d, q, e = params + if e == 0: + print(f"d={d}, q={q} of type {type(d)}, {type(q)}") + return OrthogonalDualPolarGraph(1, d, q) + if e == 0.5: + return UnitaryDualPolarGraph(2 * d, q) + if e == 1: + return SymplecticDualPolarGraph(2 * d, q) + if e == 1.5: + return UnitaryDualPolarGraph(2*d + 1, q) + if e == 2: + return OrthogonalDualPolarGraph(-1, d, q) + + raise ValueError("No known near polygons with the given parameters") + +def test_near_poly(G): + array = _intersection_array_from_graph(G) + print(f"intersection array: {array}") + res = is_near_polygon(array) + print(f"near polygon parameters {res}") + if res is False: + print("") + return + H = near_polygon_graph(*res) + print(f"graph built: {H.name()}") + ok = _intersection_array_from_graph(H) == array + print(f"tests passed: {ok}\n") + + # dictionary intersection_array (as tuple) -> construction # of spordaic distance-regular graphs from sage.graphs.generators.smallgraphs import (FosterGraph, BiggsSmithGraph, From ebe33b154dafa9a41c4d24d5d2c0e17250a9b981 Mon Sep 17 00:00:00 2001 From: Ivo Maffei Date: Tue, 25 Aug 2020 20:13:09 +0200 Subject: [PATCH 065/713] removed testing code; added doctests --- .../graphs/generators/distance_regular.pyx | 78 +++++++++++++------ 1 file changed, 54 insertions(+), 24 deletions(-) diff --git a/src/sage/graphs/generators/distance_regular.pyx b/src/sage/graphs/generators/distance_regular.pyx index 9c5a5a2f012..14185d5bb9a 100644 --- a/src/sage/graphs/generators/distance_regular.pyx +++ b/src/sage/graphs/generators/distance_regular.pyx @@ -2311,10 +2311,36 @@ def is_near_polygon(array): EXAMPLES:: + sage: from sage.graphs.generators.distance_regular import ( + ....: is_near_polygon, near_polygon_graph) + sage: is_near_polygon([7, 6, 6, 5, 5, 4, 1, 1, 2, 2, 3, 3]) + (2, 7) + sage: near_polygon_graph(2, 7) + Odd Graph with parameter 7: Graph on 1716 vertices + sage: _.is_distance_regular(True) + ([7, 6, 6, 5, 5, 4, None], [None, 1, 1, 2, 2, 3, 3]) + REFERECES: + See [BCN1989]_ pp. 198-206 for some theory about near polygons as well as + a list of known examples. + TESTS:: + sage: from sage.graphs.generators.distance_regular import ( + ....: is_near_polygon, near_polygon_graph) + sage: is_near_polygon([7, 6, 6, 4, 4, 1, 1, 3, 3, 7]) + (4, (2, 2)) + sage: near_polygon_graph(4, (2, 2)) + Double Grassmann graph (5, 2, 2): Graph on 310 vertices + sage: near_polygon_graph(*is_near_polygon([3, 2, 2, 1, 1, 3])) + Generalised hexagon of order (1, 2): Graph on 14 vertices + sage: is_near_polygon([16, 12, 8, 4, 1, 2, 3, 4]) + (6, (4, 5)) + sage: is_near_polygon([]) + False + sage: is_near_polygon([25, 16, 9, 4, 1, 1, 4, 9, 16, 25]) # JohnsonGraph + False """ from sage.arith.misc import is_prime_power from sage.combinat.q_analogues import q_binomial @@ -2326,24 +2352,20 @@ def is_near_polygon(array): d = len(array) // 2 if d < 3: - print("diameter < 3") return False k = array[0] l = k - array[1] - 1 if l < 0: - print("lambda < 0") return False if any(array[i] != k - (l+1) * array[d - 1 + i] for i in range(1, d)) or \ k < (l+1) * array[2*d - 1]: - print("no near poly") return False # additional checks if k < (l+1) * array[2*d - 1] or k % (l + 1) != 0: - print("fail additional checks") return False # check if it is known example @@ -2368,7 +2390,6 @@ def is_near_polygon(array): return (NearPolygonGraph.GeneralisedPolygon, (d, s, t)) # otherwise not known generalised polygon - print("not known gen poly") return False n = 2 * d if k == (l+1) * array[2*d - 1] else 2*d + 1 @@ -2409,7 +2430,6 @@ def is_near_polygon(array): log(l + 1, array[d + 1] - 1))) # otherwise we don't know the near polygon - print("not known near poly") return False def near_polygon_graph(family, params): @@ -2428,12 +2448,36 @@ def near_polygon_graph(family, params): EXAMPLES:: + sage: from sage.graphs.generators.distance_regular import ( + ....: is_near_polygon, near_polygon_graph) + sage: near_polygon_graph(*is_near_polygon([6, 5, 5, 4, 4, 3, 3, 2, 2, \ + ....: 1, 1, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6]) + Bipartite double of Odd graph on a set of 11 elements: Graph on 924 vertices + sage: G=_; G.is_distance_regular(True) + ([6, 5, 5, 4, 4, 3, 3, 2, 2, 1, 1, None], + [None, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6]) + REFERENCES: See [BCN1989]_ pp. 198-206 for some theory about near polygons as well as a list of known examples. TESTS:: + + sage: near_polygon_graph(12, 9) + Traceback (most recent call last): + ... + ValueError: No known near polygons with the given parameters + sage: is_near_polygon([2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2]) + (0, 12) + sage: near_polygon_graph((0, 12)) + Traceback (most recent call last): + ... + TypeError: near_polygon_graph() takes exactly 2 positional arguments (1 given) + sage: near_polygon_graph(0, 12) + Cycle graph: Graph on 12 vertices + sage: near_polygon_graph(*is_near_polygon([8, 7, 6, 5, 1, 2, 3, 8]) + Folded Cube Graph: Graph on 128 vertices """ if family == NearPolygonGraph.RegularPolygon: @@ -2475,33 +2519,18 @@ def near_polygon_graph(family, params): d, q, e = params if e == 0: - print(f"d={d}, q={q} of type {type(d)}, {type(q)}") return OrthogonalDualPolarGraph(1, d, q) if e == 0.5: - return UnitaryDualPolarGraph(2 * d, q) + return UnitaryDualPolarGraph(2 * d, int(q**0.5)) if e == 1: return SymplecticDualPolarGraph(2 * d, q) if e == 1.5: - return UnitaryDualPolarGraph(2*d + 1, q) + return UnitaryDualPolarGraph(2*d + 1, int(q**0.5)) if e == 2: return OrthogonalDualPolarGraph(-1, d, q) raise ValueError("No known near polygons with the given parameters") -def test_near_poly(G): - array = _intersection_array_from_graph(G) - print(f"intersection array: {array}") - res = is_near_polygon(array) - print(f"near polygon parameters {res}") - if res is False: - print("") - return - H = near_polygon_graph(*res) - print(f"graph built: {H.name()}") - ok = _intersection_array_from_graph(H) == array - print(f"tests passed: {ok}\n") - - # dictionary intersection_array (as tuple) -> construction # of spordaic distance-regular graphs from sage.graphs.generators.smallgraphs import (FosterGraph, BiggsSmithGraph, @@ -2567,7 +2596,8 @@ _sporadic_graph_database = { _infinite_families_database = [ (is_classical_parameters_graph, graph_with_classical_parameters), - (is_pseudo_partition_graph, pseudo_partition_graph) + (is_pseudo_partition_graph, pseudo_partition_graph), + (is_near_polygon, near_polygon_graph) ] def distance_regular_graph(list arr, existence=False, check=True): From 039c92eb0189566932ec3015fa13933c89362321 Mon Sep 17 00:00:00 2001 From: Ivo Maffei Date: Tue, 25 Aug 2020 20:16:50 +0200 Subject: [PATCH 066/713] added doctest to general function --- src/sage/graphs/generators/distance_regular.pyx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sage/graphs/generators/distance_regular.pyx b/src/sage/graphs/generators/distance_regular.pyx index 45a48f1d79a..b6d59557fc9 100644 --- a/src/sage/graphs/generators/distance_regular.pyx +++ b/src/sage/graphs/generators/distance_regular.pyx @@ -2656,6 +2656,8 @@ def distance_regular_graph(list arr, existence=False, check=True): Hamming Graph with parameters 7,3: Graph on 2187 vertices sage: graphs.distance_regular_graph([66, 45, 28, 1, 6, 30]) Graph on 1024 vertices + sage: graphs.distance_regular_graph([6,5,5,5,1,1,1,6]) + Generalised octagon of order (1, 5): Graph on 312 vertices """ from sage.misc.unknown import Unknown from sage.categories.sets_cat import EmptySetError From 2e8fbf5f7fed81dc7d33be6bb8494b0f9e8216ee Mon Sep 17 00:00:00 2001 From: Ivo Maffei Date: Tue, 25 Aug 2020 20:28:57 +0200 Subject: [PATCH 067/713] fix doctests --- src/sage/graphs/generators/distance_regular.pyx | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/src/sage/graphs/generators/distance_regular.pyx b/src/sage/graphs/generators/distance_regular.pyx index b6d59557fc9..1c2561041eb 100644 --- a/src/sage/graphs/generators/distance_regular.pyx +++ b/src/sage/graphs/generators/distance_regular.pyx @@ -2451,7 +2451,7 @@ def near_polygon_graph(family, params): sage: from sage.graphs.generators.distance_regular import ( ....: is_near_polygon, near_polygon_graph) sage: near_polygon_graph(*is_near_polygon([6, 5, 5, 4, 4, 3, 3, 2, 2, \ - ....: 1, 1, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6]) + ....: 1, 1, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6])) Bipartite double of Odd graph on a set of 11 elements: Graph on 924 vertices sage: G=_; G.is_distance_regular(True) ([6, 5, 5, 4, 4, 3, 3, 2, 2, 1, 1, None], @@ -2476,7 +2476,7 @@ def near_polygon_graph(family, params): TypeError: near_polygon_graph() takes exactly 2 positional arguments (1 given) sage: near_polygon_graph(0, 12) Cycle graph: Graph on 12 vertices - sage: near_polygon_graph(*is_near_polygon([8, 7, 6, 5, 1, 2, 3, 8]) + sage: near_polygon_graph(*is_near_polygon([8, 7, 6, 5, 1, 2, 3, 8])) Folded Cube Graph: Graph on 128 vertices """ @@ -2627,16 +2627,6 @@ def distance_regular_graph(list arr, existence=False, check=True): sage: G.is_distance_regular(True) ([12, 11, 10, 7, None], [None, 1, 2, 5, 12]) - Not all distance-regular graphs can be built with this function:: - - sage: G = graphs.DoubleOddGraph(2) - sage: G.is_distance_regular(True) - ([3, 2, 2, 1, 1, None], [None, 1, 1, 2, 2, 3]) - sage: graphs.distance_regular_graph([3, 2, 2, 1, 1, 1, 1, 2, 2, 3]) - Traceback (most recent call last): - ... - RuntimeError: No distance-regular graph with intersection array [3, 2, 2, 1, 1, 1, 1, 2, 2, 3] known - REFERENCES: See [BCN1989]_ and [VDKT2016]_. From dde9d1a43db6d94f34a2a853c7b9e4070641d742 Mon Sep 17 00:00:00 2001 From: Ivo Maffei Date: Tue, 25 Aug 2020 21:10:33 +0200 Subject: [PATCH 068/713] fix some long time that are too long --- src/sage/graphs/generators/distance_regular.pyx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/graphs/generators/distance_regular.pyx b/src/sage/graphs/generators/distance_regular.pyx index 1c2561041eb..8ac469294b9 100644 --- a/src/sage/graphs/generators/distance_regular.pyx +++ b/src/sage/graphs/generators/distance_regular.pyx @@ -463,7 +463,7 @@ def shortened_000_111_extended_binary_Golay_code_graph(): EXAMPLES:: - sage: G = graphs.shortened_000_111_extended_binary_Golay_code_graph() # long time (2 min) + sage: G = graphs.shortened_000_111_extended_binary_Golay_code_graph() # not tested (2 min) sage: G.is_distance_regular(True) # long time ([21, 20, 16, 9, 2, 1, None], [None, 1, 2, 3, 16, 20, 21]) @@ -2635,7 +2635,7 @@ def distance_regular_graph(list arr, existence=False, check=True): sage: graphs.distance_regular_graph([3, 2, 2, 1, 1, 1, 1, 2, 2, 3], ....: existence=True) - Unknown + True sage: graphs.distance_regular_graph([3, 2, 2, 1, 2, 1, 1, 2, 2, 3], ....: existence=True) False From cd075c7bd1bafaaac3f0da7806061f1eede4c004 Mon Sep 17 00:00:00 2001 From: Ivo Maffei Date: Tue, 25 Aug 2020 21:19:58 +0200 Subject: [PATCH 069/713] change long time to not tested --- src/sage/graphs/generators/distance_regular.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/graphs/generators/distance_regular.pyx b/src/sage/graphs/generators/distance_regular.pyx index 5a5436d6d8a..6aa49ee477c 100644 --- a/src/sage/graphs/generators/distance_regular.pyx +++ b/src/sage/graphs/generators/distance_regular.pyx @@ -464,7 +464,7 @@ def shortened_000_111_extended_binary_Golay_code_graph(): EXAMPLES:: sage: G = graphs.shortened_000_111_extended_binary_Golay_code_graph() # not tested (2 min) - sage: G.is_distance_regular(True) # long time + sage: G.is_distance_regular(True) # not tested; due to above ([21, 20, 16, 9, 2, 1, None], [None, 1, 2, 3, 16, 20, 21]) ALGORITHM: From e0bd099e173fc6b215c9d7666af4c7102e12546f Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Wed, 26 Aug 2020 13:55:47 +0200 Subject: [PATCH 070/713] remove some duplications in computation of edges/f_vector --- .../combinatorial_polyhedron/base.pxd | 2 +- .../combinatorial_polyhedron/base.pyx | 157 ++++++++---------- 2 files changed, 73 insertions(+), 86 deletions(-) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pxd b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pxd index 0e06529039a..a318ad4c383 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pxd +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pxd @@ -62,7 +62,7 @@ cdef class CombinatorialPolyhedron(SageObject): cdef tuple _mem_tuple cdef FaceIterator _face_iter(self, bint dual, int dimension) - cdef int _compute_f_vector(self) except -1 + cdef int _compute_f_vector(self, bint compute_edges=*) except -1 cdef int _compute_edges(self, dual) except -1 cdef int _compute_ridges(self, dual) except -1 cdef int _compute_face_lattice_incidences(self) except -1 diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx index 7ff69136eec..166579dbb04 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx @@ -2769,11 +2769,14 @@ cdef class CombinatorialPolyhedron(SageObject): # Internal methods. - cdef int _compute_f_vector(self) except -1: + cdef int _compute_f_vector(self, bint compute_edges=False) except -1: r""" Compute the ``f_vector`` of the polyhedron. - See :meth:`f_vector`. + If ``compute_edges`` computes the edges in non-dual mode as well. + In dual mode the ridges. + + See :meth:`f_vector` and :meth:`_compute_edges`. """ if self._f_vector: return 0 # There is no need to recompute the f_vector. @@ -2791,6 +2794,15 @@ cdef class CombinatorialPolyhedron(SageObject): cdef int d # dimension of the current face of the iterator cdef MemoryAllocator mem = MemoryAllocator() + # In case we compute the edges as well. + cdef size_t **edges + cdef size_t counter = 0 # the number of edges so far + cdef size_t current_length # dynamically enlarge **edges + cdef size_t a,b # vertices of an edge + if compute_edges: + edges = mem.malloc(sizeof(size_t**)) + current_length = 1 + # Initialize ``f_vector``. cdef size_t *f_vector = mem.calloc((dim + 2), sizeof(size_t)) f_vector[0] = 1 # Face iterator will only visit proper faces. @@ -2803,6 +2815,17 @@ cdef class CombinatorialPolyhedron(SageObject): while (d < dim): sig_check() f_vector[d+1] += 1 + + if compute_edges and d == 1: + # If it is an edge. + + # Set up face_iter.atom_rep + face_iter.set_atom_rep() + + # Copy the information. + a = face_iter.structure.atom_rep[0] + b = face_iter.structure.atom_rep[1] + self._set_edge(a, b, &edges, &counter, ¤t_length, mem) d = face_iter.next_dimension() # Copy ``f_vector``. @@ -2824,6 +2847,21 @@ cdef class CombinatorialPolyhedron(SageObject): self._f_vector = tuple(smallInteger(f_vector[i]) for i in range(dim+2)) + if compute_edges: + # Success, copy the data to ``CombinatorialPolyhedron``. + if dual: + sig_block() + self._n_ridges = counter + self._ridges = edges + self._mem_tuple += (mem,) + sig_unblock() + else: + sig_block() + self._n_edges = counter + self._edges = edges + self._mem_tuple += (mem,) + sig_unblock() + cdef int _compute_edges(self, dual) except -1: r""" Compute the edges of the polyhedron. @@ -2839,9 +2877,6 @@ cdef class CombinatorialPolyhedron(SageObject): cdef MemoryAllocator mem = MemoryAllocator() cdef FaceIterator face_iter cdef int dim = self.dimension() - cdef int d # dimension of the current face of ``FaceIterator`` - cdef size_t *f_vector # compute f_vector, if not done already - cdef bint is_f_vector # True if f_vector was computed previously cdef size_t **edges = mem.malloc(sizeof(size_t**)) cdef size_t counter = 0 # the number of edges so far @@ -2849,12 +2884,6 @@ cdef class CombinatorialPolyhedron(SageObject): cdef size_t a,b # vertices of an edge - if self._f_vector: - is_f_vector = True - else: - # in this case we will compute the f_vector while we're at it - is_f_vector = False - if dim == 1: # In this case there is an edge, but its not a proper face. self._set_edge(0, 1, &edges, &counter, ¤t_length, mem) @@ -2896,87 +2925,45 @@ cdef class CombinatorialPolyhedron(SageObject): sig_unblock() return 0 - if is_f_vector: - # Only compute the edges. - - if not dual: - face_iter = self._face_iter(dual, 1) - else: - # ``output_dimension`` in - # :meth:`~sage.geometry.polyhedron.combinatorial_polyhedron.face_iterator.FaceIterator.__init__` - # requires the dimension of the original polyhedron - face_iter = self._face_iter(dual, dim - 2) - - if self.n_facets() > 0 and dim > 0: - # If not, there won't even be any edges. Prevent error message. - - while (face_iter.next_dimension() == 1): - # Set up face_iter.atom_rep - face_iter.set_atom_rep() - - # Copy the information. - a = face_iter.structure.atom_rep[0] - b = face_iter.structure.atom_rep[1] - self._set_edge(a, b, &edges, &counter, ¤t_length, mem) - - # Success, copy the data to ``CombinatorialPolyhedron``. - if dual: - sig_block() - self._n_ridges = counter - self._ridges = edges - self._mem_tuple += (mem,) - sig_unblock() - else: - sig_block() - self._n_edges = counter - self._edges = edges - self._mem_tuple += (mem,) - sig_unblock() - else: - face_iter = self._face_iter(dual, -2) + if not self._f_vector: # While doing the edges one might as well do the f-vector. - f_vector = mem.calloc(dim + 2, sizeof(size_t)) - f_vector[0] = 1 # This is not a proper face. - f_vector[dim + 1] = 1 # This is not a proper face. + return self._compute_f_vector(compute_edges=True) - counter = 0 - if self.n_facets() > 0 and dim > 0: - # If not, there won't even be any edges. Prevent error message. - - d = face_iter.next_dimension() - while (d < dim): - f_vector[d+1] += 1 + # Only compute the edges. - if d == 1: - # If it is an edge. + if not dual: + face_iter = self._face_iter(dual, 1) + else: + # ``output_dimension`` in + # :meth:`~sage.geometry.polyhedron.combinatorial_polyhedron.face_iterator.FaceIterator.__init__` + # requires the dimension of the original polyhedron + face_iter = self._face_iter(dual, dim - 2) - # Set up face_iter.atom_rep - face_iter.set_atom_rep() + if self.n_facets() > 0 and dim > 0: + # If not, there won't even be any edges. Prevent error message. - # Copy the information. - a = face_iter.structure.atom_rep[0] - b = face_iter.structure.atom_rep[1] - self._set_edge(a, b, &edges, &counter, ¤t_length, mem) + while (face_iter.next_dimension() == 1): + # Set up face_iter.atom_rep + face_iter.set_atom_rep() - d = face_iter.next_dimension() # Go to next face. + # Copy the information. + a = face_iter.structure.atom_rep[0] + b = face_iter.structure.atom_rep[1] + self._set_edge(a, b, &edges, &counter, ¤t_length, mem) - # Success, copy the data to ``CombinatorialPolyhedron``. - if dual: - sig_block() - self._f_vector = \ - tuple(smallInteger(f_vector[dim+1-i]) for i in range(dim+2)) - self._n_ridges = counter - self._ridges = edges - self._mem_tuple += (mem,) - sig_unblock() - else: - sig_block() - self._f_vector = \ - tuple(smallInteger(f_vector[i]) for i in range(dim+2)) - self._n_edges = counter - self._edges = edges - self._mem_tuple += (mem,) - sig_unblock() + # Success, copy the data to ``CombinatorialPolyhedron``. + if dual: + sig_block() + self._n_ridges = counter + self._ridges = edges + self._mem_tuple += (mem,) + sig_unblock() + else: + sig_block() + self._n_edges = counter + self._edges = edges + self._mem_tuple += (mem,) + sig_unblock() cdef int _compute_ridges(self, dual) except -1: r""" From 8814532248adab90b73c4335bcaf37848622a28e Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Wed, 26 Aug 2020 15:27:57 +0200 Subject: [PATCH 071/713] simplify edges and ridges of the combinatorial polyhedron --- .../combinatorial_polyhedron/base.pxd | 13 +- .../combinatorial_polyhedron/base.pyx | 205 ++++-------------- 2 files changed, 55 insertions(+), 163 deletions(-) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pxd b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pxd index a318ad4c383..010865f66a1 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pxd +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pxd @@ -62,9 +62,16 @@ cdef class CombinatorialPolyhedron(SageObject): cdef tuple _mem_tuple cdef FaceIterator _face_iter(self, bint dual, int dimension) - cdef int _compute_f_vector(self, bint compute_edges=*) except -1 - cdef int _compute_edges(self, dual) except -1 - cdef int _compute_ridges(self, dual) except -1 + cdef int _compute_f_vector(self, bint compute_edges=*, given_dual=*) except -1 + + cdef inline int _compute_edges(self, dual) except -1: + return self._compute_edges_or_ridges(dual, True) + + cdef inline int _compute_ridges(self, dual) except -1: + return self._compute_edges_or_ridges(dual, False) + + cdef int _compute_edges_or_ridges(self, bint dual, bint do_edges) except -1 + cdef size_t _compute_edges_or_ridges_with_iterator(self, FaceIterator face_iter, bint do_atom_rep, size_t ***edges_pt, size_t *counter_pt, size_t *current_length_pt, MemoryAllocator mem) except -1 cdef int _compute_face_lattice_incidences(self) except -1 cdef inline int _set_edge(self, size_t a, size_t b, size_t ***edges_pt, size_t *counter_pt, size_t *current_length_pt, MemoryAllocator mem) except -1 diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx index 166579dbb04..def4cd842ca 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx @@ -1171,7 +1171,7 @@ cdef class CombinatorialPolyhedron(SageObject): self._compute_edges(dual=False) else: # In most bounded cases, one should use the dual. - self._compute_ridges(dual=True) + self._compute_edges(dual=True) if self._edges is NULL: raise ValueError('could not determine edges') @@ -1340,7 +1340,7 @@ cdef class CombinatorialPolyhedron(SageObject): elif self.n_Vrepresentation()*self.n_Vrepresentation() < self.n_facets(): # This is a wild estimate # that in this case it is better to use the dual. - self._compute_edges(dual=True) + self._compute_ridges(dual=True) else: # In most bounded cases, one should not use the dual. self._compute_ridges(dual=False) @@ -2769,7 +2769,7 @@ cdef class CombinatorialPolyhedron(SageObject): # Internal methods. - cdef int _compute_f_vector(self, bint compute_edges=False) except -1: + cdef int _compute_f_vector(self, bint compute_edges=False, given_dual=None) except -1: r""" Compute the ``f_vector`` of the polyhedron. @@ -2788,6 +2788,10 @@ cdef class CombinatorialPolyhedron(SageObject): else: # In this case the dual approach is faster. dual = True + if given_dual is not None: + dual = given_dual + + cdef FaceIterator face_iter = self._face_iter(dual, -2) cdef int dim = self.dimension() @@ -2862,16 +2866,18 @@ cdef class CombinatorialPolyhedron(SageObject): self._mem_tuple += (mem,) sig_unblock() - cdef int _compute_edges(self, dual) except -1: + cdef int _compute_edges_or_ridges(self, bint dual, bint do_edges) except -1: r""" - Compute the edges of the polyhedron. + Compute the edges of the polyhedron if ``edges`` else the ridges. + + If ``dual``, use the face iterator in dual mode, else in non-dual. - If ``dual`` is ``True``, compute the edges of the dual. In this case, - this will also compute the ``f_vector``, if unknown. + If the ``f_vector`` is unkown computes it as well if computing the edges + in non-dual mode or the ridges in dual-mode. See :meth:`CombinatorialPolyhedron.edges` and :meth:`CombinatorialPolyhedron.ridges`. """ - if (self._edges is not NULL and not dual) or (self._ridges is not NULL and dual): + if (self._edges is not NULL and do_edges) or (self._ridges is not NULL and not do_edges): return 0 # There is no need to recompute. cdef MemoryAllocator mem = MemoryAllocator() @@ -2881,187 +2887,66 @@ cdef class CombinatorialPolyhedron(SageObject): cdef size_t **edges = mem.malloc(sizeof(size_t**)) cdef size_t counter = 0 # the number of edges so far cdef size_t current_length = 1 # dynamically enlarge **edges + cdef int output_dim_init = 1 if do_edges else dim - 2 - cdef size_t a,b # vertices of an edge - - if dim == 1: - # In this case there is an edge, but its not a proper face. + if dim == 1 and (do_edges or self.n_facets() > 1): + # In this case there is an edge/ridge, but its not a proper face. self._set_edge(0, 1, &edges, &counter, ¤t_length, mem) - # Success, copy the data to ``CombinatorialPolyhedron``. - if dual: - # We have actually computed the ridges. - sig_block() - self._n_ridges = counter - self._ridges = edges - self._mem_tuple += (mem,) - sig_unblock() - else: - sig_block() - self._n_edges = counter - self._edges = edges - self._mem_tuple += (mem,) - sig_unblock() - return 0 - - if dim == 0: - # There is no edge. - # Prevent calling the face iterator with an improper dimension. - counter = 0 - - # Success, copy the data to ``CombinatorialPolyhedron``. - if dual: - # We have actually computed the ridges. - sig_block() - self._n_ridges = counter - self._ridges = edges - self._mem_tuple += (mem,) - sig_unblock() - else: - sig_block() - self._n_edges = counter - self._edges = edges - self._mem_tuple += (mem,) - sig_unblock() - return 0 - - if not self._f_vector: - # While doing the edges one might as well do the f-vector. - return self._compute_f_vector(compute_edges=True) + elif dim <= 1 or self.n_facets() == 0: + # There is no edge/ridge. + # Prevent an error when calling the face iterator. + pass - # Only compute the edges. + elif not self._f_vector and ((dual ^ do_edges)): + # While doing edges in non-dual mode or ridges in dual-mode + # one might as well do the f-vector. + return self._compute_f_vector(compute_edges=True, given_dual=dual) - if not dual: - face_iter = self._face_iter(dual, 1) else: - # ``output_dimension`` in - # :meth:`~sage.geometry.polyhedron.combinatorial_polyhedron.face_iterator.FaceIterator.__init__` - # requires the dimension of the original polyhedron - face_iter = self._face_iter(dual, dim - 2) + # Only compute the edges/ridges. + face_iter = self._face_iter(dual, output_dim_init) - if self.n_facets() > 0 and dim > 0: - # If not, there won't even be any edges. Prevent error message. - - while (face_iter.next_dimension() == 1): - # Set up face_iter.atom_rep - face_iter.set_atom_rep() - - # Copy the information. - a = face_iter.structure.atom_rep[0] - b = face_iter.structure.atom_rep[1] - self._set_edge(a, b, &edges, &counter, ¤t_length, mem) + self._compute_edges_or_ridges_with_iterator(face_iter, (dual ^ do_edges), &edges, &counter, ¤t_length, mem) # Success, copy the data to ``CombinatorialPolyhedron``. - if dual: + if do_edges: sig_block() - self._n_ridges = counter - self._ridges = edges + self._n_edges = counter + self._edges = edges self._mem_tuple += (mem,) sig_unblock() else: sig_block() - self._n_edges = counter - self._edges = edges + self._n_ridges = counter + self._ridges = edges self._mem_tuple += (mem,) sig_unblock() - cdef int _compute_ridges(self, dual) except -1: + cdef size_t _compute_edges_or_ridges_with_iterator(self, FaceIterator face_iter, bint do_atom_rep, size_t ***edges_pt, size_t *counter_pt, size_t *current_length_pt, MemoryAllocator mem) except -1: r""" - Compute the ridges of the polyhedron. - - If ``dual`` is ``True``, compute the ridges of the dual. - - See :meth:`edges` and :meth:`ridges`. + See :meth:`CombinatorialPolyhedron._compute_edges`. """ - if (self._edges is not NULL and dual) or (self._ridges is not NULL and not dual): - return 0 # There is no need to recompute. - - cdef MemoryAllocator mem = MemoryAllocator() - cdef FaceIterator face_iter + cdef size_t a,b # facets of an edge cdef int dim = self.dimension() + cdef output_dimension = 1 if do_atom_rep else dim - 2 - # For each ridge we determine its location in ``ridges`` - # by ``ridges[one][two]``. - cdef size_t **ridges = mem.malloc(sizeof(size_t**)) - - cdef size_t counter = 0 # the number of ridges so far - cdef size_t current_length = 1 # dynamically enlarge **ridges - - cdef size_t a,b # facets of a ridge - - if dim == 1 and self.n_facets() > 1: - # In this case there is a ridge, but its not a proper face. - self._set_edge(0, 1, &ridges, &counter, ¤t_length, mem) - - # Success, copy the data to ``CombinatorialPolyhedron``. - if not dual: - sig_block() - self._n_ridges = counter - self._ridges = ridges - self._mem_tuple += (mem,) - sig_unblock() - else: - sig_block() - self._n_edges = counter - self._edges = ridges - self._mem_tuple += (mem,) - sig_unblock() - return 0 - - if dim <= 1: - # There is no ridge, but face iterator expects a proper face dimension as input. - counter = 0 + while (face_iter.next_dimension() == output_dimension): + if do_atom_rep: + # Set up face_iter.atom_rep + face_iter.set_atom_rep() - # Success, copy the data to ``CombinatorialPolyhedron``. - if not dual: - sig_block() - self._n_ridges = counter - self._ridges = ridges - self._mem_tuple += (mem,) - sig_unblock() + # Copy the information. + a = face_iter.structure.atom_rep[0] + b = face_iter.structure.atom_rep[1] else: - sig_block() - self._n_edges = counter - self._edges = ridges - self._mem_tuple += (mem,) - sig_unblock() - return 0 - - if dual: - # ``output_dimension`` in - # :meth:`FaceIterator.__init` - # requires the dimension of the original polyhedron - face_iter = self._face_iter(dual, 1) - else: - face_iter = self._face_iter(dual, dim -2) - - if self.n_facets() > 1 and dim > 0: - # If not, there won't even be any ridges - # as intersection of two distinct facets. - # Prevent error message. - - while (face_iter.next_dimension() == dim - 2): # Set up face_iter.coatom_rep face_iter.set_coatom_rep() # Copy the information. a = face_iter.structure.coatom_rep[0] b = face_iter.structure.coatom_rep[1] - self._set_edge(a, b, &ridges, &counter, ¤t_length, mem) - - # Success, copy the data to ``CombinatorialPolyhedron``. - if not dual: - sig_block() - self._n_ridges = counter - self._ridges = ridges - self._mem_tuple += (mem,) - sig_unblock() - else: - sig_block() - self._n_edges = counter - self._edges = ridges - self._mem_tuple += (mem,) - sig_unblock() + self._set_edge(a, b, edges_pt, counter_pt, current_length_pt, mem) cdef int _compute_face_lattice_incidences(self) except -1: r""" From 536785851ff1731af9f7aa4539554c2a1ef8d633 Mon Sep 17 00:00:00 2001 From: Ivo Maffei Date: Thu, 27 Aug 2020 10:26:23 +0200 Subject: [PATCH 072/713] some code formatting --- .../graphs/generators/distance_regular.pyx | 33 ++++++++++--------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/src/sage/graphs/generators/distance_regular.pyx b/src/sage/graphs/generators/distance_regular.pyx index e1a6ac60dd6..9bf0078ebec 100644 --- a/src/sage/graphs/generators/distance_regular.pyx +++ b/src/sage/graphs/generators/distance_regular.pyx @@ -1269,7 +1269,7 @@ def GeneralisedDodecagonGraph(const int s, const int t): sage: H = graphs.GeneralisedDodecagonGraph(5, 1) # optional - gap_packages internet sage: H.order() # optional - gap_packages internet 23436 - sage: H.is_distance_regular(True) # long time (6 min); optional - gap_packages internet + sage: H.is_distance_regular(True) # not tested (6 min); optional - gap_packages internet ([10, 5, 5, 5, 5, 5, None], [None, 1, 1, 1, 1, 1, 2]) .. NOTE:: @@ -1325,14 +1325,16 @@ def GeneralisedDodecagonGraph(const int s, const int t): q = s orderType = 1 else: - raise ValueError(f"No generalised dodacagon of order ({s}, {t}) exists") + raise ValueError( + f"No generalised dodacagon of order ({s}, {t}) is known") if q == 1: # order (1, 1) from sage.graphs.generators.basic import CycleGraph return CycleGraph(12) if not is_prime_power(q): - raise ValueError(f"No generalised dodacagon of order ({s}, {t}) exists") + raise ValueError( + f"No generalised dodacagon of order ({s}, {t}) is known") if orderType == 0: # incidence graph of hexagon (q,q) @@ -1396,7 +1398,7 @@ def GeneralisedOctagonGraph(const int s, const int t): sage: G = graphs.GeneralisedOctagonGraph(4, 16) Traceback (most recent call last): ... - ValueError: generalised octagons of order (q, q^2) exist only for q odd powers of 2 + ValueError: generalised octagons of order (q, q^2) are known only for q odd powers of 2 """ from sage.arith.misc import is_prime_power @@ -1417,20 +1419,20 @@ def GeneralisedOctagonGraph(const int s, const int t): if p != 2 or k % 2 != 1: raise ValueError(("generalised octagons of order (q, q^2) " - "exist only for q odd powers of 2")) + "are known only for q odd powers of 2")) orderType = 2 elif t**2 == s: # (q^2, q) q = t orderType = 1 else: - raise ValueError(f"No generalised octagon of order ({s}, {t}) exists") + raise ValueError(f"No generalised octagon of order ({s}, {t}) is known") if q == 1: # order (1, 1) from sage.graphs.generators.basic import CycleGraph return CycleGraph(8) if not is_prime_power(q): - raise ValueError(f"No generalised octagon of order ({s}, {t}) exists") + raise ValueError(f"No generalised octagon of order ({s}, {t}) is known") if orderType == 0: # incidence graph of generalised quadrangle (q, q) @@ -1452,7 +1454,7 @@ def GeneralisedOctagonGraph(const int s, const int t): elif orderType == 1: # dual - H = GeneralisedOctagonGraph(t,s) + H = GeneralisedOctagonGraph(t, s) G = _line_graph_generalised_polygon(H) G.name("Generalised octagon of order(%d, %d)"%(s, t)) return G @@ -1535,18 +1537,18 @@ def GeneralisedHexagonGraph(const int s, const int t): q = t orderType = 1 else: - raise ValueError(f"No generalised octagon of order ({s}, {t}) exists") + raise ValueError(f"No generalised octagon of order ({s}, {t}) is known") if q == 1: # order (1, 1) from sage.graphs.generators.basic import CycleGraph return CycleGraph(6) if not is_prime_power(q): - raise ValueError(f"No generalised octagon of order ({s}, {t}) exists") + raise ValueError(f"No generalised octagon of order ({s}, {t}) is known") if orderType == 0: # incident graph of generalised 3-gon of order (q, q) - PG2 = designs.ProjectiveGeometryDesign(2,1,q) + PG2 = designs.ProjectiveGeometryDesign(2, 1, q) edges = [] for l in PG2.blocks(): @@ -1607,7 +1609,7 @@ def GeneralisedHexagonGraph(const int s, const int t): movedPoints = 819 if q==2 else 26572 group = libgap.AtlasGroup("3D4(%d)"%q, libgap.NrMovedPoints, movedPoints) - G = Graph(libgap.Orbit(group, [1, 2],libgap.OnSets), + G = Graph(libgap.Orbit(group, [1, 2], libgap.OnSets), format='list_of_edges') G.name("Generalised hexagon of order (%d, %d)"%(q, q**3)) return G @@ -1627,7 +1629,7 @@ def _extract_lines(G): OUTPUT: - A list of tuples where each tuple represent a line through the vertices + A list of tuples where each tuple represent a line via the vertices contained in that line. EXAMPLES:: @@ -1642,7 +1644,7 @@ def _extract_lines(G): sage: line = lines[0] sage: type(line) - sage: line[0] in G # elements in line are vertices + sage: line[0] in G # the elements in the line are vertices True REFERENCES: @@ -1675,11 +1677,12 @@ def _extract_lines(G): # remove already handled edges for u, v in itertools.product(bot2, repeat=2): + sig_check() try : edges.remove((u, v)) except KeyError: pass # ignore this - #end while edges + # end while edges return lines From 35b8ebd98c2cf2be8bd90ed360b4aa5dd0a4fc1e Mon Sep 17 00:00:00 2001 From: Ivo Maffei Date: Thu, 27 Aug 2020 10:50:17 +0200 Subject: [PATCH 073/713] added graphs from GQ to general function --- src/sage/graphs/generators/distance_regular.pyx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/sage/graphs/generators/distance_regular.pyx b/src/sage/graphs/generators/distance_regular.pyx index e43ea9e9e78..ba9c9956b76 100644 --- a/src/sage/graphs/generators/distance_regular.pyx +++ b/src/sage/graphs/generators/distance_regular.pyx @@ -2206,7 +2206,9 @@ _sporadic_graph_database = { } _infinite_families_database = [ - (is_classical_parameters_graph, graph_with_classical_parameters)] + (is_classical_parameters_graph, graph_with_classical_parameters), + (is_from_GQ_spread, graph_from_GQ_spread), +] def distance_regular_graph(list arr, existence=False, check=True): r""" @@ -2262,6 +2264,8 @@ def distance_regular_graph(list arr, existence=False, check=True): sage: graphs.distance_regular_graph([14, 12, 10, 8, 6, 4, 2, ....: 1, 2, 3, 4, 5, 6, 7]) Hamming Graph with parameters 7,3: Graph on 2187 vertices + sage: graphs.distance_regular_graph([64, 60, 1, 1, 15, 64], check=True) + Graph on 325 vertices """ from sage.misc.unknown import Unknown from sage.categories.sets_cat import EmptySetError From d127e7a81c27aaca23b24334277130b2eae7530b Mon Sep 17 00:00:00 2001 From: Dima Pasechnik Date: Fri, 28 Aug 2020 22:11:49 +0100 Subject: [PATCH 074/713] fix typos, English, cut/paste errors --- .../graphs/generators/distance_regular.pyx | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/sage/graphs/generators/distance_regular.pyx b/src/sage/graphs/generators/distance_regular.pyx index 9bf0078ebec..d8ddca7917e 100644 --- a/src/sage/graphs/generators/distance_regular.pyx +++ b/src/sage/graphs/generators/distance_regular.pyx @@ -1326,7 +1326,7 @@ def GeneralisedDodecagonGraph(const int s, const int t): orderType = 1 else: raise ValueError( - f"No generalised dodacagon of order ({s}, {t}) is known") + f"Generalised dodecagon of order ({s}, {t}) does not exist") if q == 1: # order (1, 1) from sage.graphs.generators.basic import CycleGraph @@ -1334,7 +1334,7 @@ def GeneralisedDodecagonGraph(const int s, const int t): if not is_prime_power(q): raise ValueError( - f"No generalised dodacagon of order ({s}, {t}) is known") + f"No generalised dodecagon of order ({s}, {t}) is known") if orderType == 0: # incidence graph of hexagon (q,q) @@ -1398,7 +1398,7 @@ def GeneralisedOctagonGraph(const int s, const int t): sage: G = graphs.GeneralisedOctagonGraph(4, 16) Traceback (most recent call last): ... - ValueError: generalised octagons of order (q, q^2) are known only for q odd powers of 2 + ValueError: generalised octagons of order (q, q^2) are known only for odd powers q of 2 """ from sage.arith.misc import is_prime_power @@ -1419,7 +1419,7 @@ def GeneralisedOctagonGraph(const int s, const int t): if p != 2 or k % 2 != 1: raise ValueError(("generalised octagons of order (q, q^2) " - "are known only for q odd powers of 2")) + "are known only for odd powers q of 2")) orderType = 2 elif t**2 == s: # (q^2, q) q = t @@ -1471,11 +1471,11 @@ def GeneralisedOctagonGraph(const int s, const int t): def GeneralisedHexagonGraph(const int s, const int t): r""" - Return the point-graph of a generalised octagon of order `(s,t)`. + Return the point-graph of a generalised hexagon of order `(s,t)`. INPUT: - - ``s, t`` -- integers; order of the generalised octagon + - ``s, t`` -- integers; order of the generalised hexagon EXAMPLES:: @@ -1490,7 +1490,7 @@ def GeneralisedHexagonGraph(const int s, const int t): .. NOTE:: - This function uses the GAP's AtlasRep package to build the graphs + This function uses the GAP's AtlasRep package to build GHs of order `(q, q)`, `(q, q^3)` or `(q^3, q)`. For those graphs you need an internet connection and Sage's optional package ``gap_packages``. @@ -1537,17 +1537,17 @@ def GeneralisedHexagonGraph(const int s, const int t): q = t orderType = 1 else: - raise ValueError(f"No generalised octagon of order ({s}, {t}) is known") + raise ValueError(f"No generalised hexagon of order ({s}, {t}) is known") if q == 1: # order (1, 1) from sage.graphs.generators.basic import CycleGraph return CycleGraph(6) if not is_prime_power(q): - raise ValueError(f"No generalised octagon of order ({s}, {t}) is known") + raise ValueError(f"No generalised hexagon of order ({s}, {t}) is known") if orderType == 0: - # incident graph of generalised 3-gon of order (q, q) + # incidence graph of generalised 3-gon of order (q, q) PG2 = designs.ProjectiveGeometryDesign(2, 1, q) edges = [] From 625b266bb08bfdccacd4fd2d4cc8bb6bf232385d Mon Sep 17 00:00:00 2001 From: Ivo Maffei Date: Sat, 29 Aug 2020 11:27:21 +0200 Subject: [PATCH 075/713] added doctests to _integersection_array_from_graph --- .../graphs/generators/distance_regular.pyx | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/sage/graphs/generators/distance_regular.pyx b/src/sage/graphs/generators/distance_regular.pyx index e42c62c7c02..136afbe981b 100644 --- a/src/sage/graphs/generators/distance_regular.pyx +++ b/src/sage/graphs/generators/distance_regular.pyx @@ -1756,10 +1756,28 @@ def _intersection_array_from_graph(G): EXAMPLES:: + sage: from sage.graphs.generators.distance_regular import \ + ....: _intersection_array_from_graph + sage: _intersection_array_from_graph(graphs.FosterGraph()) + [3, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 3] + sage: graphs.FosterGraph().is_distance_regular(True) + ([3, 2, 2, 2, 2, 1, 1, 1, None], [None, 1, 1, 1, 1, 2, 2, 2, 3]) + sage: graphs.DartGraph().is_distance_regular() + False + sage: _intersection_array_from_graph(graphs.DartGraph()) + False + TESTS:: + sage: from sage.graphs.generators.distance_regular import \ + ....: _intersection_array_from_graph + sage: _intersection_array_from_graph(Graph()) + [] + sage: _intersection_array_from_graph(Graph(3)) + [] + sage: _intersection_array_from_graph(graphs.CompleteGraph(7)) + [6, 1] """ - t = G.is_distance_regular(True) if t is False: return False From e455b1385b700a8c28a07badac1da4565ddbe837 Mon Sep 17 00:00:00 2001 From: "Alex J. Best" Date: Tue, 1 Sep 2020 18:01:11 -0400 Subject: [PATCH 076/713] turn patch for 9407 into a git branch, switch to Pari for fixed field --- src/sage/modular/dirichlet.py | 185 +++++++++++++++++++- src/sage/rings/number_field/number_field.py | 142 +++++++++++++++ 2 files changed, 326 insertions(+), 1 deletion(-) diff --git a/src/sage/modular/dirichlet.py b/src/sage/modular/dirichlet.py index 0313f303357..94ad536630d 100644 --- a/src/sage/modular/dirichlet.py +++ b/src/sage/modular/dirichlet.py @@ -66,7 +66,7 @@ import sage.rings.all as rings import sage.rings.number_field.number_field as number_field from sage.libs.pari import pari - + from sage.categories.map import Map from sage.rings.rational_field import is_RationalField from sage.rings.complex_field import is_ComplexField @@ -804,6 +804,189 @@ def conductor(self): cond *= 2 return rings.Integer(cond) + @cached_method + def fixed_field_polynomial(self, algorithm = "pari"): + r""" + Give a Dirichlet character this will return a + polynomial generating the abelian extension fixed by the kernel + of the corresponding Galois character. + + ALGORITHM: (Sage) A formula by Gauss for the products of periods; see Disquisitiones §343. See the source code for more. + + OUTPUT: + + - a poynomial with integer coefficients + + EXAMPLES:: + + sage: G = DirichletGroup(37) + sage: chi = G.0 + sage: psi = chi^18 + sage: psi.fixed_field_polynomial() + x^2 + x - 9 + + sage: G = DirichletGroup(7) + sage: chi = G.0^2 + sage: chi + Dirichlet character modulo 7 of conductor 7 mapping 3 |--> zeta6 - 1 + sage: chi.fixed_field_polynomial() + x^3 + x^2 - 2*x - 1 + + sage: G = DirichletGroup(31) + sage: chi = G.0 + sage: chi^6 + Dirichlet character modulo 31 of conductor 31 mapping 3 |--> zeta30^6 + sage: psi = chi^6 + sage: psi.fixed_field_polynomial() + x^5 + x^4 - 12*x^3 - 21*x^2 + x + 5 + + sage: G = DirichletGroup(7) + sage: chi = G.0 + sage: chi.fixed_field_polynomial() + x^6 + x^5 + x^4 + x^3 + x^2 + x + 1 + + sage: G = DirichletGroup(1001) + sage: chi = G.0 + sage: psi = chi^3 + sage: psi.order() + 2 + sage: psi.fixed_field_polynomial() + x^2 + x + 2 + + + """ + + # this algorithm was written by Francis Clarke see ticket #9407 + + from sage.rings.finite_rings.integer_mod_ring import IntegerModRing + from sage.rings.integer_ring import IntegerRing + ZZ = IntegerRing() + from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing + from sage.modules.free_module import FreeModule + from sage.matrix.constructor import matrix + + if algorithm == "sage": + n = ZZ(self.conductor()) + if not n.is_prime(): + raise NotImplementedError('the conductor %s is supposed to be prime' % n) + + d = self.order() + + # check that there will be such a field of degree d inside QQ(zeta_n) + if euler_phi(n) % d != 0: + raise ValueError('No field exists because %s does not divide %s=phi(%s)' % (d,euler_phi(n),n)) + f = euler_phi(n)/d + + S = PolynomialRing(ZZ, 'x') + + if f == 1: + from sage.misc.functional import cyclotomic_polynomial + return cyclotomic_polynomial(n, S.gen()) + + if d == 2: + if n.mod(4) == 1: + s = -1 + else: + s = 1 + return S([s*(n + s)/4, 1, 1]) + + # Using the notation of van der Waerden, where $\zeta$ is a primitive + # $n$-root of unity, + # $$ + # \eta_i = \sum_{j=0}^{f-1}\zeta^{g^{i+dj}}, + # $$ + # is represented by eta[i] as the list of exponents. + # + # gen_index is a dictionary such that gen_index[r] = i if the exponent r + # occurs in eta[i]. Thus $\eta^{(r)} = \eta_i$ in van der Waerden's + # notation. + + R = IntegerModRing(n) + g = R.unit_gens()[0] + gen_index = {} + eta = [] + for i in range(d): + eta.append([]) + for j in range(f): + r = g**(i + d*j) + eta[i].append(r) + gen_index[r] = i + + # Using Gauss's formula + # $$ + # \eta^{(r)}\eta^{(s)} = \sum_{j=0}^{f-1}\eta^{(r+sg^{dj})} + # $$ + # (with $r=1$), we construct the matrix representing multiplication by + # $\eta_0=\eta^{(1)}$ with respect to the basis consisting of the $\eta_i$. + # Its characteristic polynomial generates the field. The element + # $\eta^(0)$=f=-f\sum_{i=0}^{d-1}\eta_i$ is represented by eta_zero. + + V = FreeModule(ZZ, d) + eta_zero = V([-f]*d) + m = [] + for j in range(d): + v = 0 + for e in eta[j]: + try: + s = V.gen(gen_index[1 + e]) + except KeyError: + s = eta_zero + v += s + m.append(v) + + m = matrix(m) + + xx = S.gen() + return m.charpoly(xx) + + # Use pari + G,chi = self._pari_conversion() + K=pari.charker(G,chi) + H = pari.galoissubcyclo(G,K); + P = PolynomialRing(rings.RationalField(),"x") + x = P.gen() + return H.sage({"x":x}) + + + def fixed_field(self): + r""" + Give a Dirichlet character this will return the abelian extension + fixed by the kernel of the corresponding Galois character. + + OUTPUT: + + - a number field + + EXAMPLES:: + + sage: G = DirichletGroup(37) + sage: chi = G.0 + sage: psi = chi^18 + sage: psi.fixed_field() + Number Field in a with defining polynomial x^2 + x - 9 + + + sage: G = DirichletGroup(7) + sage: chi = G.0^2 + sage: chi + Dirichlet character modulo 7 of conductor 7 mapping 3 |--> zeta6 - 1 + sage: chi.fixed_field() + Number Field in a with defining polynomial x^3 + x^2 - 2*x - 1 + + sage: G = DirichletGroup(31) + sage: chi = G.0 + sage: chi^6 + Dirichlet character modulo 31 of conductor 31 mapping 3 |--> zeta30^6 + sage: psi = chi^6 + sage: psi.fixed_field() + Number Field in a with defining polynomial x^5 + x^4 - 12*x^3 - 21*x^2 + x + 5 + + """ + from sage.rings.number_field.number_field import NumberField + poly = self.fixed_field_polynomial() + K = NumberField(poly, 'a') + return K + @cached_method def decomposition(self): r""" diff --git a/src/sage/rings/number_field/number_field.py b/src/sage/rings/number_field/number_field.py index dddcbd1b3c3..c8f3af9dba9 100644 --- a/src/sage/rings/number_field/number_field.py +++ b/src/sage/rings/number_field/number_field.py @@ -33,6 +33,9 @@ - Anna Haensch (2018-03): added :meth:``quadratic_defect`` +- Michael Daub, Chris Wuthrich (2020-09-01): adding dirichlet characters for abelian fields + + .. note:: Unlike in PARI/GP, class group computations *in Sage* do *not* by default @@ -231,6 +234,7 @@ def proof_flag(t): from sage.rings.real_double import RDF from sage.rings.complex_double import CDF from sage.rings.real_lazy import RLF, CLF +from sage.rings.finite_rings.integer_mod_ring import IntegerModRing def NumberField(polynomial, name=None, check=True, names=None, embedding=None, latex_name=None, assume_disc_small=False, maximize_at_primes=None, structure=None, **kwds): @@ -3237,6 +3241,63 @@ def conductor(self, check_abelian=True): m *= p**(e.valuation(p)+1) return m + def dirichlet_group(self): + r""" + Given a abelian field `K`, this computes and returns the + set of all Dirichlet characters corresponding to the + characters of the Galois group of `K/\mathbb{Q}`. + + The output is random if the field is not abelian + + OUTPUT: + + - a list of Dirichlet characters + + EXAMPLES:: + + sage: K. = NumberField(x^3+x^2-36*x-4) + sage: K.conductor() + 109 + sage: K.dirichlet_group() + [Dirichlet character modulo 109 of conductor 1 mapping 6 |--> 1, + Dirichlet character modulo 109 of conductor 109 mapping 6 |--> zeta3, + Dirichlet character modulo 109 of conductor 109 mapping 6 |--> -zeta3 - 1] + + sage: K = CyclotomicField(44) + sage: L = K.subfields(5)[0][0] + sage: X = L.dirichlet_group() + sage: X + [Dirichlet character modulo 11 of conductor 1 mapping 2 |--> 1, + Dirichlet character modulo 11 of conductor 11 mapping 2 |--> zeta5, + Dirichlet character modulo 11 of conductor 11 mapping 2 |--> zeta5^2, + Dirichlet character modulo 11 of conductor 11 mapping 2 |--> zeta5^3, + Dirichlet character modulo 11 of conductor 11 mapping 2 |--> -zeta5^3 - zeta5^2 - zeta5 - 1] + sage: X[4]^2 + Dirichlet character modulo 11 of conductor 11 mapping 2 |--> zeta5^3 + sage: X[4]^2 in X + True + + """ + #todo : turn this into an abelian group rather than a list. + from sage.modular.dirichlet import DirichletGroup + + m = self.conductor() + d = self.degree() + A = _splitting_classes_gens_(self,m,d) + n = len(A) + # d could be improve to be the exponenent of the Galois group rather than the degree, but I do not see how to how about it already. + G = DirichletGroup(m, CyclotomicField(d)) + H = [G(1)] + for chi in G: + if len(H) == d: + break + if chi not in H: + if prod( chi(a)==1 for a in A): + H.append(chi) + return H + + + def latex_variable_name(self, name=None): """ Return the latex representation of the variable name for this @@ -5604,6 +5665,7 @@ def is_field(self, proof=True): """ return True + @cached_method def is_galois(self): r""" Return True if this number field is a Galois extension of @@ -10844,6 +10906,18 @@ def is_galois(self): """ return True + def is_abelian(self): + """ + Return True since all cyclotomic fields are automatically abelian. + + EXAMPLES:: + + sage: CyclotomicField(29).is_abelian() + True + """ + return True + + def is_isomorphic(self, other): """ Return True if the cyclotomic field self is isomorphic as a number @@ -11956,3 +12030,71 @@ def is_real_place(v): return True except TypeError: return False + +def _splitting_classes_gens_(K,m,d): + r""" + Given a number field `K` of conductor `m` and degree `d`, + this returns a set of multiplicative generators of the + subgroup of `(\mathbb{Z}/m\mathbb{Z})^{\times}` + containing exactly the classes that contain the primes splitting + completely in `K`. + + EXAMPLES:: + + sage: from sage.rings.number_field.number_field import _splitting_classes_gens_ + sage: K = CyclotomicField(101) + sage: L = K.subfields(20)[0][0] + sage: L.conductor() + 101 + sage: _splitting_classes_gens_(L,101,20) + [95] + + sage: K = CyclotomicField(44) + sage: L = K.subfields(4)[0][0] + sage: _splitting_classes_gens_(L,44,4) + [37] + + sage: K = CyclotomicField(44) + sage: L = K.subfields(5)[0][0] + sage: K.degree() + 20 + sage: L + Number Field in zeta44_0 with defining polynomial x^5 - 2*x^4 - 16*x^3 + 24*x^2 + 48*x - 32 with zeta44_0 = 3.837971894457990? + sage: L.conductor() + 11 + sage: _splitting_classes_gens_(L,11,5) + [10] + + """ + from sage.groups.abelian_gps.abelian_group import AbelianGroup + + R = K.ring_of_integers() + Zm = IntegerModRing(m) + unit_gens = Zm.unit_gens() + Zmstar = AbelianGroup(len(unit_gens), [x.multiplicative_order() for x in unit_gens]) + + def map_Zmstar_to_Zm(h): + li = h.list() + return prod(unit_gens[i]**li[i] for i in range(len(unit_gens))) + + Hgens = [] + A = [] + H = Zmstar.subgroup([]) + p = 0 + Horder = arith.euler_phi(m)/d + for g in Zmstar: + if H.order() == Horder: + break + if g not in H: + u = map_Zmstar_to_Zm(g) + p = u.lift() + while not p.is_prime(): + p += m + f = R.ideal(p).prime_factors()[0].residue_class_degree() + h = g**f + if h not in H: + Hgens += [h] + H = Zmstar.subgroup(Hgens) + + A = [map_Zmstar_to_Zm(h) for h in Hgens] + return A From d0bc9901b036d552bbb7ffa011df7f64fda7633f Mon Sep 17 00:00:00 2001 From: "Alex J. Best" Date: Tue, 1 Sep 2020 18:30:55 -0400 Subject: [PATCH 077/713] clearer control flow --- src/sage/modular/dirichlet.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/sage/modular/dirichlet.py b/src/sage/modular/dirichlet.py index 94ad536630d..79b96fe31e2 100644 --- a/src/sage/modular/dirichlet.py +++ b/src/sage/modular/dirichlet.py @@ -939,13 +939,17 @@ def fixed_field_polynomial(self, algorithm = "pari"): xx = S.gen() return m.charpoly(xx) - # Use pari - G,chi = self._pari_conversion() - K=pari.charker(G,chi) - H = pari.galoissubcyclo(G,K); - P = PolynomialRing(rings.RationalField(),"x") - x = P.gen() - return H.sage({"x":x}) + elif algorithm == "pari": + # Use pari + G,chi = self._pari_conversion() + K=pari.charker(G,chi) + H = pari.galoissubcyclo(G,K); + P = PolynomialRing(rings.RationalField(),"x") + x = P.gen() + return H.sage({"x":x}) + + else: + raise NotImplentedError("algorithm must be one of 'pari' or 'sage'") def fixed_field(self): From 5b216140cd930e824ba5629e15475c443a9d7d6f Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Wed, 9 Sep 2020 10:28:47 +0200 Subject: [PATCH 078/713] use reference so simplify code --- .../face_iterator.pxd | 6 +- .../face_iterator.pyx | 98 +++++++++---------- 2 files changed, 52 insertions(+), 52 deletions(-) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd index 09e803da9bc..f8809ef988b 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd @@ -90,6 +90,6 @@ cdef class FaceIterator_geom(FaceIterator_base): # Nogil definitions of crucial functions. -cdef int next_dimension(iter_struct *structptr) nogil except -1 -cdef int next_face_loop(iter_struct *structptr) nogil except -1 -cdef size_t n_atom_rep(iter_struct *structptr) nogil except -1 +cdef int next_dimension(iter_struct& structure) nogil except -1 +cdef int next_face_loop(iter_struct& structure) nogil except -1 +cdef size_t n_atom_rep(iter_struct& structure) nogil except -1 diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx index bc2cc403ee9..2aa47dc66a9 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx @@ -617,7 +617,7 @@ cdef class FaceIterator_base(SageObject): visiting sub-/supfaces instead of after. One cannot arbitrarily add faces to ``visited_all``, as visited_all has a maximal length. """ - return next_dimension(&self.structure) + return next_dimension(self.structure) cdef inline int next_face_loop(self) except -1: r""" @@ -627,7 +627,7 @@ cdef class FaceIterator_base(SageObject): If ``self.current_dimension == self.dimension``, then the iterator is consumed. """ - return next_face_loop(&self.structure) + return next_face_loop(self.structure) cdef inline size_t n_atom_rep(self) except -1: r""" @@ -636,7 +636,7 @@ cdef class FaceIterator_base(SageObject): This is a shortcut of :class:`sage.geometry.polyhedron.combinatorial_polyhedron.combinatorial_face.CombinatorialFace.n_atom_rep` """ - return n_atom_rep(&self.structure) + return n_atom_rep(self.structure) if self.structure.face: return count_atoms(self.structure.face, self.structure.face_length) @@ -1281,95 +1281,95 @@ cdef class FaceIterator_geom(FaceIterator_base): # Nogil definitions of crucial functions. -cdef inline int next_dimension(iter_struct *structptr) nogil except -1: +cdef inline int next_dimension(iter_struct& structure) nogil except -1: r""" See :meth:`FaceIterator.next_dimension`. """ - cdef int dim = structptr[0].dimension - while (not next_face_loop(structptr)) and (structptr[0].current_dimension < dim): + cdef int dim = structure.dimension + while (not next_face_loop(structure)) and (structure.current_dimension < dim): sig_check() - structptr[0]._index += 1 - return structptr[0].current_dimension + structure._index += 1 + return structure.current_dimension -cdef inline int next_face_loop(iter_struct *structptr) nogil except -1: +cdef inline int next_face_loop(iter_struct& structure) nogil except -1: r""" See :meth:`FaceIterator.next_face_loop`. """ - if unlikely(structptr[0].current_dimension == structptr[0].dimension): + if unlikely(structure.current_dimension == structure.dimension): # The function is not supposed to be called, # just prevent it from crashing. raise StopIteration # Getting ``[faces, n_faces, n_visited_all]`` according to dimension. - cdef uint64_t **faces = structptr[0].newfaces[structptr[0].current_dimension] - cdef size_t n_faces = structptr[0].n_newfaces[structptr[0].current_dimension] - cdef size_t n_visited_all = structptr[0].n_visited_all[structptr[0].current_dimension] + cdef uint64_t **faces = structure.newfaces[structure.current_dimension] + cdef size_t n_faces = structure.n_newfaces[structure.current_dimension] + cdef size_t n_visited_all = structure.n_visited_all[structure.current_dimension] cdef uint64_t **faces_coatom_rep - if structptr[0].is_simple: - faces_coatom_rep = structptr[0].newfaces_coatom_rep[structptr[0].current_dimension] + if structure.is_simple: + faces_coatom_rep = structure.newfaces_coatom_rep[structure.current_dimension] - if (structptr[0].output_dimension > -2) and (structptr[0].output_dimension != structptr[0].current_dimension): + if (structure.output_dimension > -2) and (structure.output_dimension != structure.current_dimension): # If only a specific dimension was requested (i.e. ``output_dimension > -2``), # then we will not yield faces in other dimension. - structptr[0].yet_to_visit = 0 + structure.yet_to_visit = 0 - if structptr[0].yet_to_visit: + if structure.yet_to_visit: # Set ``face`` to the next face. - structptr[0].yet_to_visit -= 1 - structptr[0].face = faces[structptr[0].yet_to_visit] - if structptr[0].is_simple: - structptr[0].face_coatom_rep = faces_coatom_rep[structptr[0].yet_to_visit] + structure.yet_to_visit -= 1 + structure.face = faces[structure.yet_to_visit] + if structure.is_simple: + structure.face_coatom_rep = faces_coatom_rep[structure.yet_to_visit] return 1 - if structptr[0].current_dimension <= structptr[0].lowest_dimension: + if structure.current_dimension <= structure.lowest_dimension: # We will not yield the empty face. # We will not yield below requested dimension. - structptr[0].current_dimension += 1 + structure.current_dimension += 1 return 0 if n_faces <= 1: # There will be no more faces from intersections. - structptr[0].current_dimension += 1 + structure.current_dimension += 1 return 0 # We will visit the last face now. - structptr[0].n_newfaces[structptr[0].current_dimension] -= 1 + structure.n_newfaces[structure.current_dimension] -= 1 n_faces -= 1 - if not structptr[0].first_time[structptr[0].current_dimension]: + if not structure.first_time[structure.current_dimension]: # In this case there exists ``faces[n_faces + 1]``, of which we # have visited all faces, but which was not added to # ``visited_all`` yet. - structptr[0].visited_all[n_visited_all] = faces[n_faces + 1] - if structptr[0].is_simple: - structptr[0].visited_all_coatom_rep[n_visited_all] = faces_coatom_rep[n_faces + 1] - structptr[0].n_visited_all[structptr[0].current_dimension] += 1 - n_visited_all = structptr[0].n_visited_all[structptr[0].current_dimension] + structure.visited_all[n_visited_all] = faces[n_faces + 1] + if structure.is_simple: + structure.visited_all_coatom_rep[n_visited_all] = faces_coatom_rep[n_faces + 1] + structure.n_visited_all[structure.current_dimension] += 1 + n_visited_all = structure.n_visited_all[structure.current_dimension] else: # Once we have visited all faces of ``faces[n_faces]``, we want # to add it to ``visited_all``. - structptr[0].first_time[structptr[0].current_dimension] = False + structure.first_time[structure.current_dimension] = False # Get the faces of codimension 1 contained in ``faces[n_faces]``, # which we have not yet visited. cdef size_t newfacescounter - if structptr[0].is_simple: + if structure.is_simple: sig_on() newfacescounter = get_next_level_simple( faces, n_faces + 1, - structptr[0].newfaces[structptr[0].current_dimension-1], - structptr[0].visited_all, n_visited_all, structptr[0].face_length, + structure.newfaces[structure.current_dimension-1], + structure.visited_all, n_visited_all, structure.face_length, faces_coatom_rep, - structptr[0].newfaces_coatom_rep[structptr[0].current_dimension-1], - structptr[0].visited_all_coatom_rep, structptr[0].face_length_coatom_rep) + structure.newfaces_coatom_rep[structure.current_dimension-1], + structure.visited_all_coatom_rep, structure.face_length_coatom_rep) sig_off() else: sig_on() newfacescounter = get_next_level( faces, n_faces + 1, - structptr[0].newfaces[structptr[0].current_dimension-1], - structptr[0].visited_all, n_visited_all, structptr[0].face_length) + structure.newfaces[structure.current_dimension-1], + structure.visited_all, n_visited_all, structure.face_length) sig_off() if newfacescounter: @@ -1377,11 +1377,11 @@ cdef inline int next_face_loop(iter_struct *structptr) nogil except -1: # We will visted them on next call, starting with codimension 1. # Setting the variables correclty for next call of ``next_face_loop``. - structptr[0].current_dimension -= 1 - structptr[0].first_time[structptr[0].current_dimension] = True - structptr[0].n_newfaces[structptr[0].current_dimension] = newfacescounter - structptr[0].n_visited_all[structptr[0].current_dimension] = n_visited_all - structptr[0].yet_to_visit = newfacescounter + structure.current_dimension -= 1 + structure.first_time[structure.current_dimension] = True + structure.n_newfaces[structure.current_dimension] = newfacescounter + structure.n_visited_all[structure.current_dimension] = n_visited_all + structure.yet_to_visit = newfacescounter return 0 else: # ``faces[n_faces]`` contains no new faces. @@ -1391,15 +1391,15 @@ cdef inline int next_face_loop(iter_struct *structptr) nogil except -1: # this step needs to be done, as ``faces[n_faces]`` might # have been added manually to ``visited_all``. # So this step is required to respect boundaries of ``visited_all``. - structptr[0].first_time[structptr[0].current_dimension] = True + structure.first_time[structure.current_dimension] = True return 0 -cdef inline size_t n_atom_rep(iter_struct *structptr) nogil except -1: +cdef inline size_t n_atom_rep(iter_struct& structure) nogil except -1: r""" See meth:`FaceIterator.n_atom_rep`. """ - if structptr[0].face: - return count_atoms(structptr[0].face, structptr[0].face_length) + if structure.face: + return count_atoms(structure.face, structure.face_length) # The face was not initialized properly. raise LookupError("``FaceIterator`` does not point to a face") From 072e2001f7677b8d3e4cc316cc309662203a0a3c Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Tue, 8 Sep 2020 09:21:54 +0200 Subject: [PATCH 079/713] temporary commit --- .../combinatorial_polyhedron/base.pyx | 4 +- ...tor_operations.cc => bitset_operations.cc} | 0 .../combinatorial_polyhedron/bitsets.cc | 55 +++++++++++++++++++ .../combinatorial_face.pyx | 4 +- .../face_iterator.pyx | 4 +- .../list_of_faces.pyx | 4 +- .../polyhedron_face_lattice.pyx | 4 +- 7 files changed, 65 insertions(+), 10 deletions(-) rename src/sage/geometry/polyhedron/combinatorial_polyhedron/{bit_vector_operations.cc => bitset_operations.cc} (100%) create mode 100644 src/sage/geometry/polyhedron/combinatorial_polyhedron/bitsets.cc diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx index beb340ff9ae..df721ff8a19 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx @@ -1,4 +1,4 @@ -# distutils: depends = sage/geometry/polyhedron/combinatorial_polyhedron/bit_vector_operations.cc +# distutils: depends = sage/geometry/polyhedron/combinatorial_polyhedron/bitset_operations.cc # distutils: language = c++ # distutils: extra_compile_args = -std=c++11 @@ -106,7 +106,7 @@ from sage.rings.integer cimport smallInteger from cysignals.signals cimport sig_check, sig_block, sig_unblock from sage.matrix.matrix_integer_dense cimport Matrix_integer_dense -cdef extern from "bit_vector_operations.cc": +cdef extern from "bitset_operations.cc": cdef size_t count_atoms(uint64_t *A, size_t face_length) # Return the number of atoms/vertices in A. # This is the number of set bits in A. diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/bit_vector_operations.cc b/src/sage/geometry/polyhedron/combinatorial_polyhedron/bitset_operations.cc similarity index 100% rename from src/sage/geometry/polyhedron/combinatorial_polyhedron/bit_vector_operations.cc rename to src/sage/geometry/polyhedron/combinatorial_polyhedron/bitset_operations.cc diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/bitsets.cc b/src/sage/geometry/polyhedron/combinatorial_polyhedron/bitsets.cc new file mode 100644 index 00000000000..e70758e5b69 --- /dev/null +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/bitsets.cc @@ -0,0 +1,55 @@ +/* +############################################################################# +# More or less taken from ``src/sage/data_structures/bitset.pxi`` +############################################################################# +*/ + +/* +############################################################################# +# Creating limb patterns +############################################################################# +*/ + +inline uint64_t limb_one_set_bit(size_t n){ + /* + Return a limb with only bit n set. + */ + return (uint64_t) 1 << (n % 64); +} + +inline uint64_t limb_one_zero_bit(mp_bitcnt_t n){ + /* + Return a limb with all bits set, except for bit n. + */ + return !((uint64_t) 1 << (n % 64)); +} + +inline uint64_t limb_lower_bits_down(mp_bitcnt_t n){ + /* + Return a limb with the lower n bits set, where n is interpreted + in [0 .. 64-1]. + */ + return ((uint64_t) 1 << (n % 64)) - 1; +} + +/* +############################################################################# +# Bitset Bit Manipulation +############################################################################# +*/ + +inline int bitset_in(uint64_t* bits, size_t n){ + /* + Check if n is in bits. Return True (i.e., 1) if n is in the + set, False (i.e., 0) otherwise. + */ + return bits[n/64] & limb_one_set_bit(n); +} + +inline int bitset_discard(uint64_t* bits, size_t n){ + /* + Remove n from bits. + */ + bits[n/64] &= limb_one_zero_bit(n); +} + diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx index 44fd8215058..af0a92521d5 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx @@ -1,4 +1,4 @@ -# distutils: depends = sage/geometry/polyhedron/combinatorial_polyhedron/bit_vector_operations.cc +# distutils: depends = sage/geometry/polyhedron/combinatorial_polyhedron/bitset_operations.cc # distutils: language = c++ # distutils: extra_compile_args = -std=c++11 @@ -78,7 +78,7 @@ from .face_iterator cimport FaceIterator_base from .polyhedron_face_lattice cimport PolyhedronFaceLattice from libc.string cimport memcpy -cdef extern from "bit_vector_operations.cc": +cdef extern from "bitset_operations.cc": cdef size_t count_atoms(uint64_t *A, size_t face_length) # Return the number of atoms/vertices in A. # This is the number of set bits in A. diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx index bc2cc403ee9..a5cc0b96ec0 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx @@ -1,4 +1,4 @@ -# distutils: depends = sage/geometry/polyhedron/combinatorial_polyhedron/bit_vector_operations.cc +# distutils: depends = sage/geometry/polyhedron/combinatorial_polyhedron/bitset_operations.cc # distutils: language = c++ # distutils: extra_compile_args = -std=c++11 @@ -185,7 +185,7 @@ from .conversions import facets_tuple_to_bit_rep_of_facets from .base cimport CombinatorialPolyhedron from sage.geometry.polyhedron.face import combinatorial_face_to_polyhedral_face, PolyhedronFace -cdef extern from "bit_vector_operations.cc": +cdef extern from "bitset_operations.cc": cdef size_t get_next_level( uint64_t **faces, const size_t n_faces, uint64_t **newfaces, uint64_t **visited_all, diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.pyx index 35a15648648..bc674d93996 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.pyx @@ -1,4 +1,4 @@ -# distutils: depends = sage/geometry/polyhedron/combinatorial_polyhedron/bit_vector_operations.cc +# distutils: depends = sage/geometry/polyhedron/combinatorial_polyhedron/bitset_operations.cc # distutils: language = c++ # distutils: extra_compile_args = -std=c++11 @@ -99,7 +99,7 @@ from libc.string cimport memcpy from .conversions cimport vertex_to_bit_dictionary from sage.matrix.matrix_integer_dense cimport Matrix_integer_dense -cdef extern from "bit_vector_operations.cc": +cdef extern from "bitset_operations.cc": # Any Bit-representation is assumed to be `chunksize`-Bit aligned. cdef const size_t chunksize diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.pyx index 506a21ef045..d1854b87f0d 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.pyx @@ -1,4 +1,4 @@ -# distutils: depends = sage/geometry/polyhedron/combinatorial_polyhedron/bit_vector_operations.cc +# distutils: depends = sage/geometry/polyhedron/combinatorial_polyhedron/bitset_operations.cc # distutils: language = c++ # distutils: extra_compile_args = -std=c++11 @@ -73,7 +73,7 @@ from .conversions cimport Vrep_list_to_bit_rep, bit_rep_to_Vrep_list from .base cimport CombinatorialPolyhedron from .face_iterator cimport FaceIterator -cdef extern from "bit_vector_operations.cc": +cdef extern from "bitset_operations.cc": cdef void intersection(uint64_t *dest, uint64_t *A, uint64_t *B, size_t face_length) # Set ``dest = A & B``, i.e. dest is the intersection of A and B. From cba37e8455ae363d493bf56ef5c177d1fa5e4e4d Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Tue, 8 Sep 2020 10:28:43 +0200 Subject: [PATCH 080/713] temporary commit --- .../combinatorial_polyhedron/bitsets.cc | 73 +++++++++++++++++-- .../combinatorial_polyhedron/conversions.pxd | 2 - .../combinatorial_polyhedron/conversions.pyx | 53 +++++--------- .../list_of_faces.pyx | 14 ++-- 4 files changed, 92 insertions(+), 50 deletions(-) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/bitsets.cc b/src/sage/geometry/polyhedron/combinatorial_polyhedron/bitsets.cc index e70758e5b69..f7fc8ac39db 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/bitsets.cc +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/bitsets.cc @@ -4,32 +4,38 @@ ############################################################################# */ +#include "gmp.h" + +const size_t index_shift = 6; +const size_t LIMB_BITS = 64; + /* ############################################################################# # Creating limb patterns ############################################################################# */ + inline uint64_t limb_one_set_bit(size_t n){ /* Return a limb with only bit n set. */ - return (uint64_t) 1 << (n % 64); + return (uint64_t) 1 << (n % LIMB_BITS); } -inline uint64_t limb_one_zero_bit(mp_bitcnt_t n){ +inline uint64_t limb_one_zero_bit(size_t n){ /* Return a limb with all bits set, except for bit n. */ - return !((uint64_t) 1 << (n % 64)); + return !((uint64_t) 1 << (n % LIMB_BITS)); } -inline uint64_t limb_lower_bits_down(mp_bitcnt_t n){ +inline uint64_t limb_lower_bits_down(size_t n){ /* Return a limb with the lower n bits set, where n is interpreted in [0 .. 64-1]. */ - return ((uint64_t) 1 << (n % 64)) - 1; + return ((uint64_t) 1 << (n % LIMB_BITS)) - 1; } /* @@ -43,13 +49,64 @@ inline int bitset_in(uint64_t* bits, size_t n){ Check if n is in bits. Return True (i.e., 1) if n is in the set, False (i.e., 0) otherwise. */ - return bits[n/64] & limb_one_set_bit(n); + return bits[n >> index_shift] & limb_one_set_bit(n); } -inline int bitset_discard(uint64_t* bits, size_t n){ +inline void bitset_discard(uint64_t* bits, size_t n){ /* Remove n from bits. */ - bits[n/64] &= limb_one_zero_bit(n); + bits[n >> index_shift] &= limb_one_zero_bit(n); +} + +inline void bitset_add(uint64_t* bits, size_t n){ + /* + Add n to bits. + */ + bits[n >> index_shift] |= limb_one_set_bit(n); +} + +/* +############################################################################# +# Bitset Searching +############################################################################# +*/ + +inline size_t _bitset_first_in_limb(uint64_t limb){ + /* + Given a limb of a bitset, return the index of the first nonzero + bit. If there are no bits set in the limb, return -1. + */ + if (limb == 0) + return -1; + return mpn_scan1(&limb, 0); +} + +inline long _bitset_first_in_limb_nonzero(uint64_t limb){ + /* + Given a non-zero limb of a bitset, return the index of the first + nonzero bit. + */ + return mpn_scan1(&limb, 0); } +inline size_t bitset_next(uint64_t* bits, size_t face_length, size_t n){ + /* + Calculate the index of the next element in the set, starting at + (and including) n. Return -1 if there are no elements from n + onwards. + */ + if (n >= face_length*LIMB_BITS) + return -1; + + size_t i = n >> index_shift; + size_t limb = bits[i] & ~limb_lower_bits_down(n); + size_t ret = _bitset_first_in_limb(limb); + if (ret != (size_t) -1) + return (i << index_shift) | ret; + for(i++; i < face_length; i++){ + if (bits[i]) + return (i << index_shift) | _bitset_first_in_limb_nonzero(bits[i]); + } + return -1; +} diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/conversions.pxd b/src/sage/geometry/polyhedron/combinatorial_polyhedron/conversions.pxd index 83caad68592..e36e0c2eb73 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/conversions.pxd +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/conversions.pxd @@ -8,5 +8,3 @@ cdef int incidences_to_bit_rep(tuple incidences, uint64_t *output, cdef size_t bit_rep_to_Vrep_list(uint64_t *face, size_t *output, size_t face_length) except -1 - -cdef uint64_t vertex_to_bit_dictionary(size_t i) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/conversions.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/conversions.pyx index 19fd87bc409..7934dcfd796 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/conversions.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/conversions.pyx @@ -1,3 +1,8 @@ +# distutils: depends = sage/geometry/polyhedron/combinatorial_polyhedron/bitsets.cc +# distutils: language = c++ +# distutils: extra_compile_args = -std=c++11 +# distutils: libraries = gmp + r""" Conversions @@ -79,13 +84,9 @@ from sage.ext.memory_allocator cimport MemoryAllocator cdef extern from "Python.h": int unlikely(int) nogil # Defined by Cython -cdef inline uint64_t vertex_to_bit_dictionary(size_t i): - r""" - Return an uint64_t with exactly the i-th bit set to 1. - - This "dictionary" helps storing a vector of 64 incidences as ``uint64_t``. - """ - return (1) << (64 - i - 1) +cdef extern from "bitsets.cc": + cdef void bitset_add(uint64_t* bits, size_t n) + cdef size_t bitset_next(uint64_t* bits, size_t face_length, size_t n) def _Vrep_list_to_bit_rep_wrapper(tup, size_t face_length=-1): r""" @@ -153,11 +154,9 @@ cdef int Vrep_list_to_bit_rep(tuple Vrep_list, uint64_t *output, if unlikely(len(Vrep_list) != len(set(Vrep_list))): raise ValueError("entries of ``tup`` are not distinct") for entry in Vrep_list: - value = entry % 64 - position = entry//64 - if unlikely(position >= face_length): + if unlikely(entry >= 64*face_length): raise IndexError("output too small to represent %s"%entry) - output[position] += vertex_to_bit_dictionary(value) + bitset_add(output, entry) def _incidences_to_bit_rep_wrapper(tup, size_t face_length=-1): r""" @@ -215,8 +214,6 @@ cdef int incidences_to_bit_rep(tuple incidences, uint64_t *output, IndexError: output too small to represent all incidences """ cdef size_t entry # index for the entries in tup - cdef size_t position # determines the position in output of entry - cdef size_t value # determines which bit will be set in output[position] cdef size_t length = len(incidences) memset(output, 0, face_length*8) #initialize @@ -225,9 +222,7 @@ cdef int incidences_to_bit_rep(tuple incidences, uint64_t *output, for entry in range(length): if incidences[entry]: # Vrep ``entry`` is contained in the face, so set the corresponding bit - value = entry % 64 - position = entry//64 - output[position] += vertex_to_bit_dictionary(value) + bitset_add(output, entry) def incidence_matrix_to_bit_rep_of_facets(Matrix_integer_dense matrix): r""" @@ -283,8 +278,6 @@ def incidence_matrix_to_bit_rep_of_facets(Matrix_integer_dense matrix): cdef uint64_t *output cdef size_t face_length = facets.face_length cdef size_t entry # index for the entries in tup - cdef size_t position # determines the position in output of entry - cdef size_t value # determines which bit will be set in output[position] cdef size_t i for i in range(ncols): @@ -296,9 +289,7 @@ def incidence_matrix_to_bit_rep_of_facets(Matrix_integer_dense matrix): for entry in range(nrows): if not matrix.get_is_zero_unsafe(entry, i): # Vrep ``entry`` is contained in the face, so set the corresponding bit - value = entry % 64 - position = entry//64 - output[position] += vertex_to_bit_dictionary(value) + bitset_add(output, entry) return facets incidence_matrix_to_bit_repr_of_facets = deprecated_function_alias(28608, incidence_matrix_to_bit_rep_of_facets) @@ -461,13 +452,11 @@ def facets_tuple_to_bit_rep_of_Vrep(tuple facets_input, size_t n_Vrep): cdef size_t input_Vrep # will iterate over vertices in facet ``input_facet`` for input_facet in range(n_facets): - value = input_facet % 64 - position = input_facet//64 for input_Vrep in facets_input[input_facet]: # Iff the input-Vrep is in the input-facet, # then in facet-representation of the Vrep # input-facet is a Vrep of intput-Vrep. - Vrep_data[input_Vrep][position] += vertex_to_bit_dictionary(value) + bitset_add(Vrep_data[input_Vrep], input_facet) return Vrep facets_tuple_to_bit_repr_of_Vrepr = deprecated_function_alias(28608, facets_tuple_to_bit_rep_of_Vrep) @@ -565,17 +554,11 @@ cdef inline size_t bit_rep_to_Vrep_list(uint64_t *face, size_t *output, ....: print('``bit_rep_to_Vrep_list`` does not behave', ....: 'as the inverse of ``Vrep_list_to_bit_rep``') """ - cdef size_t i, j cdef size_t output_length = 0 cdef uint64_t copy - for i in range(face_length): - if face[i]: - copy = face[i] - for j in range(64): - if copy >= vertex_to_bit_dictionary(j): - # Then face[i] has the j-th bit set to 1. - # This corresponds to face containing Vrep i*64 + j. - output[output_length] = i*64 + j - output_length += 1 - copy -= vertex_to_bit_dictionary(j) + cdef size_t j = bitset_next(face, face_length, 0) + while (j != -1): + output[output_length] = j + output_length += 1 + j = bitset_next(face, face_length, j+1) return output_length diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.pyx index bc674d93996..b4baa7714d7 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.pyx @@ -1,6 +1,7 @@ -# distutils: depends = sage/geometry/polyhedron/combinatorial_polyhedron/bitset_operations.cc +# distutils: depends = sage/geometry/polyhedron/combinatorial_polyhedron/bitset_operations.cc sage/geometry/polyhedron/combinatorial_polyhedron/bitsets.cc # distutils: language = c++ # distutils: extra_compile_args = -std=c++11 +# distutils: libraries = gmp r""" List of faces @@ -96,7 +97,6 @@ from sage.structure.element import is_Matrix from cysignals.signals cimport sig_on, sig_off from libc.string cimport memcpy -from .conversions cimport vertex_to_bit_dictionary from sage.matrix.matrix_integer_dense cimport Matrix_integer_dense cdef extern from "bitset_operations.cc": @@ -143,6 +143,10 @@ cdef extern from "bitset_operations.cc": # This is the number of set bits in A. # ``face_length`` is the length of A in terms of uint64_t. +cdef extern from "bitsets.cc": + cdef void bitset_add(uint64_t* bits, size_t n) + cdef int bitset_in(uint64_t* bits, size_t n) + cdef extern from "Python.h": int unlikely(int) nogil # Defined by Cython @@ -428,13 +432,13 @@ cdef class ListOfFaces: # All old coatoms contain their respective old atoms. # Also all of them contain the new atom. memcpy(copy.data[i], self.data[i], self.face_length*8) - copy.data[i][self.n_atoms//64] |= vertex_to_bit_dictionary(self.n_atoms % 64) + bitset_add(copy.data[i], self.n_atoms) # The new coatom contains all atoms, but the new atom. for i in range(copy.face_length): copy.data[self.n_faces][i] = 0 for i in range(self.n_atoms): - copy.data[self.n_faces][i//64] |= vertex_to_bit_dictionary(i % 64) + bitset_add(copy.data[self.n_faces], i) return copy @@ -472,7 +476,7 @@ cdef class ListOfFaces: cdef size_t i,j for i in range(self.n_faces): for j in range(self.n_atoms): - if self.data[i][j//64] & vertex_to_bit_dictionary(j % 64): + if bitset_in(self.data[i], j): M.set_unsafe_si(i, j, 1) M.set_immutable() From 0b41c77a91f6686e3a6e9a99eb78053d93a2e904 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Mon, 7 Sep 2020 21:56:07 +0200 Subject: [PATCH 081/713] remove basic acces to bitsets --- .../combinatorial_polyhedron/bitsets.cc | 63 ++++++++++++++++++- .../combinatorial_face.pyx | 13 ++-- .../combinatorial_polyhedron/conversions.pyx | 32 +++++----- .../list_of_faces.pyx | 20 +++--- .../polyhedron_face_lattice.pyx | 14 +++-- 5 files changed, 104 insertions(+), 38 deletions(-) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/bitsets.cc b/src/sage/geometry/polyhedron/combinatorial_polyhedron/bitsets.cc index f7fc8ac39db..14a07044797 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/bitsets.cc +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/bitsets.cc @@ -5,6 +5,8 @@ */ #include "gmp.h" +#include +using namespace std; const size_t index_shift = 6; const size_t LIMB_BITS = 64; @@ -15,7 +17,6 @@ const size_t LIMB_BITS = 64; ############################################################################# */ - inline uint64_t limb_one_set_bit(size_t n){ /* Return a limb with only bit n set. @@ -38,6 +39,52 @@ inline uint64_t limb_lower_bits_down(size_t n){ return ((uint64_t) 1 << (n % LIMB_BITS)) - 1; } +/* +############################################################################# +# Bitset Initalization +############################################################################# +*/ + +inline void bitset_clear(uint64_t* bits, size_t face_length){ + /* + Remove all elements from the set. + */ + memset(bits, 0, face_length*8); +} + +inline void bitset_copy(uint64_t* dst, uint64_t* src, size_t face_length){ + /* + Copy the bitset src over to the bitset dst, overwriting dst. + + We assume ``dst.limbs == src.limbs``. + */ + memcpy(dst, src, face_length*8); +} + +inline void bitset_copy(uint64_t* dst, uint64_t* src, size_t face_length_dst, size_t face_length_src){ + /* + Copy the bitset src over to the bitset dst, overwriting dst. + + If ``dst`` is longer, then additional bits are set to zero. + */ + if (face_length_src > face_length_dst) + face_length_src = face_length_dst; + + memcpy(dst, src, face_length_src*8); + memset(dst+face_length_src, 0, (face_length_dst-face_length_src)*8); +} + +inline int bitset_cmp(uint64_t* a, uint64_t* b, size_t face_length){ + /* + Compare bitsets a and b. Return 0 if the two sets are + identical, and consistently return -1 or 1 for two sets that are + not equal. + + We assume ``a.limbs >= b.limbs``. + */ + return memcmp(a, b, face_length*8); +} + /* ############################################################################# # Bitset Bit Manipulation @@ -66,6 +113,20 @@ inline void bitset_add(uint64_t* bits, size_t n){ bits[n >> index_shift] |= limb_one_set_bit(n); } +inline void bitset_set_first_n(uint64_t* bits, size_t face_length, size_t n){ + /* + Set exactly the first n bits. + */ + size_t i; + size_t index = n >> index_shift; + for(i = 0; i < index; i++) + bits[i] = -1; + if (index < face_length) + bits[index] = limb_lower_bits_down(n); + for(i=index+1; i < face_length; i++) + bits[i] = 0; +} + /* ############################################################################# # Bitset Searching diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx index af0a92521d5..28db5c498f9 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx @@ -1,6 +1,7 @@ -# distutils: depends = sage/geometry/polyhedron/combinatorial_polyhedron/bitset_operations.cc +# distutils: depends = sage/geometry/polyhedron/combinatorial_polyhedron/bitset_operations.cc sage/geometry/polyhedron/combinatorial_polyhedron/bitsets.cc # distutils: language = c++ # distutils: extra_compile_args = -std=c++11 +# distutils: libraries = gmp r""" Combinatorial face of a polyhedron @@ -76,7 +77,6 @@ from .conversions cimport bit_rep_to_Vrep_list from .base cimport CombinatorialPolyhedron from .face_iterator cimport FaceIterator_base from .polyhedron_face_lattice cimport PolyhedronFaceLattice -from libc.string cimport memcpy cdef extern from "bitset_operations.cc": cdef size_t count_atoms(uint64_t *A, size_t face_length) @@ -92,6 +92,9 @@ cdef extern from "bitset_operations.cc": # in terms of uint64_t. # ``n_coatoms`` length of ``coatoms``. +cdef extern from "bitsets.cc": + cdef void bitset_copy(uint64_t* dst, uint64_t* src, size_t face_length) + cdef extern from "Python.h": int unlikely(int) nogil # Defined by Cython @@ -113,7 +116,7 @@ cdef class CombinatorialFace(SageObject): sage: F = C.face_lattice() sage: F._elements[3] - 29 + 17 sage: C.face_by_face_lattice_index(29) A 1-dimensional face of a 5-dimensional combinatorial polyhedron @@ -186,7 +189,7 @@ cdef class CombinatorialFace(SageObject): self._dual = it.dual self.face_mem = ListOfFaces(1, it.structure.face_length*64) self.face = self.face_mem.data[0] - memcpy(self.face, it.structure.face, it.structure.face_length*8) + bitset_copy(self.face, it.structure.face, it.structure.face_length) self._mem = MemoryAllocator() self._dimension = it.structure.current_dimension self._ambient_dimension = it.structure.dimension @@ -211,7 +214,7 @@ cdef class CombinatorialFace(SageObject): self._dual = all_faces.dual self.face_mem = ListOfFaces(1, all_faces.face_length*64) self.face = self.face_mem.data[0] - memcpy(self.face, all_faces.faces[dimension+1][index], all_faces.face_length*8) + bitset_copy(self.face, all_faces.faces[dimension+1][index], all_faces.face_length) self._mem = MemoryAllocator() self._dimension = dimension self._ambient_dimension = all_faces.dimension diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/conversions.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/conversions.pyx index 7934dcfd796..eba74e7fbfe 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/conversions.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/conversions.pyx @@ -75,7 +75,6 @@ AUTHOR: from sage.structure.element import is_Matrix -from libc.string cimport memset from .list_of_faces cimport ListOfFaces from sage.misc.superseded import deprecated_function_alias from sage.matrix.matrix_integer_dense cimport Matrix_integer_dense @@ -87,6 +86,7 @@ cdef extern from "Python.h": cdef extern from "bitsets.cc": cdef void bitset_add(uint64_t* bits, size_t n) cdef size_t bitset_next(uint64_t* bits, size_t face_length, size_t n) + cdef void bitset_clear(uint64_t* bits, size_t face_length) def _Vrep_list_to_bit_rep_wrapper(tup, size_t face_length=-1): r""" @@ -96,7 +96,7 @@ def _Vrep_list_to_bit_rep_wrapper(tup, size_t face_length=-1): sage: from sage.geometry.polyhedron.combinatorial_polyhedron.conversions \ ....: import _Vrep_list_to_bit_rep_wrapper - sage: _Vrep_list_to_bit_rep_wrapper((60, 63)) + sage: _Vrep_list_to_bit_rep_wrapper((0, 3)) (9,) """ if face_length == -1: @@ -129,9 +129,9 @@ cdef int Vrep_list_to_bit_rep(tuple Vrep_list, uint64_t *output, sage: from sage.geometry.polyhedron.combinatorial_polyhedron.conversions \ ....: import _Vrep_list_to_bit_rep_wrapper - sage: _Vrep_list_to_bit_rep_wrapper((62, 63)) + sage: _Vrep_list_to_bit_rep_wrapper((0, 1)) (3,) - sage: _Vrep_list_to_bit_rep_wrapper((61, 63, 125)) + sage: _Vrep_list_to_bit_rep_wrapper((0, 2, 64+2)) (5, 4) sage: _Vrep_list_to_bit_rep_wrapper((62, 70), face_length=1) Traceback (most recent call last): @@ -150,7 +150,7 @@ cdef int Vrep_list_to_bit_rep(tuple Vrep_list, uint64_t *output, cdef size_t position # determines the position in output of entry cdef size_t value # determines which bit will be set in output[position] - memset(output, 0, face_length*8) # initialize output + bitset_clear(output, face_length) if unlikely(len(Vrep_list) != len(set(Vrep_list))): raise ValueError("entries of ``tup`` are not distinct") for entry in Vrep_list: @@ -166,7 +166,7 @@ def _incidences_to_bit_rep_wrapper(tup, size_t face_length=-1): sage: from sage.geometry.polyhedron.combinatorial_polyhedron.conversions \ ....: import _incidences_to_bit_rep_wrapper - sage: _incidences_to_bit_rep_wrapper((0,) * 60 + (1,0,0,1)) + sage: _incidences_to_bit_rep_wrapper((1,0,0,1)) (9,) """ if face_length == -1: @@ -201,10 +201,10 @@ cdef int incidences_to_bit_rep(tuple incidences, uint64_t *output, sage: from sage.geometry.polyhedron.combinatorial_polyhedron.conversions \ ....: import _incidences_to_bit_rep_wrapper - sage: _incidences_to_bit_rep_wrapper((0,) * 62 + (1,1)) + sage: _incidences_to_bit_rep_wrapper((1,1)) (3,) - sage: _incidences_to_bit_rep_wrapper((0,) * 61 + (1,0,1) + - ....: (0,) * 61 + (1,)) + sage: _incidences_to_bit_rep_wrapper((1,0,1) + 61*(0,) + + ....: (0,0,1,)) (5, 4) sage: _incidences_to_bit_rep_wrapper((1,) * 64) == (2**64-1,) True @@ -216,7 +216,7 @@ cdef int incidences_to_bit_rep(tuple incidences, uint64_t *output, cdef size_t entry # index for the entries in tup cdef size_t length = len(incidences) - memset(output, 0, face_length*8) #initialize + bitset_clear(output, face_length) if unlikely(length > 64*face_length): raise IndexError("output too small to represent all incidences") for entry in range(length): @@ -282,7 +282,7 @@ def incidence_matrix_to_bit_rep_of_facets(Matrix_integer_dense matrix): cdef size_t i for i in range(ncols): output = facets_data[i] - memset(output, 0, face_length*8) #initialize + bitset_clear(output, face_length) # Filling each facet with its Vrep-incidences, which "is" the # "i-th column" of the original matrix (but we have transposed). @@ -444,7 +444,7 @@ def facets_tuple_to_bit_rep_of_Vrep(tuple facets_input, size_t n_Vrep): cdef size_t face_length = Vrep.face_length cdef size_t i for i in range(n_Vrep): - memset(Vrep_data[i], 0, face_length*8) + bitset_clear(Vrep_data[i], face_length) cdef size_t input_facet # will iterate over indices of facets_input cdef size_t position # determines the position in output of entry @@ -484,7 +484,7 @@ def _bit_rep_to_Vrep_list_wrapper(data, index=0): ....: import facets_tuple_to_bit_rep_of_facets, \ ....: _bit_rep_to_Vrep_list_wrapper sage: _bit_rep_to_Vrep_list_wrapper((1, 1)) - (63, 127) + (0, 64) sage: faces = facets_tuple_to_bit_rep_of_facets(((1,5,123,1054),), 1055) sage: _bit_rep_to_Vrep_list_wrapper(faces, 0) (1, 5, 123, 1054) @@ -531,11 +531,11 @@ cdef inline size_t bit_rep_to_Vrep_list(uint64_t *face, size_t *output, sage: from sage.geometry.polyhedron.combinatorial_polyhedron.conversions \ ....: import _bit_rep_to_Vrep_list_wrapper sage: _bit_rep_to_Vrep_list_wrapper((17, 31)) - (59, 63, 123, 124, 125, 126, 127) + (0, 4, 64, 65, 66, 67, 68) sage: _bit_rep_to_Vrep_list_wrapper((13,)) - (60, 61, 63) + (0, 2, 3) sage: _bit_rep_to_Vrep_list_wrapper((0, 61)) - (122, 123, 124, 125, 127) + (64, 66, 67, 68, 69) TESTS: diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.pyx index b4baa7714d7..a15b9704a6d 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.pyx @@ -96,7 +96,6 @@ AUTHOR: from sage.structure.element import is_Matrix from cysignals.signals cimport sig_on, sig_off -from libc.string cimport memcpy from sage.matrix.matrix_integer_dense cimport Matrix_integer_dense cdef extern from "bitset_operations.cc": @@ -146,6 +145,10 @@ cdef extern from "bitset_operations.cc": cdef extern from "bitsets.cc": cdef void bitset_add(uint64_t* bits, size_t n) cdef int bitset_in(uint64_t* bits, size_t n) + cdef void bitset_copy(uint64_t* dst, uint64_t* src, size_t face_length) + cdef void bitset_copy_flex "bitset_copy" (uint64_t* dst, uint64_t* src, size_t face_length_dst, size_t face_length_src) + cdef void bitset_clear(uint64_t* bits, size_t face_length) + cdef void bitset_set_first_n(uint64_t* bits, size_t face_length, size_t n) cdef extern from "Python.h": int unlikely(int) nogil # Defined by Cython @@ -267,7 +270,7 @@ cdef class ListOfFaces: cdef ListOfFaces copy = ListOfFaces(self.n_faces, self.n_atoms) cdef size_t i for i in range(self.n_faces): - memcpy(copy.data[i], self.data[i], self.face_length*8) + bitset_copy(copy.data[i], self.data[i], self.face_length) return copy cpdef int compute_dimension(self) except -2: @@ -356,7 +359,7 @@ cdef class ListOfFaces: cdef size_t new_n_faces sig_on() new_n_faces = get_next_level(faces, n_faces, - newfaces.data, NULL, 0, face_length) + newfaces.data, NULL, 0, face_length) sig_off() newfaces.n_faces = new_n_faces @@ -420,25 +423,20 @@ cdef class ListOfFaces: [1 1 1 1 0] """ cdef ListOfFaces copy - cdef size_t i, j + cdef size_t i # ``copy`` has a new atom and a new coatom. copy = ListOfFaces(self.n_faces + 1, self.n_atoms + 1) for i in range(self.n_faces): - for j in range(self.face_length, copy.face_length): - copy.data[i][j] = 0 # All old coatoms contain their respective old atoms. # Also all of them contain the new atom. - memcpy(copy.data[i], self.data[i], self.face_length*8) + bitset_copy_flex(copy.data[i], self.data[i], copy.face_length, self.face_length) bitset_add(copy.data[i], self.n_atoms) # The new coatom contains all atoms, but the new atom. - for i in range(copy.face_length): - copy.data[self.n_faces][i] = 0 - for i in range(self.n_atoms): - bitset_add(copy.data[self.n_faces], i) + bitset_set_first_n(copy.data[self.n_faces], copy.face_length, self.n_atoms) return copy diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.pyx index d1854b87f0d..ae1d1f42f3b 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.pyx @@ -1,6 +1,7 @@ -# distutils: depends = sage/geometry/polyhedron/combinatorial_polyhedron/bitset_operations.cc +# distutils: depends = sage/geometry/polyhedron/combinatorial_polyhedron/bitset_operations.cc sage/geometry/polyhedron/combinatorial_polyhedron/bitsets.cc # distutils: language = c++ # distutils: extra_compile_args = -std=c++11 +# distutils: libraries = gmp r""" PolyhedronFaceLattice @@ -68,7 +69,6 @@ from .conversions \ facets_tuple_to_bit_rep_of_Vrep from sage.rings.integer cimport smallInteger -from libc.string cimport memcmp, memcpy, memset from .conversions cimport Vrep_list_to_bit_rep, bit_rep_to_Vrep_list from .base cimport CombinatorialPolyhedron from .face_iterator cimport FaceIterator @@ -87,6 +87,10 @@ cdef extern from "bitset_operations.cc": # in terms of uint64_t. # ``n_coatoms`` length of ``coatoms``. +cdef extern from "bitsets.cc": + cdef int bitset_cmp(uint64_t* a, uint64_t* b, size_t face_length) + cdef void bitset_copy(uint64_t* dst, uint64_t* src, size_t face_length) + cdef extern from "Python.h": int unlikely(int) nogil # Defined by Cython @@ -245,7 +249,7 @@ cdef class PolyhedronFaceLattice: raise IOError("trying to add too many faces to ``PolyhedronFaceLattice``") # Actually add the face by copying its data. - memcpy(self.faces[face_dim + 1][counter], face, self.face_length*8) + bitset_copy(self.faces[face_dim + 1][counter], face, self.face_length) self.face_counter[face_dim + 1] += 1 cdef int _sort(self) except -1: @@ -413,7 +417,7 @@ cdef class PolyhedronFaceLattice: r""" Return `1` if ``one`` is smaller than ``two``, otherwise `0`. """ - return memcmp(one, two, self.face_length*8) < 0 + return bitset_cmp(one, two, self.face_length) < 0 cdef inline int is_equal(self, int dimension, size_t index, uint64_t *face) except -1: r""" @@ -426,7 +430,7 @@ cdef class PolyhedronFaceLattice: raise IndexError() cdef uint64_t *face2 = self.faces[dimension+1][index] cdef size_t i - return (0 == memcmp(face, face2, self.face_length*8)) + return (0 == bitset_cmp(face, face2, self.face_length)) cpdef CombinatorialFace get_face(self, int dimension, size_t index): r""" From 5010301773ff7009b48203df6126443fa09bb5fa Mon Sep 17 00:00:00 2001 From: "Alex J. Best" Date: Fri, 11 Sep 2020 13:53:43 -0400 Subject: [PATCH 082/713] small fixes --- src/sage/modular/dirichlet.py | 2 +- src/sage/rings/number_field/number_field.py | 5 +---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/sage/modular/dirichlet.py b/src/sage/modular/dirichlet.py index 79b96fe31e2..8a8461235ee 100644 --- a/src/sage/modular/dirichlet.py +++ b/src/sage/modular/dirichlet.py @@ -949,7 +949,7 @@ def fixed_field_polynomial(self, algorithm = "pari"): return H.sage({"x":x}) else: - raise NotImplentedError("algorithm must be one of 'pari' or 'sage'") + raise NotImplementedError("algorithm must be one of 'pari' or 'sage'") def fixed_field(self): diff --git a/src/sage/rings/number_field/number_field.py b/src/sage/rings/number_field/number_field.py index c8f3af9dba9..bfd17319fc6 100644 --- a/src/sage/rings/number_field/number_field.py +++ b/src/sage/rings/number_field/number_field.py @@ -3284,7 +3284,6 @@ def dirichlet_group(self): m = self.conductor() d = self.degree() A = _splitting_classes_gens_(self,m,d) - n = len(A) # d could be improve to be the exponenent of the Galois group rather than the degree, but I do not see how to how about it already. G = DirichletGroup(m, CyclotomicField(d)) H = [G(1)] @@ -3292,12 +3291,10 @@ def dirichlet_group(self): if len(H) == d: break if chi not in H: - if prod( chi(a)==1 for a in A): + if all(chi(a) == 1 for a in A): H.append(chi) return H - - def latex_variable_name(self, name=None): """ Return the latex representation of the variable name for this From 7d17b7d3832609d0d34a7f3e00818cba7225f964 Mon Sep 17 00:00:00 2001 From: Ivo Maffei Date: Sun, 13 Sep 2020 14:20:42 +0200 Subject: [PATCH 083/713] removed blank lines --- src/sage/graphs/generators/distance_regular.pyx | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/sage/graphs/generators/distance_regular.pyx b/src/sage/graphs/generators/distance_regular.pyx index 456e6b78772..92c31452e39 100644 --- a/src/sage/graphs/generators/distance_regular.pyx +++ b/src/sage/graphs/generators/distance_regular.pyx @@ -1316,7 +1316,6 @@ def GeneralisedDodecagonGraph(const int s, const int t): sage: G = graphs.GeneralisedDodecagonGraph(2, 1) # optional - gap_packages internet sage: G.is_distance_regular(True) # optional - gap_packages internet ([4, 2, 2, 2, 2, 2, None], [None, 1, 1, 1, 1, 1, 2]) - """ from sage.arith.misc import is_prime_power @@ -1404,7 +1403,6 @@ def GeneralisedOctagonGraph(const int s, const int t): Traceback (most recent call last): ... ValueError: generalised octagons of order (q, q^2) are known only for odd powers q of 2 - """ from sage.arith.misc import is_prime_power from sage.libs.gap.libgap import libgap @@ -1518,7 +1516,6 @@ def GeneralisedHexagonGraph(const int s, const int t): sage: G = graphs.GeneralisedHexagonGraph(2, 8) # optional - gap_packages internet sage: G.is_distance_regular(True) # optional - gap_packages internet ([18, 16, 16, None], [None, 1, 1, 9]) - """ from sage.arith.misc import is_prime_power from sage.libs.gap.libgap import libgap From 7a32286a203e22f6d04c449301d3370560ee0291 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Mon, 14 Sep 2020 08:41:52 +0200 Subject: [PATCH 084/713] removed deprecated functions --- .../polyhedron/combinatorial_polyhedron/conversions.pyx | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/conversions.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/conversions.pyx index eba74e7fbfe..b26fc921a56 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/conversions.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/conversions.pyx @@ -76,7 +76,6 @@ AUTHOR: from sage.structure.element import is_Matrix from .list_of_faces cimport ListOfFaces -from sage.misc.superseded import deprecated_function_alias from sage.matrix.matrix_integer_dense cimport Matrix_integer_dense from sage.ext.memory_allocator cimport MemoryAllocator @@ -291,7 +290,6 @@ def incidence_matrix_to_bit_rep_of_facets(Matrix_integer_dense matrix): # Vrep ``entry`` is contained in the face, so set the corresponding bit bitset_add(output, entry) return facets -incidence_matrix_to_bit_repr_of_facets = deprecated_function_alias(28608, incidence_matrix_to_bit_rep_of_facets) def incidence_matrix_to_bit_rep_of_Vrep(Matrix_integer_dense matrix): r""" @@ -352,7 +350,6 @@ def incidence_matrix_to_bit_rep_of_Vrep(Matrix_integer_dense matrix): (0, 4, 6) """ return incidence_matrix_to_bit_rep_of_facets(matrix.transpose()) -incidence_matrix_to_bit_repr_of_Vrepr = deprecated_function_alias(28608, incidence_matrix_to_bit_rep_of_Vrep) def facets_tuple_to_bit_rep_of_facets(tuple facets_input, size_t n_Vrep): r""" @@ -395,7 +392,6 @@ def facets_tuple_to_bit_rep_of_facets(tuple facets_input, size_t n_Vrep): # filling each facet with the data from the corresponding facet Vrep_list_to_bit_rep(facets_input[i], facets_data[i], face_length) return facets -facets_tuple_to_bit_repr_of_facets = deprecated_function_alias(28608, facets_tuple_to_bit_rep_of_facets) def facets_tuple_to_bit_rep_of_Vrep(tuple facets_input, size_t n_Vrep): r""" @@ -458,7 +454,6 @@ def facets_tuple_to_bit_rep_of_Vrep(tuple facets_input, size_t n_Vrep): # input-facet is a Vrep of intput-Vrep. bitset_add(Vrep_data[input_Vrep], input_facet) return Vrep -facets_tuple_to_bit_repr_of_Vrepr = deprecated_function_alias(28608, facets_tuple_to_bit_rep_of_Vrep) def _bit_rep_to_Vrep_list_wrapper(data, index=0): r""" From af27e0256bc049fe6046df6fc2a69789c8b00ddf Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Mon, 14 Sep 2020 09:52:02 +0200 Subject: [PATCH 085/713] remove doctests that rely on implementation details --- .../combinatorial_polyhedron/conversions.pyx | 194 ++++++++---------- 1 file changed, 80 insertions(+), 114 deletions(-) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/conversions.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/conversions.pyx index b26fc921a56..164bf1a397b 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/conversions.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/conversions.pyx @@ -87,7 +87,7 @@ cdef extern from "bitsets.cc": cdef size_t bitset_next(uint64_t* bits, size_t face_length, size_t n) cdef void bitset_clear(uint64_t* bits, size_t face_length) -def _Vrep_list_to_bit_rep_wrapper(tup, size_t face_length=-1): +def _Vrep_list_to_bit_rep_wrapper(tup): r""" A function to allow doctesting of :func:`Vrep_list_to_bit_rep`. @@ -95,15 +95,12 @@ def _Vrep_list_to_bit_rep_wrapper(tup, size_t face_length=-1): sage: from sage.geometry.polyhedron.combinatorial_polyhedron.conversions \ ....: import _Vrep_list_to_bit_rep_wrapper - sage: _Vrep_list_to_bit_rep_wrapper((0, 3)) - (9,) + sage: _Vrep_list_to_bit_rep_wrapper((0, 3)).matrix() + [1 0 0 1] """ - if face_length == -1: - face_length = max(tup)//64 + 1 - cdef MemoryAllocator mem = MemoryAllocator() - cdef uint64_t *output = mem.allocarray(face_length, 8) - Vrep_list_to_bit_rep(tup, output, face_length) - return tuple(output[i] for i in range(face_length)) + cdef ListOfFaces output = ListOfFaces(1, max(tup) + 1) + Vrep_list_to_bit_rep(tup, output.data[0], output.face_length) + return output cdef int Vrep_list_to_bit_rep(tuple Vrep_list, uint64_t *output, size_t face_length) except -1: @@ -128,14 +125,10 @@ cdef int Vrep_list_to_bit_rep(tuple Vrep_list, uint64_t *output, sage: from sage.geometry.polyhedron.combinatorial_polyhedron.conversions \ ....: import _Vrep_list_to_bit_rep_wrapper - sage: _Vrep_list_to_bit_rep_wrapper((0, 1)) - (3,) - sage: _Vrep_list_to_bit_rep_wrapper((0, 2, 64+2)) - (5, 4) - sage: _Vrep_list_to_bit_rep_wrapper((62, 70), face_length=1) - Traceback (most recent call last): - ... - IndexError: output too small to represent 70 + sage: _Vrep_list_to_bit_rep_wrapper((0, 1)).matrix() + [1 1] + sage: _Vrep_list_to_bit_rep_wrapper((0, 2, 66)).matrix().nonzero_positions_in_row(0) + [0, 2, 66] sage: _Vrep_list_to_bit_rep_wrapper((-1, 12)) Traceback (most recent call last): ... @@ -157,7 +150,7 @@ cdef int Vrep_list_to_bit_rep(tuple Vrep_list, uint64_t *output, raise IndexError("output too small to represent %s"%entry) bitset_add(output, entry) -def _incidences_to_bit_rep_wrapper(tup, size_t face_length=-1): +def _incidences_to_bit_rep_wrapper(tup): r""" A function to allow doctesting of :func:`incidences_to_bit_rep`. @@ -165,16 +158,12 @@ def _incidences_to_bit_rep_wrapper(tup, size_t face_length=-1): sage: from sage.geometry.polyhedron.combinatorial_polyhedron.conversions \ ....: import _incidences_to_bit_rep_wrapper - sage: _incidences_to_bit_rep_wrapper((1,0,0,1)) - (9,) + sage: _incidences_to_bit_rep_wrapper((1,0,0,1)).matrix() + [1 0 0 1] """ - if face_length == -1: - face_length = (len(tup)-1)//64 + 1 - cdef MemoryAllocator mem = MemoryAllocator() - cdef uint64_t *output = \ - mem.allocarray(face_length, 8) - incidences_to_bit_rep(tup, output, face_length) - return tuple(output[i] for i in range(face_length)) + cdef ListOfFaces output = ListOfFaces(1, len(tup)) + incidences_to_bit_rep(tup, output.data[0], output.face_length) + return output cdef int incidences_to_bit_rep(tuple incidences, uint64_t *output, size_t face_length) except -1: @@ -200,17 +189,11 @@ cdef int incidences_to_bit_rep(tuple incidences, uint64_t *output, sage: from sage.geometry.polyhedron.combinatorial_polyhedron.conversions \ ....: import _incidences_to_bit_rep_wrapper - sage: _incidences_to_bit_rep_wrapper((1,1)) - (3,) + sage: _incidences_to_bit_rep_wrapper((1,1)).matrix() + [1 1] sage: _incidences_to_bit_rep_wrapper((1,0,1) + 61*(0,) + - ....: (0,0,1,)) - (5, 4) - sage: _incidences_to_bit_rep_wrapper((1,) * 64) == (2**64-1,) - True - sage: _incidences_to_bit_rep_wrapper((1,) * 70, face_length=1) - Traceback (most recent call last): - ... - IndexError: output too small to represent all incidences + ....: (0,0,1,)).matrix().nonzero_positions_in_row(0) + [0, 2, 66] """ cdef size_t entry # index for the entries in tup cdef size_t length = len(incidences) @@ -247,26 +230,24 @@ def incidence_matrix_to_bit_rep_of_facets(Matrix_integer_dense matrix): sage: inc = P.incidence_matrix() sage: mod_inc = inc.delete_columns([i for i,V in enumerate(P.Hrepresentation()) if V.is_equation()]) sage: facets = incidence_matrix_to_bit_rep_of_facets(mod_inc) - sage: facets.n_faces - 14 - sage: facets.n_atoms - 24 - sage: for i in range(facets.n_faces): - ....: print(_bit_rep_to_Vrep_list_wrapper(facets, i)) - (18, 19, 20, 21, 22, 23) - (3, 5, 8, 10, 12, 17) - (2, 7, 11, 13, 20, 21) - (2, 5, 12, 13) - (4, 6, 14, 15, 19, 23) - (3, 4, 8, 14) - (6, 7, 21, 23) - (2, 3, 4, 5, 6, 7) - (0, 1, 9, 16, 18, 22) - (0, 9, 10, 17) - (1, 11, 20, 22) - (0, 1, 10, 11, 12, 13) - (15, 16, 18, 19) - (8, 9, 14, 15, 16, 17) + sage: facets.matrix().dimensions() + (14, 24) + sage: for row in facets.matrix(): + ....: row.nonzero_positions() + [18, 19, 20, 21, 22, 23] + [3, 5, 8, 10, 12, 17] + [2, 7, 11, 13, 20, 21] + [2, 5, 12, 13] + [4, 6, 14, 15, 19, 23] + [3, 4, 8, 14] + [6, 7, 21, 23] + [2, 3, 4, 5, 6, 7] + [0, 1, 9, 16, 18, 22] + [0, 9, 10, 17] + [1, 11, 20, 22] + [0, 1, 10, 11, 12, 13] + [15, 16, 18, 19] + [8, 9, 14, 15, 16, 17] """ # Output will be a ``ListOfFaces`` with ``matrix.ncols()`` faces and # ``matrix.nrows()`` Vrep. @@ -318,36 +299,34 @@ def incidence_matrix_to_bit_rep_of_Vrep(Matrix_integer_dense matrix): sage: inc = P.incidence_matrix() sage: mod_inc = inc.delete_columns([i for i,V in enumerate(P.Hrepresentation()) if V.is_equation()]) sage: vertices = incidence_matrix_to_bit_rep_of_Vrep(mod_inc) - sage: vertices.n_faces - 24 - sage: vertices.n_atoms - 14 - sage: for i in range(vertices.n_faces): - ....: print(_bit_rep_to_Vrep_list_wrapper(vertices, i)) - (8, 9, 11) - (8, 10, 11) - (2, 3, 7) - (1, 5, 7) - (4, 5, 7) - (1, 3, 7) - (4, 6, 7) - (2, 6, 7) - (1, 5, 13) - (8, 9, 13) - (1, 9, 11) - (2, 10, 11) - (1, 3, 11) - (2, 3, 11) - (4, 5, 13) - (4, 12, 13) - (8, 12, 13) - (1, 9, 13) - (0, 8, 12) - (0, 4, 12) - (0, 2, 10) - (0, 2, 6) - (0, 8, 10) - (0, 4, 6) + sage: vertices.matrix().dimensions() + (24, 14) + sage: for row in vertices.matrix(): + ....: row.nonzero_positions() + [8, 9, 11] + [8, 10, 11] + [2, 3, 7] + [1, 5, 7] + [4, 5, 7] + [1, 3, 7] + [4, 6, 7] + [2, 6, 7] + [1, 5, 13] + [8, 9, 13] + [1, 9, 11] + [2, 10, 11] + [1, 3, 11] + [2, 3, 11] + [4, 5, 13] + [4, 12, 13] + [8, 12, 13] + [1, 9, 13] + [0, 8, 12] + [0, 4, 12] + [0, 2, 10] + [0, 2, 6] + [0, 8, 10] + [0, 4, 6] """ return incidence_matrix_to_bit_rep_of_facets(matrix.transpose()) @@ -455,50 +434,33 @@ def facets_tuple_to_bit_rep_of_Vrep(tuple facets_input, size_t n_Vrep): bitset_add(Vrep_data[input_Vrep], input_facet) return Vrep -def _bit_rep_to_Vrep_list_wrapper(data, index=0): +def _bit_rep_to_Vrep_list_wrapper(ListOfFaces faces, index=0): r""" A function to test :func:`bit_rep_to_Vrep_list`. INPUT: - - ``data`` -- either a :class:`~sage.geometry.polyhedron.combinatorial_polyhedron.list_of_faces.ListOfFaces` - or a tuple of integers in ``range(0,2**64)`` - - ``index`` -- ``0`` if ``data`` is a tuple, otherwise the index of the ``face`` - to convert - - OUTPUT: A tuple of integers. + - ``faces`` -- a :class:`~sage.geometry.polyhedron.combinatorial_polyhedron.list_of_faces.ListOfFaces` + - ``index`` -- (default: ``0``); the face to obtain - If the input is a tuple, it will be interpreted as a list of faces over with `64` atoms per element in the - tuple. Each number in the tuple corresponds to an ``uint64_t``. - - The list of faces is then translated into a tuple of the integers with set bits. + OUTPUT: The face as tuple of integers. EXAMPLES:: sage: from sage.geometry.polyhedron.combinatorial_polyhedron.conversions \ ....: import facets_tuple_to_bit_rep_of_facets, \ ....: _bit_rep_to_Vrep_list_wrapper - sage: _bit_rep_to_Vrep_list_wrapper((1, 1)) - (0, 64) sage: faces = facets_tuple_to_bit_rep_of_facets(((1,5,123,1054),), 1055) sage: _bit_rep_to_Vrep_list_wrapper(faces, 0) (1, 5, 123, 1054) """ - cdef ListOfFaces faces - if isinstance(data, ListOfFaces): - faces = data - else: - assert isinstance(data, tuple) - faces = ListOfFaces(1, 64*len(data)) - for i in range(len(data)): - faces.data[0][i] = data[i] - cdef MemoryAllocator mem = MemoryAllocator() cdef size_t *output output = mem.allocarray(faces.n_atoms, sizeof(size_t)) + length = bit_rep_to_Vrep_list( - faces.data[index], output, faces.face_length) + faces.data[index], output, faces.face_length) return tuple(output[i] for i in range(length)) cdef inline size_t bit_rep_to_Vrep_list(uint64_t *face, size_t *output, @@ -524,12 +486,16 @@ cdef inline size_t bit_rep_to_Vrep_list(uint64_t *face, size_t *output, EXAMPLES:: sage: from sage.geometry.polyhedron.combinatorial_polyhedron.conversions \ - ....: import _bit_rep_to_Vrep_list_wrapper - sage: _bit_rep_to_Vrep_list_wrapper((17, 31)) + ....: import _bit_rep_to_Vrep_list_wrapper, \ + ....: _Vrep_list_to_bit_rep_wrapper + sage: faces = _Vrep_list_to_bit_rep_wrapper((0, 4, 64, 65, 66, 67, 68)) + sage: _bit_rep_to_Vrep_list_wrapper(faces, 0) (0, 4, 64, 65, 66, 67, 68) - sage: _bit_rep_to_Vrep_list_wrapper((13,)) + sage: faces = _Vrep_list_to_bit_rep_wrapper((0, 2, 3)) + sage: _bit_rep_to_Vrep_list_wrapper(faces, 0) (0, 2, 3) - sage: _bit_rep_to_Vrep_list_wrapper((0, 61)) + sage: faces = _Vrep_list_to_bit_rep_wrapper((64, 66, 67, 68, 69)) + sage: _bit_rep_to_Vrep_list_wrapper(faces, 0) (64, 66, 67, 68, 69) TESTS: From 63c88c0285c1118fcfc7225e702c0c33a8b27bc4 Mon Sep 17 00:00:00 2001 From: Ivo Maffei Date: Mon, 14 Sep 2020 10:35:43 +0200 Subject: [PATCH 086/713] fix bug; avoid long computations on import --- .../graphs/generators/distance_regular.pyx | 30 +++++++++++-------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/src/sage/graphs/generators/distance_regular.pyx b/src/sage/graphs/generators/distance_regular.pyx index f2450c79840..9f3606d4d69 100644 --- a/src/sage/graphs/generators/distance_regular.pyx +++ b/src/sage/graphs/generators/distance_regular.pyx @@ -1808,18 +1808,21 @@ _sporadic_graph_database = { shortened_00_11_binary_Golay_code_graph, (21, 20, 16, 9, 2, 1, 1, 2, 3, 16, 20, 21) : \ shortened_000_111_extended_binary_Golay_code_graph, - (22, 21, 20, 3, 2, 1, 1, 2, 3, 20, 21, 22) : \ + (22, 21, 20, 3, 2, 1, 1, 2, 3, 20, 21, 22) : lambda : \ codes.GolayCode(GF(2), extended=False).shortened([0]).cosetGraph, (3, 2, 1, 1, 1, 1, 1, 1, 2, 3) : DodecahedralGraph, - (22, 20, 18, 2, 1, 1, 2, 9, 20, 22) : \ + (22, 20, 18, 2, 1, 1, 2, 9, 20, 22) : lambda : \ codes.GolayCode(GF(3)).shortened([0]).cosetGraph, - (7, 6, 6, 1, 1, 1, 1, 6, 6, 7) : HoffmanSingletonGraph().bipartite_double, - (10, 9, 8, 2, 1, 1, 2, 8, 9, 10) : SimsGewirtzGraph().bipartite_double, - (16, 15, 12, 4, 1, 1, 4, 12, 15, 16) : \ + (7, 6, 6, 1, 1, 1, 1, 6, 6, 7) : lambda : \ + HoffmanSingletonGraph().bipartite_double, + (10, 9, 8, 2, 1, 1, 2, 8, 9, 10) : lambda : \ + SimsGewirtzGraph().bipartite_double, + (16, 15, 12, 4, 1, 1, 4, 12, 15, 16) : lambda : \ strongly_regular_graph(77, 16, 0, check=False).bipartite_double, - (22, 21, 16, 6, 1, 1, 6, 16, 21, 22) : HigmanSimsGraph().bipartite_double, + (22, 21, 16, 6, 1, 1, 6, 16, 21, 22) : lambda : \ + HigmanSimsGraph().bipartite_double, (3, 2, 2, 1, 1, 1, 1, 2) : CoxeterGraph, - (6, 5, 5, 4, 1, 1, 2, 6) : LintSchrijverGraph, + (6, 5, 5, 4, 1, 1, 2, 6) : vanLintSchrijverGraph, (7, 6, 4, 4, 1, 1, 1, 6) : DoublyTruncatedWittGraph, (9, 8, 6, 3, 1, 1, 3, 8) : distance_3_doubly_truncated_Golay_code_graph, (10, 8, 8, 2, 1, 1, 4, 5) : J2Graph, @@ -1827,21 +1830,22 @@ _sporadic_graph_database = { (5, 4, 1, 1, 1, 1, 4, 5) : WellsGraph, (6, 4, 2, 1, 1, 1, 4, 6) : FosterGraph3S6, (10, 6, 4, 1, 1, 2, 6, 10) : ConwaySmith_for_3S7, - (20, 18, 4, 1, 1, 2, 18, 20) : \ + (20, 18, 4, 1, 1, 2, 18, 20) : lambda : \ codes.GolayCode(GF(3), extended=False).shortened([0]).cosetGraph, (45, 32, 12, 1, 1, 6, 32, 45) : locally_GQ42_distance_transitive_graph, (117, 80, 24, 1, 1, 12, 80, 117) : graph_3O73, - (22, 21, 20, 1, 2, 6): \ + (22, 21, 20, 1, 2, 6): lambda : \ codes.GolayCode(GF(2), extended=False).punctured([0]).cosetGraph, - (23, 22, 21, 1, 2, 3): codes.GolayCode(GF(2), extended=False).cosetGraph, - (24, 23, 22, 21, 1, 2, 3, 24): codes.GolayCode(GF(2)).cosetGraph, + (23, 22, 21, 1, 2, 3): lambda : \ + codes.GolayCode(GF(2), extended=False).cosetGraph, + (24, 23, 22, 21, 1, 2, 3, 24): lambda : codes.GolayCode(GF(2)).cosetGraph, (12, 11, 10, 7, 1, 2, 5, 12): LeonardGraph, (15, 14, 10, 3, 1, 5, 12, 15): cocliques_HoffmannSingleton, (27, 10, 1, 1, 10, 27): GossetGraph, (30, 28, 24, 1, 3, 15): LargeWittGraph, (15, 14, 12, 1, 1, 9): TruncatedWittGraph, - (24, 22, 20, 1, 2, 12): codes.GolayCode(GF(3)).cosetGraph, - (21, 20, 16, 1, 2, 12): \ + (24, 22, 20, 1, 2, 12): lambda : codes.GolayCode(GF(3)).cosetGraph, + (21, 20, 16, 1, 2, 12): lambda : \ codes.GolayCode(GF(2), extended=False).punctured([0, 1]).cosetGraph } From eb8db08941ca0532e259b9cf8af9b2994aea7a0b Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 15 Sep 2020 17:01:41 -0700 Subject: [PATCH 087/713] src/sage_setup/library_order.py: Get library lists from sage.env.cython_aliases --- src/sage_setup/library_order.py | 67 ++------------------------------- 1 file changed, 3 insertions(+), 64 deletions(-) diff --git a/src/sage_setup/library_order.py b/src/sage_setup/library_order.py index c7bf73cb8c9..7c08c684e80 100644 --- a/src/sage_setup/library_order.py +++ b/src/sage_setup/library_order.py @@ -4,67 +4,6 @@ import os -# BEGIN copied from module_list.py (but #29706 removes the original). -# TODO: When #29706 is merged, simplify this module using the expanded cython_aliases. - -import pkgconfig - -# CBLAS can be one of multiple implementations -cblas_pc = pkgconfig.parse('cblas') -cblas_libs = cblas_pc['libraries'] -cblas_library_dirs = cblas_pc['library_dirs'] -cblas_include_dirs = cblas_pc['include_dirs'] - -# TODO: Remove Cygwin hack by installing a suitable cblas.pc -if os.path.exists('/usr/lib/libblas.dll.a'): - cblas_libs = ['gslcblas'] - -# LAPACK can be one of multiple implementations -lapack_pc = pkgconfig.parse('lapack') -lapack_libs = lapack_pc['libraries'] -lapack_library_dirs = lapack_pc['library_dirs'] -lapack_include_dirs = lapack_pc['include_dirs'] - -# GD image library -gd_pc = pkgconfig.parse('gdlib') -gd_libs = gd_pc['libraries'] -gd_library_dirs = gd_pc['library_dirs'] -gd_include_dirs = gd_pc['include_dirs'] - -# PNG image library -png_pc = pkgconfig.parse('libpng') -png_libs = png_pc['libraries'] -png_library_dirs = png_pc['library_dirs'] -png_include_dirs = png_pc['include_dirs'] - -# zlib -try: - zlib_pc = pkgconfig.parse('zlib') -except pkgconfig.PackageNotFoundError: - from collections import defaultdict - zlib_pc = defaultdict(list, {'libraries': ['z']}) -zlib_libs = zlib_pc['libraries'] -zlib_library_dirs = zlib_pc['library_dirs'] -zlib_include_dirs = zlib_pc['include_dirs'] - -######################################################### -### M4RI flags -######################################################### - -m4ri_pc = pkgconfig.parse('m4ri') -m4ri_libs = m4ri_pc['libraries'] -m4ri_library_dirs = m4ri_pc['library_dirs'] -m4ri_include_dirs = m4ri_pc['include_dirs'] - -m4ri_extra_compile_args = pkgconfig.cflags('m4ri').split() -try: - m4ri_extra_compile_args.remove("-pedantic") -except ValueError: - pass - -# END copied from module_list.py (but #29706 removes the original). - - # This list defines the *order* of linking libraries. A library should # be put *before* any library it links to. Cython allows # defining libraries using "# distutils: libraries = LIB". However, if @@ -85,11 +24,11 @@ "brial", "brial_groebner", "m4rie", -] + m4ri_libs + [ +] + aliases["M4RI_LIBRARIES"] + [ "zn_poly", "gap", -] + gd_libs + png_libs + [ +] + aliases["GDLIB_LIBRARIES"] + aliases["LIBPNG_LIBRARIES"] + [ "m", "readline", "Lfunction" , -] + cblas_libs + zlib_libs +] + aliases["CBLAS_LIBRARIES"] + aliases["ZLIB_LIBRARIES"] # Make a dict with library:order pairs, where the order are negative # integers sorted according to library_order_list. When sorting, From 5faac2030c853833cc2690ae924dc82e603ec750 Mon Sep 17 00:00:00 2001 From: Nicolo' Piazzalunga Date: Thu, 17 Sep 2020 11:15:09 +0200 Subject: [PATCH 088/713] added void linux spkgs for pari --- build/pkgs/pari/distros/void.txt | 4 ++++ build/pkgs/pari_elldata/distros/void.txt | 1 + build/pkgs/pari_galdata/distros/void.txt | 1 + build/pkgs/pari_galpol/distros/void.txt | 1 + build/pkgs/pari_seadata/distros/void.txt | 1 + 5 files changed, 8 insertions(+) create mode 100644 build/pkgs/pari_elldata/distros/void.txt create mode 100644 build/pkgs/pari_galdata/distros/void.txt create mode 100644 build/pkgs/pari_galpol/distros/void.txt create mode 100644 build/pkgs/pari_seadata/distros/void.txt diff --git a/build/pkgs/pari/distros/void.txt b/build/pkgs/pari/distros/void.txt index 7c8f04b36ae..0a6a6eb2969 100644 --- a/build/pkgs/pari/distros/void.txt +++ b/build/pkgs/pari/distros/void.txt @@ -1,2 +1,6 @@ pari pari-devel +pari-elldata +pari-galdata +pari-galpol +pari-seadata diff --git a/build/pkgs/pari_elldata/distros/void.txt b/build/pkgs/pari_elldata/distros/void.txt new file mode 100644 index 00000000000..540f0b1ab86 --- /dev/null +++ b/build/pkgs/pari_elldata/distros/void.txt @@ -0,0 +1 @@ +pari-elldata diff --git a/build/pkgs/pari_galdata/distros/void.txt b/build/pkgs/pari_galdata/distros/void.txt new file mode 100644 index 00000000000..38ad64bac82 --- /dev/null +++ b/build/pkgs/pari_galdata/distros/void.txt @@ -0,0 +1 @@ +pari-galdata diff --git a/build/pkgs/pari_galpol/distros/void.txt b/build/pkgs/pari_galpol/distros/void.txt new file mode 100644 index 00000000000..e3b41380f41 --- /dev/null +++ b/build/pkgs/pari_galpol/distros/void.txt @@ -0,0 +1 @@ +pari-galpol diff --git a/build/pkgs/pari_seadata/distros/void.txt b/build/pkgs/pari_seadata/distros/void.txt new file mode 100644 index 00000000000..3abdcf7bfcd --- /dev/null +++ b/build/pkgs/pari_seadata/distros/void.txt @@ -0,0 +1 @@ +pari-seadata From cb818f8a14148a4444e1ad85add1a9882063d63b Mon Sep 17 00:00:00 2001 From: Joshua Campbell Date: Thu, 17 Sep 2020 14:16:49 -0700 Subject: [PATCH 089/713] Scale clipping planes by overall plot size to accomodate very large/small plots Kept the same ratio between near and far clipping distances as before to preserve current depth buffer precision, but skewed it bit more toward the near clip plane for better zooming in. Can still zoom out plenty far. By the time you hit the far plane, the plot is so tiny as to be useless anyways. --- src/sage/ext_data/threejs/threejs_template.html | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/sage/ext_data/threejs/threejs_template.html b/src/sage/ext_data/threejs/threejs_template.html index 62dc0c44a93..0877bd87100 100644 --- a/src/sage/ext_data/threejs/threejs_template.html +++ b/src/sage/ext_data/threejs/threejs_template.html @@ -184,13 +184,17 @@ var aspect = window.innerWidth / window.innerHeight; + // Scale the near and far clipping planes along with the overall plot size. + var nearClip = 0.01 * midToCorner; + var farClip = 100 * midToCorner; + if ( options.projection === 'orthographic' ) { - var camera = new THREE.OrthographicCamera( -1, 1, 1, -1, -1000, 1000 ); + var camera = new THREE.OrthographicCamera( -1, 1, 1, -1, -farClip, farClip ); updateCameraAspect( camera, aspect ); return camera; } - return new THREE.PerspectiveCamera( 45, aspect, 0.1, 1000 ); + return new THREE.PerspectiveCamera( 45, aspect, nearClip, farClip ); } From e63f4c536d3c4ac85b2f3d37079b348745c32289 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 17 Sep 2020 21:18:50 -0700 Subject: [PATCH 090/713] build/pkgs/r/distros/void.txt: Change to R --- build/pkgs/r/distros/void.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/pkgs/r/distros/void.txt b/build/pkgs/r/distros/void.txt index 4286f428e3b..331bae08fb7 100644 --- a/build/pkgs/r/distros/void.txt +++ b/build/pkgs/r/distros/void.txt @@ -1 +1 @@ -r +R From 605fc6b5f0a3da9f5db169a09b0be71ea72b300a Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Wed, 16 Sep 2020 10:53:18 +0200 Subject: [PATCH 091/713] delete old files --- .../bitset_operations.cc | 273 ------------------ .../combinatorial_polyhedron/bitsets.cc | 173 ----------- 2 files changed, 446 deletions(-) delete mode 100644 src/sage/geometry/polyhedron/combinatorial_polyhedron/bitset_operations.cc delete mode 100644 src/sage/geometry/polyhedron/combinatorial_polyhedron/bitsets.cc diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/bitset_operations.cc b/src/sage/geometry/polyhedron/combinatorial_polyhedron/bitset_operations.cc deleted file mode 100644 index 5b028cccde0..00000000000 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/bitset_operations.cc +++ /dev/null @@ -1,273 +0,0 @@ -/* -#***************************************************************************** -# Copyright (C) 2019 Jonathan Kliem -# -# 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/ -#***************************************************************************** -*/ - -#include -#include -#include -using namespace std; - -/* -The following three functions can be optimized with intrinsics easily. -Enabling the intrinsics is subject of #27103. -The chunksize is the number of vertices/atoms -that can be handled with one operation. -*/ - -// Any Bit-representation is assumed to be `chunksize`-Bit aligned. -const size_t chunksize = 64; - -inline int is_subset(uint64_t *A, uint64_t *B, size_t face_length){ - /* - Return ``A & ~B == 0``. - A is not subset of B, iff there is a vertex in A, which is not in B. - ``face_length`` is the length of A and B in terms of uint64_t. - */ - for (size_t i = 0; i < face_length; i++){ - if (A[i] & ~B[i]){ - return 0; - } - } - return 1; -} - -inline void intersection(uint64_t *dest, uint64_t *A, uint64_t *B, \ - size_t face_length){ - /* - Set ``dest = A & B``, i.e. dest is the intersection of A and B. - ``face_length`` is the length of A, B and dest in terms of uint64_t. - */ - for (size_t i = 0; i < face_length; i++){ - dest[i] = A[i] & B[i]; - } -} - -inline void unite(uint64_t *dest, uint64_t *A, uint64_t *B, \ - size_t face_length){ - /* - Set ``dest = A | B``, i.e. dest is the union of A and B. - ``face_length`` is the length of A, B and dest in terms of uint64_t. - */ - for (size_t i = 0; i < face_length; i++){ - dest[i] = A[i] | B[i]; - } -} - -inline size_t count_atoms(const uint64_t* A, size_t face_length){ - /* - Return the number of atoms/vertices in A. - This is the number of set bits in A. - ``face_length`` is the length of A in terms of uint64_t. - */ - size_t i; - unsigned int count = 0; - for (i=0; i> 1) & 0x5555555555555555ULL); - a = (a & 0x3333333333333333ULL) + ((a >> 2) & 0x3333333333333333ULL); - count += ( ((a + (a >> 4)) & 0x0f0f0f0f0f0f0f0fULL) * 0x0101010101010101ULL ) >> 56; - } - return count; -} -inline int is_zero(uint64_t *A, size_t face_length){ - for (size_t i = 0; i < face_length; i++){ - if (A[i]){ - return 0; - } - } - return 1; -} - -inline int is_contained_in_one(uint64_t *face, uint64_t **faces, size_t n_faces, size_t face_length){ - /* - Return whether ``face`` is contained in one of ``faces``. - */ - for(size_t i = 0; i < n_faces; i++){ - if (is_subset(face, faces[i], face_length)) - return 1; - } - return 0; -} - -inline int is_contained_in_one(uint64_t *face, uint64_t **faces, size_t n_faces, size_t face_length, size_t skip){ - /* - Return whether ``face`` is contained in one of ``faces``. - - Skips ``faces[skip]``. - */ - return is_contained_in_one(face, faces, skip, face_length) || \ - is_contained_in_one(face, faces+skip+1, n_faces-skip-1, face_length); -} - -inline int contains_one(uint64_t *face, uint64_t **faces, size_t n_faces, size_t face_length){ - /* - Return whether ``face`` contains one of ``faces``. - */ - for(size_t i = 0; i < n_faces; i++){ - if (is_subset(faces[i], face, face_length)) - return 1; - } - return 0; -} - -inline int contains_one(uint64_t *face, uint64_t **faces, size_t n_faces, size_t face_length, size_t skip){ - /* - Return whether ``face`` contains one of ``faces``. - - Skips ``faces[skip]``. - */ - return contains_one(face, faces, skip, face_length) || \ - contains_one(face, faces+skip+1, n_faces-skip-1, face_length); -} - -size_t get_next_level(\ - uint64_t **faces, size_t n_faces, \ - uint64_t **newfaces, uint64_t **visited_all, \ - size_t n_visited_all, size_t face_length){ - /* - Set ``newfaces`` to be the facets of ``faces[n_faces -1]`` - that are not contained in a face of ``visited_all``. - - INPUT: - - - ``newfaces`` -- quasi of type ``uint64_t[n_faces -1][face_length]``, - needs to be ``chunksize``-Bit aligned - - ``visited_all`` -- quasi of type ``*uint64_t[n_visited_all] - - ``face_length`` -- length of the faces - - OUTPUT: - - - return number of ``newfaces`` - - set ``newfaces`` to point to the new faces - - ALGORITHM: - - To get all facets of ``faces[n_faces-1]``, we would have to: - - Intersect the first ``n_faces-1`` faces of ``faces`` with the last face. - - Add all the intersection of ``visited_all`` with the last face - - Out of both the inclusion-maximal ones are of codimension one, i.e. facets. - - As we have visited all faces of ``visited_all``, we alter the algorithm - to not revisit: - Step 1: Intersect the first ``n_faces-1`` faces of ``faces`` with the last face. - Step 2: Out of those the inclusion-maximal ones are some of the facets. - At least we obtain all of those, that we have not already visited. - Maybe, we get some more. - Step 3: Only keep those that we have not already visited. - We obtain exactly the facets of ``faces[n_faces-1]`` that we have - not visited yet. - */ - - // We keep track, which face in ``newfaces`` is a new face. - int is_not_newface[n_faces -1]; - - // Step 1: - for (size_t j = 0; j < n_faces - 1; j++){ - intersection(newfaces[j], faces[j], faces[n_faces - 1], face_length); - is_not_newface[j] = 0; - } - - - // For each face we will do Step 2 and Step 3. - for (size_t j = 0; j < n_faces-1; j++){ - if (is_contained_in_one(newfaces[j], newfaces, n_faces-1, face_length, j) || \ - is_contained_in_one(newfaces[j], visited_all, n_visited_all, face_length)) - is_not_newface[j] = 1; - } - - // Set ``newfaces`` to point to the correct ones. - size_t n_newfaces = 0; // length of newfaces2 - for (size_t j = 0; j < n_faces -1; j++){ - if (is_not_newface[j]) { - // Not a new face of codimension 1. - continue; - } - // It is a new face of codimension 1. - // Either ``n_newfaces == j`` or ``newfaces[n_newfaces]`` is not - // a new face. - swap(newfaces[n_newfaces], newfaces[j]); - n_newfaces++; - } - return n_newfaces; -} - -size_t get_next_level_simple(\ - uint64_t **faces, const size_t n_faces, \ - uint64_t **newfaces, uint64_t **visited_all, \ - size_t n_visited_all, size_t face_length, - uint64_t **faces_coatom_rep, \ - uint64_t **newfaces_coatom_rep, uint64_t **visited_all_coatom_rep, \ - size_t face_length_coatom_rep){ - /* - As above, but modified for the case where every interval not containing zero is boolean. - */ - - // We keep track, which face in ``newfaces`` is a new face. - int is_not_newface[n_faces -1]; - - // Step 1: - for (size_t j = 0; j < n_faces - 1; j++){ - intersection(newfaces[j], faces[j], faces[n_faces - 1], face_length); - unite(newfaces_coatom_rep[j], faces_coatom_rep[j], faces_coatom_rep[n_faces - 1], face_length_coatom_rep); - is_not_newface[j] = 0; - } - - // For each face we will do Step 2 and Step 3. - for (size_t j = 0; j < n_faces-1; j++){ - // Step 2: - // Check if the atom representation is zero. - // - // and - // - // - // Step 3: - if (is_zero(newfaces[j], face_length) || \ - contains_one(newfaces_coatom_rep[j], visited_all_coatom_rep, n_visited_all, face_length_coatom_rep)) - is_not_newface[j] = 1; - } - - // Set ``newfaces`` to point to the correct ones. - size_t n_newfaces = 0; // length of newfaces2 - for (size_t j = 0; j < n_faces -1; j++){ - if (is_not_newface[j]) { - // Not a new face of codimension 1. - continue; - } - // It is a new face of codimension 1. - // Either ``n_newfaces == j`` or ``newfaces[n_newfaces]`` is not - // a new face. - swap(newfaces[n_newfaces], newfaces[j]); - swap(newfaces_coatom_rep[n_newfaces], newfaces_coatom_rep[j]); - n_newfaces++; - } - return n_newfaces; -} - -size_t bit_rep_to_coatom_rep(uint64_t *face, uint64_t **coatoms, \ - size_t n_coatoms, size_t face_length, \ - size_t *output){ - /* - Write the coatom-representation of face in output. Return length. - ``face_length`` is the length of ``face`` and ``coatoms[i]`` - in terms of uint64_t. - ``n_coatoms`` length of ``coatoms``. - */ - size_t count_length = 0; - for (size_t i = 0; i < n_coatoms; i++){ - if (is_subset(face, coatoms[i], face_length)){ - // ``face`` is contain in ``coatoms[i]``, - // then ``i`` is an element in the coatom-representation. - output[count_length] = i; - count_length++; - } - } - return count_length; -} diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/bitsets.cc b/src/sage/geometry/polyhedron/combinatorial_polyhedron/bitsets.cc deleted file mode 100644 index 14a07044797..00000000000 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/bitsets.cc +++ /dev/null @@ -1,173 +0,0 @@ -/* -############################################################################# -# More or less taken from ``src/sage/data_structures/bitset.pxi`` -############################################################################# -*/ - -#include "gmp.h" -#include -using namespace std; - -const size_t index_shift = 6; -const size_t LIMB_BITS = 64; - -/* -############################################################################# -# Creating limb patterns -############################################################################# -*/ - -inline uint64_t limb_one_set_bit(size_t n){ - /* - Return a limb with only bit n set. - */ - return (uint64_t) 1 << (n % LIMB_BITS); -} - -inline uint64_t limb_one_zero_bit(size_t n){ - /* - Return a limb with all bits set, except for bit n. - */ - return !((uint64_t) 1 << (n % LIMB_BITS)); -} - -inline uint64_t limb_lower_bits_down(size_t n){ - /* - Return a limb with the lower n bits set, where n is interpreted - in [0 .. 64-1]. - */ - return ((uint64_t) 1 << (n % LIMB_BITS)) - 1; -} - -/* -############################################################################# -# Bitset Initalization -############################################################################# -*/ - -inline void bitset_clear(uint64_t* bits, size_t face_length){ - /* - Remove all elements from the set. - */ - memset(bits, 0, face_length*8); -} - -inline void bitset_copy(uint64_t* dst, uint64_t* src, size_t face_length){ - /* - Copy the bitset src over to the bitset dst, overwriting dst. - - We assume ``dst.limbs == src.limbs``. - */ - memcpy(dst, src, face_length*8); -} - -inline void bitset_copy(uint64_t* dst, uint64_t* src, size_t face_length_dst, size_t face_length_src){ - /* - Copy the bitset src over to the bitset dst, overwriting dst. - - If ``dst`` is longer, then additional bits are set to zero. - */ - if (face_length_src > face_length_dst) - face_length_src = face_length_dst; - - memcpy(dst, src, face_length_src*8); - memset(dst+face_length_src, 0, (face_length_dst-face_length_src)*8); -} - -inline int bitset_cmp(uint64_t* a, uint64_t* b, size_t face_length){ - /* - Compare bitsets a and b. Return 0 if the two sets are - identical, and consistently return -1 or 1 for two sets that are - not equal. - - We assume ``a.limbs >= b.limbs``. - */ - return memcmp(a, b, face_length*8); -} - -/* -############################################################################# -# Bitset Bit Manipulation -############################################################################# -*/ - -inline int bitset_in(uint64_t* bits, size_t n){ - /* - Check if n is in bits. Return True (i.e., 1) if n is in the - set, False (i.e., 0) otherwise. - */ - return bits[n >> index_shift] & limb_one_set_bit(n); -} - -inline void bitset_discard(uint64_t* bits, size_t n){ - /* - Remove n from bits. - */ - bits[n >> index_shift] &= limb_one_zero_bit(n); -} - -inline void bitset_add(uint64_t* bits, size_t n){ - /* - Add n to bits. - */ - bits[n >> index_shift] |= limb_one_set_bit(n); -} - -inline void bitset_set_first_n(uint64_t* bits, size_t face_length, size_t n){ - /* - Set exactly the first n bits. - */ - size_t i; - size_t index = n >> index_shift; - for(i = 0; i < index; i++) - bits[i] = -1; - if (index < face_length) - bits[index] = limb_lower_bits_down(n); - for(i=index+1; i < face_length; i++) - bits[i] = 0; -} - -/* -############################################################################# -# Bitset Searching -############################################################################# -*/ - -inline size_t _bitset_first_in_limb(uint64_t limb){ - /* - Given a limb of a bitset, return the index of the first nonzero - bit. If there are no bits set in the limb, return -1. - */ - if (limb == 0) - return -1; - return mpn_scan1(&limb, 0); -} - -inline long _bitset_first_in_limb_nonzero(uint64_t limb){ - /* - Given a non-zero limb of a bitset, return the index of the first - nonzero bit. - */ - return mpn_scan1(&limb, 0); -} - -inline size_t bitset_next(uint64_t* bits, size_t face_length, size_t n){ - /* - Calculate the index of the next element in the set, starting at - (and including) n. Return -1 if there are no elements from n - onwards. - */ - if (n >= face_length*LIMB_BITS) - return -1; - - size_t i = n >> index_shift; - size_t limb = bits[i] & ~limb_lower_bits_down(n); - size_t ret = _bitset_first_in_limb(limb); - if (ret != (size_t) -1) - return (i << index_shift) | ret; - for(i++; i < face_length; i++){ - if (bits[i]) - return (i << index_shift) | _bitset_first_in_limb_nonzero(bits[i]); - } - return -1; -} From f3e851f25544e39e8c1be91e169049f14f435c69 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Wed, 16 Sep 2020 10:54:31 +0200 Subject: [PATCH 092/713] use face_list_t and face_t for combinatorial polyhedron --- .../combinatorial_polyhedron/base.pxd | 5 +- .../combinatorial_polyhedron/base.pyx | 61 ++-- .../combinatorial_face.pxd | 6 +- .../combinatorial_face.pyx | 72 ++--- .../combinatorial_polyhedron/conversions.pxd | 11 +- .../combinatorial_polyhedron/conversions.pyx | 104 +++---- .../face_iterator.pxd | 34 +- .../face_iterator.pyx | 292 ++++++------------ .../face_list_data_structure.pxd | 1 - .../list_of_faces.pxd | 14 +- .../list_of_faces.pyx | 179 +++-------- .../polyhedron_face_lattice.pxd | 20 +- .../polyhedron_face_lattice.pyx | 286 ++++------------- 13 files changed, 317 insertions(+), 768 deletions(-) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pxd b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pxd index 9015e2329d1..f417e368f84 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pxd +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pxd @@ -1,9 +1,9 @@ cimport cython -from libc.stdint cimport uint64_t from sage.ext.memory_allocator cimport MemoryAllocator from sage.structure.sage_object cimport SageObject from .face_iterator cimport FaceIterator, CombinatorialFace from .list_of_faces cimport ListOfFaces +from .face_data_structure cimport face_t from .polyhedron_face_lattice cimport PolyhedronFaceLattice @cython.final @@ -21,7 +21,7 @@ cdef class CombinatorialPolyhedron(SageObject): cdef bint _bounded # ``True`` iff Polyhedron is bounded cdef ListOfFaces _bitrep_facets # facets in bit representation cdef ListOfFaces _bitrep_Vrep # vertices in bit representation - cdef ListOfFaces _far_face # a 'face' containing all none-vertices of Vrep + cdef face_t _far_face # a 'face' containing all none-vertices of Vrep cdef tuple _far_face_tuple cdef tuple _f_vector @@ -50,7 +50,6 @@ cdef class CombinatorialPolyhedron(SageObject): cdef bint is_bounded(self) cdef ListOfFaces bitrep_facets(self) cdef ListOfFaces bitrep_Vrep(self) - cdef ListOfFaces far_face(self) cdef tuple far_face_tuple(self) # Methods to obtain a different combinatorial polyhedron. diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx index df721ff8a19..86175a23769 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx @@ -1,7 +1,3 @@ -# distutils: depends = sage/geometry/polyhedron/combinatorial_polyhedron/bitset_operations.cc -# distutils: language = c++ -# distutils: extra_compile_args = -std=c++11 - r""" Combinatorial polyhedron @@ -20,7 +16,7 @@ Terminology used in this module: - Facets -- facets of the polyhedron. - Vrepresentation -- represents a face by the list of Vrep it contains. - Hrepresentation -- represents a face by a list of Hrep it is contained in. -- bit representation -- represents incidences as ``uint64_t``-array, where +- bit representation -- represents incidences as bitset, where each bit represents one incidence. There might be trailing zeros, to fit alignment requirements. In most instances, faces are represented by the @@ -100,17 +96,14 @@ from .conversions \ incidence_matrix_to_bit_rep_of_Vrep, \ facets_tuple_to_bit_rep_of_facets, \ facets_tuple_to_bit_rep_of_Vrep +from .conversions cimport Vrep_list_to_bit_rep from sage.misc.cachefunc import cached_method from sage.rings.integer cimport smallInteger from cysignals.signals cimport sig_check, sig_block, sig_unblock from sage.matrix.matrix_integer_dense cimport Matrix_integer_dense -cdef extern from "bitset_operations.cc": - cdef size_t count_atoms(uint64_t *A, size_t face_length) -# Return the number of atoms/vertices in A. -# This is the number of set bits in A. -# ``face_length`` is the length of A in terms of uint64_t. +from .face_data_structure cimport face_len_atoms, face_init cdef extern from "Python.h": int unlikely(int) nogil # Defined by Cython @@ -347,6 +340,7 @@ cdef class CombinatorialPolyhedron(SageObject): self._equalities = () self._all_faces = None self._mem_tuple = () + cdef MemoryAllocator mem # ``_length_edges_list`` should not be touched in an instance # of :class:`CombinatorialPolyhedron`. This number can be altered, @@ -468,13 +462,14 @@ cdef class CombinatorialPolyhedron(SageObject): # Initializing the Vrep as their Bit-representation. self._bitrep_Vrep = incidence_matrix_to_bit_rep_of_Vrep(data_modified) - self._n_facets = self.bitrep_facets().n_faces + self._n_facets = self.bitrep_facets().n_faces() # Initialize far_face if unbounded. if not self._bounded: - self._far_face = facets_tuple_to_bit_rep_of_facets((tuple(far_face),), self._n_Vrepresentation) - else: - self._far_face = None + mem = MemoryAllocator() + self._mem_tuple += (mem,) + face_init(self._far_face, self.bitrep_facets().n_atoms(), self._n_facets, mem) + Vrep_list_to_bit_rep(tuple(far_face), self._far_face) elif isinstance(data, numbers.Integral): # To construct a trivial polyhedron, equal to its affine hull, @@ -496,22 +491,21 @@ cdef class CombinatorialPolyhedron(SageObject): # Initializing the Vrep as their Bit-representation. self._bitrep_Vrep = facets_tuple_to_bit_rep_of_Vrep((), 0) - self._far_face = None - elif isinstance(data, (tuple, list)) and len(data) == 2 and isinstance(data[0], ListOfFaces) and isinstance(data[1], ListOfFaces): # Initialize self from two ``ListOfFaces``. self._bitrep_facets = data[0] self._bitrep_Vrep = data[1] - self._n_Hrepresentation = self._bitrep_facets.n_faces - self._n_Vrepresentation = self._bitrep_Vrep.n_faces + self._n_Hrepresentation = self._bitrep_facets.n_faces() + self._n_Vrepresentation = self._bitrep_Vrep.n_faces() self._n_facets = self._n_Hrepresentation # Initialize far_face if unbounded. if not self._bounded: - self._far_face = facets_tuple_to_bit_rep_of_facets((tuple(far_face),), self._n_Vrepresentation) - else: - self._far_face = None + mem = MemoryAllocator() + self._mem_tuple += (mem,) + face_init(self._far_face, self.bitrep_facets().n_atoms(), self._n_facets, mem) + Vrep_list_to_bit_rep(tuple(far_face), self._far_face) else: # Input is a "list" of facets. @@ -552,9 +546,10 @@ cdef class CombinatorialPolyhedron(SageObject): # Initialize far_face if unbounded. if not self._bounded: - self._far_face = facets_tuple_to_bit_rep_of_facets((tuple(far_face),), n_Vrepresentation) - else: - self._far_face = None + mem = MemoryAllocator() + self._mem_tuple += (mem,) + face_init(self._far_face, self.bitrep_facets().n_atoms(), self._n_facets, mem) + Vrep_list_to_bit_rep(tuple(far_face), self._far_face) if not self._bounded: self._far_face_tuple = tuple(far_face) @@ -1876,13 +1871,12 @@ cdef class CombinatorialPolyhedron(SageObject): raise NotImplementedError("this function is implemented for polytopes only") cdef ListOfFaces facets = self._bitrep_facets - cdef size_t n_facets = facets.n_faces - cdef size_t face_length = facets.face_length + cdef size_t n_facets = facets.n_faces() cdef size_t i cdef int dim = self.dimension() for i in range(n_facets): - if count_atoms(facets.data[i], face_length) != dim: + if face_len_atoms(facets.data.faces[i]) != dim: return False return True @@ -1984,13 +1978,12 @@ cdef class CombinatorialPolyhedron(SageObject): if not self.is_bounded(): return False cdef ListOfFaces vertices = self._bitrep_Vrep - cdef size_t n_vertices = vertices.n_faces - cdef size_t face_length = vertices.face_length + cdef size_t n_vertices = vertices.n_faces() cdef size_t i cdef int dim = self.dimension() for i in range(n_vertices): - if count_atoms(vertices.data[i], face_length) != dim: + if face_len_atoms(vertices.data.faces[i]) != dim: return False return True @@ -2757,14 +2750,6 @@ cdef class CombinatorialPolyhedron(SageObject): """ return self._bitrep_Vrep - cdef ListOfFaces far_face(self): - r""" - Return a list with only the far face. - - This is a face containing all Vrepresentatives that are not vertices. - """ - return self._far_face - cdef tuple far_face_tuple(self): r""" Return the far face as it was given on initialization. diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pxd b/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pxd index bf1b8e9cb56..64dd767cc94 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pxd +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pxd @@ -1,22 +1,20 @@ cimport cython -from libc.stdint cimport uint64_t from sage.ext.memory_allocator cimport MemoryAllocator from sage.structure.sage_object cimport SageObject from .list_of_faces cimport ListOfFaces +from .face_data_structure cimport face_t from .face_iterator cimport FaceIterator @cython.final cdef class CombinatorialFace(SageObject): cdef readonly bint _dual # if 1, then iterate over dual Polyhedron - cdef ListOfFaces face_mem # constructing face - cdef uint64_t *face # the face in bit-rep + cdef face_t face # the face in bit-rep cdef MemoryAllocator _mem cdef size_t *atom_rep # a place where atom-representation of face will be stored cdef size_t *coatom_rep # a place where coatom-representation of face will be stored cdef int _dimension # dimension of current face, dual dimension if ``dual`` cdef int _ambient_dimension # dimension of the polyhedron - cdef size_t face_length # stores length of the faces in terms of uint64_t # An index to give different hashes for all faces of a Polyhedron. # The index must be chosen such that `F \subset G` implies ``hash(F) < hash(G)``. diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx index 28db5c498f9..c370503cf00 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx @@ -1,8 +1,3 @@ -# distutils: depends = sage/geometry/polyhedron/combinatorial_polyhedron/bitset_operations.cc sage/geometry/polyhedron/combinatorial_polyhedron/bitsets.cc -# distutils: language = c++ -# distutils: extra_compile_args = -std=c++11 -# distutils: libraries = gmp - r""" Combinatorial face of a polyhedron @@ -77,23 +72,8 @@ from .conversions cimport bit_rep_to_Vrep_list from .base cimport CombinatorialPolyhedron from .face_iterator cimport FaceIterator_base from .polyhedron_face_lattice cimport PolyhedronFaceLattice - -cdef extern from "bitset_operations.cc": - cdef size_t count_atoms(uint64_t *A, size_t face_length) -# Return the number of atoms/vertices in A. -# This is the number of set bits in A. -# ``face_length`` is the length of A in terms of uint64_t. - - cdef size_t bit_rep_to_coatom_rep( - uint64_t *face, uint64_t **coatoms, size_t n_coatoms, - size_t face_length, size_t *output) -# Write the coatom-representation of face in output. Return length. -# ``face_length`` is the length of ``face`` and ``coatoms[i]`` -# in terms of uint64_t. -# ``n_coatoms`` length of ``coatoms``. - -cdef extern from "bitsets.cc": - cdef void bitset_copy(uint64_t* dst, uint64_t* src, size_t face_length) +from .face_data_structure cimport face_len_atoms, face_init, face_copy +from .face_list_data_structure cimport bit_rep_to_coatom_rep cdef extern from "Python.h": int unlikely(int) nogil # Defined by Cython @@ -116,7 +96,7 @@ cdef class CombinatorialFace(SageObject): sage: F = C.face_lattice() sage: F._elements[3] - 17 + 34 sage: C.face_by_face_lattice_index(29) A 1-dimensional face of a 5-dimensional combinatorial polyhedron @@ -187,18 +167,21 @@ cdef class CombinatorialFace(SageObject): # Copy data from FaceIterator. it = data self._dual = it.dual - self.face_mem = ListOfFaces(1, it.structure.face_length*64) - self.face = self.face_mem.data[0] - bitset_copy(self.face, it.structure.face, it.structure.face_length) self._mem = MemoryAllocator() + self.atoms = it.atoms + self.coatoms = it.coatoms + + if it.structure.face_status == 0: + raise LookupError("face iterator not set to a face") + + face_init(self.face, self.coatoms.n_atoms(), self.coatoms.n_coatoms(), self._mem) + face_copy(self.face, it.structure.face) + self._dimension = it.structure.current_dimension self._ambient_dimension = it.structure.dimension - self.face_length = it.structure.face_length self._ambient_Vrep = it._Vrep self._ambient_facets = it._facet_names self._equalities = it._equalities - self.atoms = it.atoms - self.coatoms = it.coatoms self._hash_index = it.structure._index self._initialized_from_face_lattice = False @@ -212,18 +195,18 @@ cdef class CombinatorialFace(SageObject): # Copy data from PolyhedronFaceLattice. self._dual = all_faces.dual - self.face_mem = ListOfFaces(1, all_faces.face_length*64) - self.face = self.face_mem.data[0] - bitset_copy(self.face, all_faces.faces[dimension+1][index], all_faces.face_length) self._mem = MemoryAllocator() + self.atoms = all_faces.atoms + self.coatoms = all_faces.coatoms + + face_init(self.face, self.coatoms.n_atoms(), self.coatoms.n_coatoms(), self._mem) + face_copy(self.face, all_faces.faces[dimension+1].faces[index]) + self._dimension = dimension self._ambient_dimension = all_faces.dimension - self.face_length = all_faces.face_length self._ambient_Vrep = all_faces._Vrep self._ambient_facets = all_faces._facet_names self._equalities = all_faces._equalities - self.atoms = all_faces.atoms - self.coatoms = all_faces.coatoms self._initialized_from_face_lattice = True @@ -773,32 +756,23 @@ cdef class CombinatorialFace(SageObject): Compute the number of atoms in the current face by counting the number of set bits. """ - if self.face: - return count_atoms(self.face, self.face_length) - - # The face was not initialized properly. - raise LookupError("``FaceIterator`` does not point to a face") + return face_len_atoms(self.face) cdef size_t set_coatom_rep(self) except -1: r""" Set ``coatom_rep`` to be the coatom-representation of the current face. Return its length. """ - cdef size_t n_coatoms = self.coatoms.n_faces - cdef uint64_t **coatoms = self.coatoms.data - cdef size_t face_length = self.face_length if not self.coatom_rep: - self.coatom_rep = self._mem.allocarray(self.coatoms.n_faces, sizeof(size_t)) - return bit_rep_to_coatom_rep(self.face, coatoms, n_coatoms, - face_length, self.coatom_rep) + self.coatom_rep = self._mem.allocarray(self.coatoms.n_faces(), sizeof(size_t)) + return bit_rep_to_coatom_rep(self.face, self.coatoms.data, self.coatom_rep) cdef size_t set_atom_rep(self) except -1: r""" Set ``atom_rep`` to be the atom-representation of the current face. Return its length. """ - cdef size_t face_length = self.face_length if not self.atom_rep: - self.atom_rep = self._mem.allocarray(self.coatoms.n_atoms, sizeof(size_t)) - return bit_rep_to_Vrep_list(self.face, self.atom_rep, face_length) + self.atom_rep = self._mem.allocarray(self.coatoms.n_atoms(), sizeof(size_t)) + return bit_rep_to_Vrep_list(self.face, self.atom_rep) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/conversions.pxd b/src/sage/geometry/polyhedron/combinatorial_polyhedron/conversions.pxd index e36e0c2eb73..82ac7f6dcb8 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/conversions.pxd +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/conversions.pxd @@ -1,10 +1,7 @@ -from libc.stdint cimport uint64_t +from .face_list_data_structure cimport face_t -cdef int Vrep_list_to_bit_rep(tuple Vrep_list, uint64_t *output, - size_t face_length) except -1 +cdef int Vrep_list_to_bit_rep(tuple Vrep_list, face_t output) except -1 -cdef int incidences_to_bit_rep(tuple incidences, uint64_t *output, - size_t face_length) except -1 +cdef int incidences_to_bit_rep(tuple incidences, face_t output) except -1 -cdef size_t bit_rep_to_Vrep_list(uint64_t *face, size_t *output, - size_t face_length) except -1 +cdef size_t bit_rep_to_Vrep_list(face_t face, size_t *output) except -1 diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/conversions.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/conversions.pyx index 164bf1a397b..95d8b96f220 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/conversions.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/conversions.pyx @@ -1,8 +1,3 @@ -# distutils: depends = sage/geometry/polyhedron/combinatorial_polyhedron/bitsets.cc -# distutils: language = c++ -# distutils: extra_compile_args = -std=c++11 -# distutils: libraries = gmp - r""" Conversions @@ -30,7 +25,6 @@ Obtain the facets of a polyhedron as :class:`~sage.geometry.polyhedron.combinato sage: inc = P.incidence_matrix() sage: mod_inc = inc.delete_columns([i for i,V in enumerate(P.Hrepresentation()) if V.is_equation()]) sage: face_list = incidence_matrix_to_bit_rep_of_facets(mod_inc) - sage: face_list = incidence_matrix_to_bit_rep_of_facets(mod_inc) sage: face_list.compute_dimension() 4 @@ -78,15 +72,12 @@ from sage.structure.element import is_Matrix from .list_of_faces cimport ListOfFaces from sage.matrix.matrix_integer_dense cimport Matrix_integer_dense from sage.ext.memory_allocator cimport MemoryAllocator +from .face_data_structure cimport face_next_atom, face_add_atom_safe, facet_set_coatom, face_clear +from .face_list_data_structure cimport face_list_t cdef extern from "Python.h": int unlikely(int) nogil # Defined by Cython -cdef extern from "bitsets.cc": - cdef void bitset_add(uint64_t* bits, size_t n) - cdef size_t bitset_next(uint64_t* bits, size_t face_length, size_t n) - cdef void bitset_clear(uint64_t* bits, size_t face_length) - def _Vrep_list_to_bit_rep_wrapper(tup): r""" A function to allow doctesting of :func:`Vrep_list_to_bit_rep`. @@ -98,12 +89,11 @@ def _Vrep_list_to_bit_rep_wrapper(tup): sage: _Vrep_list_to_bit_rep_wrapper((0, 3)).matrix() [1 0 0 1] """ - cdef ListOfFaces output = ListOfFaces(1, max(tup) + 1) - Vrep_list_to_bit_rep(tup, output.data[0], output.face_length) + cdef ListOfFaces output = ListOfFaces(1, max(tup) + 1, 1) + Vrep_list_to_bit_rep(tup, output.data.faces[0]) return output -cdef int Vrep_list_to_bit_rep(tuple Vrep_list, uint64_t *output, - size_t face_length) except -1: +cdef int Vrep_list_to_bit_rep(tuple Vrep_list, face_t output) except -1: r""" Convert a vertex list into Bit-representation. Store it in ``output``. @@ -112,10 +102,8 @@ cdef int Vrep_list_to_bit_rep(tuple Vrep_list, uint64_t *output, INPUT: - - ``vertex_list`` -- tuple of pairwise distinct positive integers in - ``range(face_length*64)`` - - ``output`` -- array of ``uint64_t`` of length ``face_length`` - - ``face_length`` + - ``vertex_list`` -- tuple of pairwise distinct positive integers that fit into ``output`` + - ``output`` -- an already initialized face OUTPUT: @@ -139,16 +127,12 @@ cdef int Vrep_list_to_bit_rep(tuple Vrep_list, uint64_t *output, ValueError: entries of ``tup`` are not distinct """ cdef size_t entry # will iterate over tup - cdef size_t position # determines the position in output of entry - cdef size_t value # determines which bit will be set in output[position] - bitset_clear(output, face_length) + face_clear(output) if unlikely(len(Vrep_list) != len(set(Vrep_list))): raise ValueError("entries of ``tup`` are not distinct") for entry in Vrep_list: - if unlikely(entry >= 64*face_length): - raise IndexError("output too small to represent %s"%entry) - bitset_add(output, entry) + face_add_atom_safe(output, entry) def _incidences_to_bit_rep_wrapper(tup): r""" @@ -161,13 +145,11 @@ def _incidences_to_bit_rep_wrapper(tup): sage: _incidences_to_bit_rep_wrapper((1,0,0,1)).matrix() [1 0 0 1] """ - cdef ListOfFaces output = ListOfFaces(1, len(tup)) - incidences_to_bit_rep(tup, output.data[0], output.face_length) + cdef ListOfFaces output = ListOfFaces(1, len(tup), 1) + incidences_to_bit_rep(tup, output.data.faces[0]) return output -cdef int incidences_to_bit_rep(tuple incidences, uint64_t *output, - size_t face_length) except -1: - +cdef int incidences_to_bit_rep(tuple incidences, face_t output) except -1: r""" Convert a tuple of incidences into Bit-representation. @@ -176,10 +158,8 @@ cdef int incidences_to_bit_rep(tuple incidences, uint64_t *output, INPUT: - - ``incidences`` -- tuple of integers representing incidences - of length at most ``face_length*64`` - - ``output`` -- array of ``uint64_t`` of length ``face_length`` - - ``face_length`` + - ``incidences`` -- tuple of integers representing incidences that fit into ``output`` + - ``output`` -- an already initialized face OUTPUT: @@ -198,13 +178,11 @@ cdef int incidences_to_bit_rep(tuple incidences, uint64_t *output, cdef size_t entry # index for the entries in tup cdef size_t length = len(incidences) - bitset_clear(output, face_length) - if unlikely(length > 64*face_length): - raise IndexError("output too small to represent all incidences") + face_clear(output) for entry in range(length): if incidences[entry]: # Vrep ``entry`` is contained in the face, so set the corresponding bit - bitset_add(output, entry) + face_add_atom_safe(output, entry) def incidence_matrix_to_bit_rep_of_facets(Matrix_integer_dense matrix): r""" @@ -253,23 +231,21 @@ def incidence_matrix_to_bit_rep_of_facets(Matrix_integer_dense matrix): # ``matrix.nrows()`` Vrep. cdef size_t nrows = matrix._nrows cdef size_t ncols = matrix._ncols - cdef ListOfFaces facets = ListOfFaces(ncols, nrows) - cdef uint64_t **facets_data = facets.data - cdef uint64_t *output - cdef size_t face_length = facets.face_length + cdef ListOfFaces facets = ListOfFaces(ncols, nrows, ncols) + cdef face_t output cdef size_t entry # index for the entries in tup cdef size_t i for i in range(ncols): - output = facets_data[i] - bitset_clear(output, face_length) + output = facets.data.faces[i] + facet_set_coatom(output, i) # Filling each facet with its Vrep-incidences, which "is" the # "i-th column" of the original matrix (but we have transposed). for entry in range(nrows): if not matrix.get_is_zero_unsafe(entry, i): # Vrep ``entry`` is contained in the face, so set the corresponding bit - bitset_add(output, entry) + face_add_atom_safe(output, entry) return facets def incidence_matrix_to_bit_rep_of_Vrep(Matrix_integer_dense matrix): @@ -364,12 +340,11 @@ def facets_tuple_to_bit_rep_of_facets(tuple facets_input, size_t n_Vrep): (0, 3, 5) """ cdef Py_ssize_t i - cdef ListOfFaces facets = ListOfFaces(len(facets_input), n_Vrep) - cdef size_t face_length = facets.face_length - cdef uint64_t **facets_data = facets.data + cdef ListOfFaces facets = ListOfFaces(len(facets_input), n_Vrep, len(facets_input)) for i in range(len(facets_input)): # filling each facet with the data from the corresponding facet - Vrep_list_to_bit_rep(facets_input[i], facets_data[i], face_length) + Vrep_list_to_bit_rep(facets_input[i], facets.data.faces[i]) + facet_set_coatom(facets.data.faces[i], i) return facets def facets_tuple_to_bit_rep_of_Vrep(tuple facets_input, size_t n_Vrep): @@ -412,26 +387,24 @@ def facets_tuple_to_bit_rep_of_Vrep(tuple facets_input, size_t n_Vrep): # Vertices in facet-representation will be a ``ListOfFaces`` # with number of Vrep faces and # number of facets "Vrep"/atoms. - cdef ListOfFaces Vrep = ListOfFaces(n_Vrep, n_facets) - cdef uint64_t **Vrep_data = Vrep.data + cdef ListOfFaces Vrep = ListOfFaces(n_Vrep, n_facets, n_Vrep) + cdef face_t* Vrep_data = Vrep.data.faces # Initializing the data of ListOfFaces. - cdef size_t face_length = Vrep.face_length - cdef size_t i - for i in range(n_Vrep): - bitset_clear(Vrep_data[i], face_length) cdef size_t input_facet # will iterate over indices of facets_input - cdef size_t position # determines the position in output of entry - cdef size_t value # determines which bit will be set in output[position] cdef size_t input_Vrep # will iterate over vertices in facet ``input_facet`` + cdef size_t i + + for i in range(n_Vrep): + facet_set_coatom(Vrep_data[i], i) for input_facet in range(n_facets): for input_Vrep in facets_input[input_facet]: # Iff the input-Vrep is in the input-facet, # then in facet-representation of the Vrep # input-facet is a Vrep of intput-Vrep. - bitset_add(Vrep_data[input_Vrep], input_facet) + face_add_atom_safe(Vrep_data[input_Vrep], input_facet) return Vrep def _bit_rep_to_Vrep_list_wrapper(ListOfFaces faces, index=0): @@ -456,15 +429,14 @@ def _bit_rep_to_Vrep_list_wrapper(ListOfFaces faces, index=0): """ cdef MemoryAllocator mem = MemoryAllocator() cdef size_t *output - output = mem.allocarray(faces.n_atoms, + output = mem.allocarray(faces.n_atoms(), sizeof(size_t)) length = bit_rep_to_Vrep_list( - faces.data[index], output, faces.face_length) + faces.data.faces[index], output) return tuple(output[i] for i in range(length)) -cdef inline size_t bit_rep_to_Vrep_list(uint64_t *face, size_t *output, - size_t face_length) except -1: +cdef inline size_t bit_rep_to_Vrep_list(face_t face, size_t *output) except -1: r""" Convert a bitrep-representation to a list of Vrep. Return length of representation. @@ -475,8 +447,7 @@ cdef inline size_t bit_rep_to_Vrep_list(uint64_t *face, size_t *output, - ``face`` -- a Bit-representation of a face - ``output`` -- an array of ``size_t`` long enough to contain all Vrep - of that face (``face_length*64`` will suffice) - - ``face_length`` -- the length of ``face`` + of that face OUTPUT: @@ -516,10 +487,9 @@ cdef inline size_t bit_rep_to_Vrep_list(uint64_t *face, size_t *output, ....: 'as the inverse of ``Vrep_list_to_bit_rep``') """ cdef size_t output_length = 0 - cdef uint64_t copy - cdef size_t j = bitset_next(face, face_length, 0) + cdef long j = face_next_atom(face, 0) while (j != -1): output[output_length] = j output_length += 1 - j = bitset_next(face, face_length, j+1) + j = face_next_atom(face, j+1) return output_length diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd index f8809ef988b..56b062af495 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd @@ -1,13 +1,15 @@ cimport cython -from libc.stdint cimport uint64_t from sage.ext.memory_allocator cimport MemoryAllocator from sage.structure.sage_object cimport SageObject from .list_of_faces cimport ListOfFaces +from .face_data_structure cimport face_t +from .face_list_data_structure cimport face_list_t from .combinatorial_face cimport CombinatorialFace -cdef struct iter_struct: +cdef struct iter_s: bint dual # if 1, then iterate over dual Polyhedron - uint64_t *face # the current face of the iterator + face_t face # the current face of the iterator + int face_status # 0 not initialized, 1 initialized, 2 added to visited_all size_t *atom_rep # a place where atom-representaion of face will be stored size_t *coatom_rep # a place where coatom-representaion of face will be stored int current_dimension # dimension of current face, dual dimension if ``dual`` @@ -15,7 +17,6 @@ cdef struct iter_struct: int output_dimension # only faces of this (dual?) dimension are considered int lowest_dimension # don't consider faces below this (dual?) dimension size_t _index # this counts the number of seen faces, useful for hasing the faces - size_t face_length # stores length of the faces in terms of uint64_t # ``visited_all`` points to faces, of which we have visited all faces already. # The number of faces in ``visited_all` might depend on the current dimension: @@ -27,13 +28,11 @@ cdef struct iter_struct: # In this way, we will append ``visited_all`` in lower dimension, but # will ignore those changes when going up in dimension again. # This is why the number of faces in ``visited_all``depends on dimension. - uint64_t **visited_all - size_t *n_visited_all + face_list_t* visited_all - # ``newfaces`` is where the new faces are stored. + # ``new_faces`` is where the new faces are stored. # Needs to be long enought to store all possible intersections of a face with all coatoms. - uint64_t ***newfaces - size_t *n_newfaces # number of newfaces for each dimension + face_list_t* new_faces # After having visited a face completely, we want to add it to ``visited_all``. # ``first_dim[i]`` will indicate, wether there is one more face in @@ -47,20 +46,13 @@ cdef struct iter_struct: # that have not been visited yet. size_t yet_to_visit - # Some modifications that make things better for simple and simplicial polyhedra. - bint is_simple - uint64_t *face_coatom_rep - size_t face_length_coatom_rep - uint64_t **visited_all_coatom_rep - uint64_t ***newfaces_coatom_rep +ctypedef iter_s iter_t[1] cdef class FaceIterator_base(SageObject): - cdef iter_struct structure + cdef iter_t structure cdef readonly bint dual # if 1, then iterate over dual Polyhedron cdef MemoryAllocator _mem - cdef tuple newfaces_lists # tuple to hold the ListOfFaces corresponding to newfaces - cdef tuple newfaces_lists_coatom_rep # some copies from ``CombinatorialPolyhedron`` cdef tuple _Vrep, _facet_names, _equalities @@ -90,6 +82,6 @@ cdef class FaceIterator_geom(FaceIterator_base): # Nogil definitions of crucial functions. -cdef int next_dimension(iter_struct& structure) nogil except -1 -cdef int next_face_loop(iter_struct& structure) nogil except -1 -cdef size_t n_atom_rep(iter_struct& structure) nogil except -1 +cdef int next_dimension(iter_t structure) nogil except -1 +cdef int next_face_loop(iter_t structure) nogil except -1 +cdef size_t n_atom_rep(iter_t structure) nogil except -1 diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx index c91111c8b9d..6d79766950e 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx @@ -1,7 +1,3 @@ -# distutils: depends = sage/geometry/polyhedron/combinatorial_polyhedron/bitset_operations.cc -# distutils: language = c++ -# distutils: extra_compile_args = -std=c++11 - r""" Face iterator for polyhedra @@ -179,72 +175,13 @@ AUTHOR: #***************************************************************************** from sage.rings.integer cimport smallInteger -from cysignals.signals cimport sig_check, sig_on, sig_off +from cysignals.signals cimport sig_check from .conversions cimport bit_rep_to_Vrep_list from .conversions import facets_tuple_to_bit_rep_of_facets from .base cimport CombinatorialPolyhedron -from sage.geometry.polyhedron.face import combinatorial_face_to_polyhedral_face, PolyhedronFace -cdef extern from "bitset_operations.cc": - cdef size_t get_next_level( - uint64_t **faces, const size_t n_faces, - uint64_t **newfaces, uint64_t **visited_all, - size_t n_visited_all, size_t face_length) nogil -# Set ``newfaces`` to be the facets of ``faces[n_faces -1]`` -# that are not contained in a face of ``visited_all``. - -# INPUT: - -# - ``newfaces`` -- quasi of type ``uint64_t[n_faces -1][face_length]``, -# needs to be ``chunksize``-Bit aligned -# - ``visited_all`` -- quasi of type ``*uint64_t[n_visited_all] -# - ``face_length`` -- length of the faces - -# OUTPUT: - -# - return number of ``newfaces`` -# - set ``newfaces`` to point to the new faces - -# ALGORITHM: - -# To get all facets of ``faces[n_faces-1]``, we would have to: -# - Intersect the first ``n_faces-1`` faces of ``faces`` with the last face. -# - Add all the intersection of ``visited_all`` with the last face -# - Out of both the inclusion-maximal ones are of codimension 1, i.e. facets. - -# As we have visited all faces of ``visited_all``, we alter the algorithm -# to not revisit: -# Step 1: Intersect the first ``n_faces-1`` faces of ``faces`` with the last face. -# Step 2: Out of thosse the inclusion-maximal ones are some of the facets. -# At least we obtain all of those, that we have not already visited. -# Maybe, we get some more. -# Step 3: Only keep those that we have not already visited. -# We obtain exactly the facets of ``faces[n_faces-1]`` that we have -# not visited yet. - - cdef size_t get_next_level_simple( - uint64_t **faces, const size_t n_faces, - uint64_t **newfaces, uint64_t **visited_all, - size_t n_visited_all, size_t face_length, - uint64_t **faces_coatom_rep, - uint64_t **newfaces_coatom_rep, uint64_t **visited_all_coatom_rep, - size_t face_length_coatom_rep) nogil -# /* -# As above, but modified for the case where every interval not containing zero is boolean. -# */ - - cdef size_t count_atoms(uint64_t *A, size_t face_length) nogil -# Return the number of atoms/vertices in A. -# This is the number of set bits in A. -# ``face_length`` is the length of A in terms of uint64_t. - - cdef size_t bit_rep_to_coatom_rep( - uint64_t *face, uint64_t **coatoms, size_t n_coatoms, - size_t face_length, size_t *output) nogil -# Write the coatom-representation of face in output. Return length. -# ``face_length`` is the length of ``face`` and ``coatoms[i]`` -# in terms of uint64_t. -# ``n_coatoms`` length of ``coatoms``. +from sage.geometry.polyhedron.face import combinatorial_face_to_polyhedral_face, PolyhedronFace +from .face_list_data_structure cimport * cdef extern from "Python.h": int unlikely(int) nogil # Defined by Cython @@ -281,12 +218,10 @@ cdef class FaceIterator_base(SageObject): raise ValueError("cannot iterate over dual of unbounded Polyedron") cdef int i cdef size_t j - cdef ListOfFaces some_list # make Cython aware of type self.dual = dual self.structure.dual = dual - self.structure.face = NULL - self.structure.face_coatom_rep = NULL + self.structure.face_status = 0 self.structure.dimension = C.dimension() self.structure.current_dimension = self.structure.dimension -1 self._mem = MemoryAllocator() @@ -315,16 +250,15 @@ cdef class FaceIterator_base(SageObject): else: self.coatoms = C.bitrep_facets() self.atoms = C.bitrep_Vrep() - self.structure.face_length = self.coatoms.face_length self._Vrep = C.Vrep() self._facet_names = C.facet_names() self._equalities = C.equalities() self._bounded = C.is_bounded() - self.structure.atom_rep = self._mem.allocarray(self.coatoms.n_atoms, sizeof(size_t)) - self.structure.coatom_rep = self._mem.allocarray(self.coatoms.n_faces, sizeof(size_t)) + self.structure.atom_rep = self._mem.allocarray(self.coatoms.n_atoms(), sizeof(size_t)) + self.structure.coatom_rep = self._mem.allocarray(self.coatoms.n_faces(), sizeof(size_t)) - if self.structure.dimension == 0 or self.coatoms.n_faces == 0: + if self.structure.dimension == 0 or self.coatoms.n_faces() == 0: # As we will only yield proper faces, # there is nothing to yield in those cases. # We have to discontinue initialization, @@ -333,23 +267,29 @@ cdef class FaceIterator_base(SageObject): return # We may assume ``dimension > 0`` and ``n_faces > 0``. - # Initialize ``newfaces``. - self.newfaces_lists = tuple(ListOfFaces(self.coatoms.n_faces, self.coatoms.n_atoms) - for i in range(self.structure.dimension -1)) - self.structure.newfaces = self._mem.allocarray((self.structure.dimension), sizeof(uint64_t **)) + # Initialize ``new_faces``. + self.structure.new_faces = self._mem.allocarray((self.structure.dimension), sizeof(face_list_t)) for i in range(self.structure.dimension-1): - some_list = self.newfaces_lists[i] - self.structure.newfaces[i] = some_list.data + face_list_init(self.structure.new_faces[i], + self.coatoms.n_faces(), self.coatoms.n_atoms(), + self.coatoms.n_coatoms(), self._mem) + + # We start with the coatoms + face_list_shallow_init(self.structure.new_faces[self.structure.dimension-1], + self.coatoms.n_faces(), self.coatoms.n_atoms(), + self.coatoms.n_coatoms(), self._mem) + + + face_list_shallow_copy(self.structure.new_faces[self.structure.dimension-1], self.coatoms.data) - # We start with the coatoms. - self.structure.newfaces[self.structure.dimension - 1] = self._mem.allocarray(self.coatoms.n_faces, sizeof(uint64_t*)) - for j in range(self.coatoms.n_faces): - self.structure.newfaces[self.structure.dimension - 1][j] = self.coatoms.data[j] # Initialize ``visited_all``. - self.structure.visited_all = self._mem.allocarray(self.coatoms.n_faces, sizeof(uint64_t *)) - self.structure.n_visited_all = self._mem.allocarray(self.structure.dimension, sizeof(size_t)) - self.structure.n_visited_all[self.structure.dimension -1] = 0 + self.structure.visited_all = self._mem.allocarray((self.structure.dimension), sizeof(face_list_t)) + face_list_shallow_init(self.structure.visited_all[self.structure.dimension-1], + self.coatoms.n_faces(), self.coatoms.n_atoms(), + self.coatoms.n_coatoms(), self._mem) + self.structure.visited_all[self.structure.dimension-1].n_faces = 0 + if not C.is_bounded(): # Treating the far face as if we had visited all its elements. # Hence we will visit all intersections of facets unless contained in the far face. @@ -359,48 +299,22 @@ cdef class FaceIterator_base(SageObject): # needs to be at most ``n_facets - 1``. # Hence it is fine to use the first entry already for the far face, # as ``self.visited_all`` holds ``n_facets`` pointers. - some_list = C.far_face() - self.structure.visited_all[0] = some_list.data[0] - self.structure.n_visited_all[self.structure.dimension -1] = 1 - - # Initialize ``n_newfaces``. - self.structure.n_newfaces = self._mem.allocarray(self.structure.dimension, sizeof(size_t)) - self.structure.n_newfaces[self.structure.dimension - 1] = self.coatoms.n_faces + add_face_shallow(self.structure.visited_all[self.structure.dimension-1], C._far_face) # Initialize ``first_time``. self.structure.first_time = self._mem.allocarray(self.structure.dimension, sizeof(bint)) self.structure.first_time[self.structure.dimension - 1] = True - self.structure.yet_to_visit = self.coatoms.n_faces + self.structure.yet_to_visit = self.coatoms.n_faces() self.structure._index = 0 if C.is_bounded() and ((dual and C.is_simplicial()) or (not dual and C.is_simple())): # We are in the comfortable situation that for our iterator # all intervals not containing the 0 element are boolean. # This makes things a lot easier. - self.structure.is_simple = True - - self.structure.face_length_coatom_rep = self.atoms.face_length - - # Initializing the facets in their Bit-representation. - self.coatoms_coatom_rep = facets_tuple_to_bit_rep_of_facets(tuple((i,) for i in range(self.coatoms.n_faces)), self.coatoms.n_faces) - - # Initialize ``newfaces``, - # the place where the new faces are being stored. - self.newfaces_lists_coatom_rep = tuple(ListOfFaces(self.coatoms.n_faces, self.atoms.n_atoms) - for i in range(self.structure.dimension -1)) - self.structure.newfaces_coatom_rep = self._mem.allocarray((self.structure.dimension), sizeof(uint64_t **)) - for i in range(self.structure.dimension -1): - some_list = self.newfaces_lists_coatom_rep[i] - self.structure.newfaces_coatom_rep[i] = some_list.data - self.structure.newfaces_coatom_rep[self.structure.dimension - 1] = self.coatoms_coatom_rep.data # we start with coatoms - - # Initialize ``visited_all``. - self.structure.visited_all_coatom_rep = self._mem.allocarray(self.coatoms.n_faces, sizeof(uint64_t *)) - - # Note that C is not bounded. + self.structure.new_faces[self.structure.dimension -1].polyhedron_is_simple = True else: - self.structure.is_simple = False + self.structure.new_faces[self.structure.dimension -1].polyhedron_is_simple = False def reset(self): r""" @@ -419,7 +333,7 @@ cdef class FaceIterator_base(SageObject): sage: next(it).ambient_V_indices() (0, 3, 4, 5) """ - if self.structure.dimension == 0 or self.coatoms.n_faces == 0: + if self.structure.dimension == 0 or self.coatoms.n_faces() == 0: # As we will only yield proper faces, # there is nothing to yield in those cases. # We have to discontinue initialization, @@ -427,15 +341,15 @@ cdef class FaceIterator_base(SageObject): self.structure.current_dimension = self.structure.dimension return if self._bounded: - self.structure.n_visited_all[self.structure.dimension -1] = 0 + self.structure.visited_all[self.structure.dimension -1].n_faces = 0 else: - self.structure.n_visited_all[self.structure.dimension -1] = 1 - self.structure.face = NULL - self.structure.n_newfaces[self.structure.dimension - 1] = self.coatoms.n_faces + self.structure.visited_all[self.structure.dimension -1].n_faces = 1 + self.structure.face_status = 0 + self.structure.new_faces[self.structure.dimension - 1].n_faces = self.coatoms.n_faces() self.structure.current_dimension = self.structure.dimension - 1 self.structure.first_time[self.structure.dimension - 1] = True - self.structure.yet_to_visit = self.coatoms.n_faces + self.structure.yet_to_visit = self.coatoms.n_faces() self.structure._index = 0 def __next__(self): @@ -472,7 +386,7 @@ cdef class FaceIterator_base(SageObject): sage: next(it).ambient_V_indices() == it.current().ambient_V_indices() True """ - if unlikely(self.structure.face is NULL): + if unlikely(self.structure.face_status == 0): raise ValueError("iterator not set to a face yet") return CombinatorialFace(self) @@ -577,16 +491,18 @@ cdef class FaceIterator_base(SageObject): See :meth:`FaceIterator_base.ignore_subfaces` and :meth:`FaceIterator_base.ignore_supfaces`. """ - if unlikely(self.structure.face is NULL): + if unlikely(self.structure.face_status == 0): raise ValueError("iterator not set to a face yet") + if unlikely(self.structure.face_status == 2): + # Nothing to do. + return 0 # The current face is added to ``visited_all``. # This will make the iterator skip those faces. # Also, this face will not be added a second time to ``visited_all``, # as there are no new faces. - self.structure.visited_all[self.structure.n_visited_all[self.structure.current_dimension]] = self.structure.face - if self.structure.is_simple: - self.structure.visited_all_coatom_rep[self.structure.n_visited_all[self.structure.current_dimension]] = self.structure.face_coatom_rep - self.structure.n_visited_all[self.structure.current_dimension] += 1 + + add_face_shallow(self.structure.visited_all[self.structure.current_dimension], self.structure.face) + self.structure.face_status = 2 cdef inline CombinatorialFace next_face(self): r""" @@ -637,11 +553,6 @@ cdef class FaceIterator_base(SageObject): This is a shortcut of :class:`sage.geometry.polyhedron.combinatorial_polyhedron.combinatorial_face.CombinatorialFace.n_atom_rep` """ return n_atom_rep(self.structure) - if self.structure.face: - return count_atoms(self.structure.face, self.structure.face_length) - - # The face was not initialized properly. - raise LookupError("face iterator does not point to a face") cdef size_t set_coatom_rep(self) except -1: r""" @@ -650,11 +561,7 @@ cdef class FaceIterator_base(SageObject): This is a shortcut of :class:`sage.geometry.polyhedron.combinatorial_polyhedron.combinatorial_face.CombinatorialFace.set_coatom_rep` """ - cdef size_t n_coatoms = self.coatoms.n_faces - cdef uint64_t **coatoms = self.coatoms.data - cdef size_t face_length = self.structure.face_length - return bit_rep_to_coatom_rep(self.structure.face, coatoms, n_coatoms, - face_length, self.structure.coatom_rep) + return bit_rep_to_coatom_rep(self.structure.face, self.coatoms.data, self.structure.coatom_rep) cdef size_t set_atom_rep(self) except -1: r""" @@ -663,8 +570,7 @@ cdef class FaceIterator_base(SageObject): This is a shortcut of :class:`sage.geometry.polyhedron.combinatorial_polyhedron.combinatorial_face.CombinatorialFace.set_atom_rep` """ - cdef size_t face_length = self.structure.face_length - return bit_rep_to_Vrep_list(self.structure.face, self.structure.atom_rep, face_length) + return bit_rep_to_Vrep_list(self.structure.face, self.structure.atom_rep) cdef class FaceIterator(FaceIterator_base): r""" @@ -848,9 +754,9 @@ cdef class FaceIterator(FaceIterator_base): while facets: one_face = faces.pop() - newfaces = [one_face.intersection(face) for face in faces] + new_faces = [one_face.intersection(face) for face in faces] - # ``maybe_newfaces`` contains all facets of ``one_face``, + # ``maybe_new_faces`` contains all facets of ``one_face``, # which we have not visited before. # Proof: Let `F` be a facet of ``one_face``. # We have a chain: @@ -865,31 +771,31 @@ cdef class FaceIterator(FaceIterator_base): # Then, intersecting ``one_face`` with ``second_face`` gives # ``F``. ∎ - # If an element in ``maybe_newfaces`` is inclusion maximal + # If an element in ``maybe_new_faces`` is inclusion maximal # and not contained in any of the ``visited_all``, # it is a facet of ``one_face``. - # Any facet in ``maybe_newfaces`` of ``one_face`` + # Any facet in ``maybe_new_faces`` of ``one_face`` # is inclusion maximal. - maybe_newfaces2 = [] - for face1 in maybe_newfaces: + maybe_new_faces2 = [] + for face1 in maybe_new_faces: # ``face1`` is a facet of ``one_face``, # iff it is not contained in another facet. - if all(not face1 < face2 for face2 in maybe_newfaces): - maybe_newfaces2.append(face1) + if all(not face1 < face2 for face2 in maybe_new_faces): + maybe_new_faces2.append(face1) - # ``maybe_newfaces2`` contains only facets of ``one_face`` + # ``maybe_new_faces2`` contains only facets of ``one_face`` # and some faces contained in any of ``visited_all``. # It also contains all the facets not contained in any of ``visited_all``. - # Let ``newfaces`` be the list of all facets of ``one_face`` + # Let ``new_faces`` be the list of all facets of ``one_face`` # not contained in any of ``visited_all``. - newfaces = [] - for face1 in maybe_newfaces2: + new_faces = [] + for face1 in maybe_new_faces2: if all(not face1 < face2 for face2 in visited_all): - newfaces.append(face1) + new_faces.append(face1) # By induction we can apply the algorithm, to visit all # faces of ``one_face`` not contained in ``visited_all``: - face_iterator(newfaces, visited_all) + face_iterator(new_faces, visited_all) # Finally visit ``one_face`` and add it to ``visited_all``: visit(one_face) @@ -1281,17 +1187,18 @@ cdef class FaceIterator_geom(FaceIterator_base): # Nogil definitions of crucial functions. -cdef inline int next_dimension(iter_struct& structure) nogil except -1: +cdef inline int next_dimension(iter_t structure) nogil except -1: r""" See :meth:`FaceIterator.next_dimension`. """ cdef int dim = structure.dimension + structure.face_status = 0 while (not next_face_loop(structure)) and (structure.current_dimension < dim): sig_check() structure._index += 1 return structure.current_dimension -cdef inline int next_face_loop(iter_struct& structure) nogil except -1: +cdef inline int next_face_loop(iter_t structure) nogil except -1: r""" See :meth:`FaceIterator.next_face_loop`. """ @@ -1301,12 +1208,10 @@ cdef inline int next_face_loop(iter_struct& structure) nogil except -1: raise StopIteration # Getting ``[faces, n_faces, n_visited_all]`` according to dimension. - cdef uint64_t **faces = structure.newfaces[structure.current_dimension] - cdef size_t n_faces = structure.n_newfaces[structure.current_dimension] - cdef size_t n_visited_all = structure.n_visited_all[structure.current_dimension] - cdef uint64_t **faces_coatom_rep - if structure.is_simple: - faces_coatom_rep = structure.newfaces_coatom_rep[structure.current_dimension] + cdef face_list_t* faces = &structure.new_faces[structure.current_dimension] + cdef face_list_t* new_faces = &structure.new_faces[structure.current_dimension-1] + cdef face_list_t* visited_all = &structure.visited_all[structure.current_dimension] + cdef size_t n_faces = faces[0].n_faces if (structure.output_dimension > -2) and (structure.output_dimension != structure.current_dimension): # If only a specific dimension was requested (i.e. ``output_dimension > -2``), @@ -1316,9 +1221,8 @@ cdef inline int next_face_loop(iter_struct& structure) nogil except -1: if structure.yet_to_visit: # Set ``face`` to the next face. structure.yet_to_visit -= 1 - structure.face = faces[structure.yet_to_visit] - if structure.is_simple: - structure.face_coatom_rep = faces_coatom_rep[structure.yet_to_visit] + structure.face[0] = faces[0].faces[structure.yet_to_visit][0] + structure.face_status = 1 return 1 if structure.current_dimension <= structure.lowest_dimension: @@ -1332,74 +1236,52 @@ cdef inline int next_face_loop(iter_struct& structure) nogil except -1: structure.current_dimension += 1 return 0 - # We will visit the last face now. - structure.n_newfaces[structure.current_dimension] -= 1 - n_faces -= 1 - if not structure.first_time[structure.current_dimension]: - # In this case there exists ``faces[n_faces + 1]``, of which we + # In this case there exists ``faces[0].faces[n_faces]``, of which we # have visited all faces, but which was not added to # ``visited_all`` yet. - structure.visited_all[n_visited_all] = faces[n_faces + 1] - if structure.is_simple: - structure.visited_all_coatom_rep[n_visited_all] = faces_coatom_rep[n_faces + 1] - structure.n_visited_all[structure.current_dimension] += 1 - n_visited_all = structure.n_visited_all[structure.current_dimension] + add_face_shallow(visited_all[0], faces[0].faces[n_faces]) else: # Once we have visited all faces of ``faces[n_faces]``, we want # to add it to ``visited_all``. structure.first_time[structure.current_dimension] = False - # Get the faces of codimension 1 contained in ``faces[n_faces]``, + # We will visit the last face now. + + # Get the faces of codimension 1 contained in ``faces[n_faces-1]``, # which we have not yet visited. - cdef size_t newfacescounter - - if structure.is_simple: - sig_on() - newfacescounter = get_next_level_simple( - faces, n_faces + 1, - structure.newfaces[structure.current_dimension-1], - structure.visited_all, n_visited_all, structure.face_length, - faces_coatom_rep, - structure.newfaces_coatom_rep[structure.current_dimension-1], - structure.visited_all_coatom_rep, structure.face_length_coatom_rep) - sig_off() - else: - sig_on() - newfacescounter = get_next_level( - faces, n_faces + 1, - structure.newfaces[structure.current_dimension-1], - structure.visited_all, n_visited_all, structure.face_length) - sig_off() - - if newfacescounter: + cdef size_t new_faces_counter + + new_faces_counter = get_next_level( + faces[0], new_faces[0], visited_all[0]) + + if new_faces_counter: # ``faces[n_faces]`` contains new faces. # We will visted them on next call, starting with codimension 1. # Setting the variables correclty for next call of ``next_face_loop``. structure.current_dimension -= 1 structure.first_time[structure.current_dimension] = True - structure.n_newfaces[structure.current_dimension] = newfacescounter - structure.n_visited_all[structure.current_dimension] = n_visited_all - structure.yet_to_visit = newfacescounter + structure.visited_all[structure.current_dimension][0] = visited_all[0][0] + structure.yet_to_visit = new_faces_counter return 0 else: - # ``faces[n_faces]`` contains no new faces. + # ``faces.faces[n_faces-1]`` contains no new faces. # Hence there is no need to add it to ``visited_all``. # NOTE: # For the methods ``ignore_subfaces`` and ``ignore_supfaces`` - # this step needs to be done, as ``faces[n_faces]`` might + # this step needs to be done, as ``faces.faces[n_faces-1]`` might # have been added manually to ``visited_all``. # So this step is required to respect boundaries of ``visited_all``. structure.first_time[structure.current_dimension] = True return 0 -cdef inline size_t n_atom_rep(iter_struct& structure) nogil except -1: +cdef inline size_t n_atom_rep(iter_t structure) nogil except -1: r""" See meth:`FaceIterator.n_atom_rep`. """ - if structure.face: - return count_atoms(structure.face, structure.face_length) + if structure.face_status: + return face_len_atoms(structure.face) # The face was not initialized properly. raise LookupError("``FaceIterator`` does not point to a face") diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_list_data_structure.pxd b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_list_data_structure.pxd index 8ec5a0b84ad..624cbc8b444 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_list_data_structure.pxd +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_list_data_structure.pxd @@ -11,7 +11,6 @@ Inline cython methods for lists of faces. # https://www.gnu.org/licenses/ # **************************************************************************** -from sage.geometry.polyhedron.combinatorial_polyhedron.list_of_faces cimport * from .face_data_structure cimport * from libc.string cimport memset from cysignals.signals cimport sig_on, sig_off diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.pxd b/src/sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.pxd index d938d07ddc7..a571b8c1c84 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.pxd +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.pxd @@ -1,18 +1,22 @@ cimport cython -from libc.stdint cimport uint64_t from sage.ext.memory_allocator cimport MemoryAllocator +from .face_list_data_structure cimport face_list_t @cython.final cdef class ListOfFaces: cdef MemoryAllocator _mem - # ``face_length`` is the length of each face in terms of ``uint64_t``. - cdef readonly size_t n_faces, face_length, n_atoms - # ``data`` points to the raw data. # It will be of "type" ``uint64_t[n_faces][face_length]`` - cdef uint64_t **data + cdef face_list_t data cpdef int compute_dimension(self) except -2 + cdef inline size_t n_faces(self): + return self.data.n_faces + cdef inline size_t n_atoms(self): + return self.data.n_atoms + cdef inline size_t n_coatoms(self): + return self.data.n_coatoms + cpdef ListOfFaces pyramid(self) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.pyx index a15b9704a6d..80fde681638 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.pyx @@ -1,8 +1,3 @@ -# distutils: depends = sage/geometry/polyhedron/combinatorial_polyhedron/bitset_operations.cc sage/geometry/polyhedron/combinatorial_polyhedron/bitsets.cc -# distutils: language = c++ -# distutils: extra_compile_args = -std=c++11 -# distutils: libraries = gmp - r""" List of faces @@ -28,7 +23,9 @@ Provide enough space to store `20` faces as incidences to `60` vertices:: sage: from sage.geometry.polyhedron.combinatorial_polyhedron.list_of_faces \ ....: import ListOfFaces - sage: face_list = ListOfFaces(20, 60) + sage: face_list = ListOfFaces(20, 60, 20) + sage: face_list.matrix().is_zero() + True Obtain the facets of a polyhedron:: @@ -95,60 +92,9 @@ AUTHOR: from sage.structure.element import is_Matrix -from cysignals.signals cimport sig_on, sig_off from sage.matrix.matrix_integer_dense cimport Matrix_integer_dense -cdef extern from "bitset_operations.cc": - # Any Bit-representation is assumed to be `chunksize`-Bit aligned. - cdef const size_t chunksize - - cdef size_t get_next_level( - uint64_t **faces, const size_t n_faces, uint64_t **newfaces, - uint64_t **visited_all, size_t n_visited_all, size_t face_length) -# Set ``newfaces`` to be the facets of ``faces[n_faces -1]`` -# that are not contained in a face of ``visited_all``. - -# INPUT: - -# - ``newfaces`` -- quasi of type ``uint64_t[n_faces -1][face_length]``, -# needs to be ``chunksize``-Bit aligned -# - ``visited_all`` -- quasi of type ``*uint64_t[n_visited_all] -# - ``face_length`` -- length of the faces - -# OUTPUT: - -# - return number of ``newfaces`` -# - set ``newfaces`` to point to the new faces - -# ALGORITHM: - -# To get all facets of ``faces[n_faces-1]``, we would have to: -# - Intersect the first ``n_faces-1`` faces of ``faces`` with the last face. -# - Add all the intersection of ``visited_all`` with the last face -# - Out of both the inclusion-maximal ones are of codimension 1, i.e. facets. - -# As we have visited all faces of ``visited_all``, we alter the algorithm -# to not revisit: -# Step 1: Intersect the first ``n_faces-1`` faces of ``faces`` with the last face. -# Step 2: Out of thosse the inclusion-maximal ones are some of the facets. -# At least we obtain all of those, that we have not already visited. -# Maybe, we get some more. -# Step 3: Only keep those that we have not already visited. -# We obtain exactly the facets of ``faces[n_faces-1]`` that we have -# not visited yet. - - cdef size_t count_atoms(uint64_t *A, size_t face_length) -# Return the number of atoms/vertices in A. -# This is the number of set bits in A. -# ``face_length`` is the length of A in terms of uint64_t. - -cdef extern from "bitsets.cc": - cdef void bitset_add(uint64_t* bits, size_t n) - cdef int bitset_in(uint64_t* bits, size_t n) - cdef void bitset_copy(uint64_t* dst, uint64_t* src, size_t face_length) - cdef void bitset_copy_flex "bitset_copy" (uint64_t* dst, uint64_t* src, size_t face_length_dst, size_t face_length_src) - cdef void bitset_clear(uint64_t* bits, size_t face_length) - cdef void bitset_set_first_n(uint64_t* bits, size_t face_length, size_t n) +from .face_list_data_structure cimport * cdef extern from "Python.h": int unlikely(int) nogil # Defined by Cython @@ -163,6 +109,7 @@ cdef class ListOfFaces: - ``n_faces`` -- the number of faces to be stored - ``n_atoms`` -- the total number of atoms the faces contain + - ``n_coatoms`` -- the total number of coatoms of the polyhedron .. SEEALSO:: @@ -177,15 +124,11 @@ cdef class ListOfFaces: sage: from sage.geometry.polyhedron.combinatorial_polyhedron.list_of_faces \ ....: import ListOfFaces - sage: facets = ListOfFaces(5, 13) - sage: facets.face_length in (1, 2, 4) - True - sage: facets.n_atoms - 13 - sage: facets.n_faces - 5 + sage: facets = ListOfFaces(5, 13, 5) + sage: facets.matrix().dimensions() + (5, 13) """ - def __init__(self, size_t n_faces, size_t n_atoms): + def __init__(self, size_t n_faces, size_t n_atoms, size_t n_coatoms): r""" Initialize :class:`ListOfFaces`. @@ -195,27 +138,8 @@ cdef class ListOfFaces: sage: TestSuite(sage.geometry.polyhedron.combinatorial_polyhedron.list_of_faces.ListOfFaces).run() """ - self.n_faces = n_faces - self.n_atoms = n_atoms self._mem = MemoryAllocator() - - # ``data`` will point to the faces as ``*uint64_t``. - self.data = self._mem.allocarray(n_faces, sizeof(uint64_t *)) - - # ``face_length`` is the length in terms of ``uint64_t`` - # NOTE: This needs to be divisible by 2, if chunksize is 128 - # and divisible by 4, if chunksize is 256. - self.face_length = ((n_atoms - 1)//chunksize + 1)*chunksize//64 - - - cdef size_t i - for i in range(n_faces): - # Allocate the memory for the i-th face. - # We must allocate the memory for ListOfFaces overaligned: - # - must be 16-byte aligned if chunksize = 128 - # - must be 32-byte aligned if chunksize = 256 - self.data[i] = \ - self._mem.aligned_malloc(chunksize//8, self.face_length*8) + face_list_init(self.data, n_faces, n_atoms, n_coatoms, self._mem) def _test_alignment(self): r""" @@ -225,17 +149,10 @@ cdef class ListOfFaces: sage: from sage.geometry.polyhedron.combinatorial_polyhedron.list_of_faces \ ....: import ListOfFaces - sage: facets = ListOfFaces(10, 13) + sage: facets = ListOfFaces(10, 13, 10) sage: facets._test_alignment() """ - cdef size_t address - cdef size_t required_alignment - cdef size_t i - - required_alignment = chunksize/8 - for i in range(self.n_faces): - address = self.data[i] - assert address == address & ~(required_alignment - 1) + assert face_list_check_alignment(self.data) def __copy__(self): r""" @@ -245,11 +162,9 @@ cdef class ListOfFaces: sage: from sage.geometry.polyhedron.combinatorial_polyhedron.list_of_faces \ ....: import ListOfFaces - sage: facets = ListOfFaces(5, 13) - sage: copy(facets).n_atoms - 13 - sage: copy(facets).n_faces - 5 + sage: facets = ListOfFaces(5, 13, 5) + sage: copy(facets).matrix().dimensions() + (5, 13) TESTS:: @@ -267,10 +182,8 @@ cdef class ListOfFaces: sage: copy(facets) is facets False """ - cdef ListOfFaces copy = ListOfFaces(self.n_faces, self.n_atoms) - cdef size_t i - for i in range(self.n_faces): - bitset_copy(copy.data[i], self.data[i], self.face_length) + cdef ListOfFaces copy = ListOfFaces(self.n_faces(), self.n_atoms(), self.n_coatoms()) + face_list_copy(copy.data, self.data) return copy cpdef int compute_dimension(self) except -2: @@ -336,37 +249,35 @@ cdef class ListOfFaces: ....: if not d1 == d2 == d3: ....: print('calculation_dimension() seems to be incorrect') """ - if self.n_faces == 0: + if self.n_faces() == 0: raise TypeError("at least one face needed") - cdef size_t n_faces = self.n_faces - cdef size_t face_length = self.face_length - cdef uint64_t **faces = self.data + cdef size_t n_faces = self.n_faces() + + cdef face_list_t empty_forbidden + empty_forbidden.n_faces = 0 if n_faces == 1: # We expect the face to be the empty polyhedron. # Possibly it contains more than one vertex/rays/lines. # The dimension of a polyhedron with this face as only facet is # the number of atoms it contains. - return count_atoms(faces[0], face_length) + return face_len_atoms(self.data.faces[0]) # ``newfaces`` are all intersection of ``faces[n_faces -1]`` with previous faces. # It needs to be allocated to store those faces. - cdef ListOfFaces newfaces = ListOfFaces(n_faces, face_length*64) + cdef ListOfFaces new_faces = ListOfFaces(self.n_faces(), self.n_atoms(), self.n_coatoms()) # Calculating ``newfaces`` # such that ``newfaces`` points to all facets of ``faces[n_faces -1]``. - cdef size_t new_n_faces - sig_on() - new_n_faces = get_next_level(faces, n_faces, - newfaces.data, NULL, 0, face_length) - sig_off() + cdef size_t new_n_faces = get_next_level(self.data, new_faces.data, empty_forbidden) - newfaces.n_faces = new_n_faces + # Undo what ``get_next_level`` does. + self.data.n_faces += 1 # compute the dimension of the polyhedron, # by calculating dimension of one of its faces. - return newfaces.compute_dimension() + 1 + return new_faces.compute_dimension() + 1 cpdef ListOfFaces pyramid(self): r""" @@ -424,19 +335,25 @@ cdef class ListOfFaces: """ cdef ListOfFaces copy cdef size_t i + cdef size_t n_faces = self.n_faces() + cdef size_t n_atoms = self.n_atoms() # ``copy`` has a new atom and a new coatom. - copy = ListOfFaces(self.n_faces + 1, self.n_atoms + 1) + copy = ListOfFaces(n_faces + 1, n_atoms + 1, n_faces + 1) - for i in range(self.n_faces): + # Note that a pyramid is simple if and only if the the base is simple. - # All old coatoms contain their respective old atoms. - # Also all of them contain the new atom. - bitset_copy_flex(copy.data[i], self.data[i], copy.face_length, self.face_length) - bitset_add(copy.data[i], self.n_atoms) + face_list_copy(copy.data, self.data) + + for i in range(n_faces): + face_add_atom(copy.data.faces[i], n_atoms) + facet_set_coatom(copy.data.faces[i], i) + + copy.data.n_faces += 1 # The new coatom contains all atoms, but the new atom. - bitset_set_first_n(copy.data[self.n_faces], copy.face_length, self.n_atoms) + face_set_first_n_atoms(copy.data.faces[n_faces], n_atoms) + facet_set_coatom(copy.data.faces[n_faces], n_faces) return copy @@ -469,13 +386,15 @@ cdef class ListOfFaces: from sage.rings.all import ZZ from sage.matrix.constructor import matrix cdef Matrix_integer_dense M = matrix( - ZZ, self.n_faces, self.n_atoms, 0) + ZZ, self.n_faces(), self.n_atoms(), 0) - cdef size_t i,j - for i in range(self.n_faces): - for j in range(self.n_atoms): - if bitset_in(self.data[i], j): - M.set_unsafe_si(i, j, 1) + cdef size_t i + cdef long j + for i in range(self.n_faces()): + j = face_next_atom(self.data.faces[i], 0) + while j != -1: + M.set_unsafe_si(i, j, 1) + j = face_next_atom(self.data.faces[i], j+1) M.set_immutable() return M diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.pxd b/src/sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.pxd index 250fa8328ad..951fd46db41 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.pxd +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.pxd @@ -1,7 +1,8 @@ cimport cython -from libc.stdint cimport uint64_t from sage.ext.memory_allocator cimport MemoryAllocator from .list_of_faces cimport ListOfFaces +from .face_data_structure cimport face_t +from .face_list_data_structure cimport face_list_t from .combinatorial_face cimport CombinatorialFace @cython.final @@ -9,9 +10,7 @@ cdef class PolyhedronFaceLattice: cdef MemoryAllocator _mem cdef int dimension # dimension of Polyhedron cdef readonly bint dual # if True, then List of all faces by dual Polyhedron - cdef size_t face_length # stores length of the faces in terms of uint64_t cdef size_t *f_vector # a copy of the f-vector, is reversed if dual - cdef size_t *face_counter # how many faces of each dimension have been initialized cdef size_t *atom_rep # a place where atom-representation of face will be stored cdef size_t *coatom_rep # a place where coatom-representation of face will be stored @@ -24,8 +23,7 @@ cdef class PolyhedronFaceLattice: # All faces are stored in ``faces``. ``faces[i]`` stores all faces of # dimension `i` in Bit-representation (of atoms). - cdef uint64_t *** faces - cdef tuple faces_mem # tuple to hold the ListOfFaces corresponding to faces + cdef face_list_t *faces # It follows a number of attributes to iterate over all incidences. # After initialization, ``PolyhedronFaceLattice`` can iterate over all incidences @@ -39,18 +37,10 @@ cdef class PolyhedronFaceLattice: # Intersection of ``faces[incidence_dim_one][incidence_counter_one]`` with # ``coatoms[incidence_counter_two]``. # If this is contained in ``faces[incidence_dim_two]``, there is an incidence. - cdef uint64_t *incidence_face + cdef face_t incidence_face - cdef int _add_face(self, int face_dim, uint64_t *face) except -1 cdef int _sort(self) except -1 - cdef int _sort_one_list(self, uint64_t **faces, size_t n_faces) except -1 - cdef int _sort_one_list_loop( - self, uint64_t **inp, uint64_t **output1, - uint64_t **output2, size_t n_faces) except -1 - cdef inline size_t find_face(self, int dimension, uint64_t *face) except -1 - cdef inline bint is_smaller(self, uint64_t *one, uint64_t *two) - cdef inline int is_equal(self, int dimension, size_t index, - uint64_t *face) except -1 + cdef inline size_t find_face(self, int dimension, face_t face) except -2 cpdef CombinatorialFace get_face(self, int dimension, size_t index) cdef size_t set_coatom_rep(self, int dimension, size_t index) except -1 cdef size_t set_atom_rep(self, int dimension, size_t index) except -1 diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.pyx index ae1d1f42f3b..ea4d11db09c 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.pyx @@ -1,8 +1,3 @@ -# distutils: depends = sage/geometry/polyhedron/combinatorial_polyhedron/bitset_operations.cc sage/geometry/polyhedron/combinatorial_polyhedron/bitsets.cc -# distutils: language = c++ -# distutils: extra_compile_args = -std=c++11 -# distutils: libraries = gmp - r""" PolyhedronFaceLattice @@ -68,28 +63,13 @@ from .conversions \ import facets_tuple_to_bit_rep_of_facets, \ facets_tuple_to_bit_rep_of_Vrep -from sage.rings.integer cimport smallInteger -from .conversions cimport Vrep_list_to_bit_rep, bit_rep_to_Vrep_list -from .base cimport CombinatorialPolyhedron -from .face_iterator cimport FaceIterator - -cdef extern from "bitset_operations.cc": - cdef void intersection(uint64_t *dest, uint64_t *A, uint64_t *B, - size_t face_length) -# Set ``dest = A & B``, i.e. dest is the intersection of A and B. -# ``face_length`` is the length of A, B and dest in terms of uint64_t. - - cdef size_t bit_rep_to_coatom_rep( - uint64_t *face, uint64_t **coatoms, size_t n_coatoms, - size_t face_length, size_t *output) -# Write the coatom-representation of face in output. Return length. -# ``face_length`` is the length of ``face`` and ``coatoms[i]`` -# in terms of uint64_t. -# ``n_coatoms`` length of ``coatoms``. - -cdef extern from "bitsets.cc": - cdef int bitset_cmp(uint64_t* a, uint64_t* b, size_t face_length) - cdef void bitset_copy(uint64_t* dst, uint64_t* src, size_t face_length) +from .conversions cimport bit_rep_to_Vrep_list + +from sage.rings.integer cimport smallInteger +from .base cimport CombinatorialPolyhedron +from .face_iterator cimport FaceIterator +from .face_list_data_structure cimport * + cdef extern from "Python.h": int unlikely(int) nogil # Defined by Cython @@ -146,15 +126,16 @@ cdef class PolyhedronFaceLattice: sage: TestSuite(sage.geometry.polyhedron.combinatorial_polyhedron.polyhedron_face_lattice.PolyhedronFaceLattice).run() """ + cdef int i + cdef size_t j self._mem = MemoryAllocator() self.dimension = C.dimension() self.dual = False - if C.bitrep_facets().n_faces > C.bitrep_Vrep().n_faces: + if C.bitrep_facets().n_faces() > C.bitrep_Vrep().n_faces(): self.dual = True if not C.is_bounded(): self.dual = False cdef FaceIterator face_iter = C._face_iter(self.dual, -2) - self.face_length = face_iter.structure.face_length self._Vrep = C.Vrep() self._facet_names = C.facet_names() self._equalities = C.equalities() @@ -169,60 +150,50 @@ cdef class PolyhedronFaceLattice: for i in range(-1, self.dimension + 1): self.f_vector[i+1] = f_vector[i+1] - # face_counter keeps track, if all faces have been added already - self.face_counter = self._mem.calloc(self.dimension + 2, sizeof(size_t)) - self.face_counter[0] = 1 - self.face_counter[self.dimension + 1] = 1 - if self.dimension > -1: - # We will obtain the coatoms from ``CombinatorialPolyhedron``. - self.face_counter[self.dimension] = self.f_vector[self.dimension] - # Initialize atoms, coatoms, ``atom_rep`` and ``coatom_rep``. if self.dimension == 0: # In case of the 0-dimensional polyhedron, we have to fix atoms and coatoms. # So far this didn't matter, as we only iterated over proper faces. self.atoms = facets_tuple_to_bit_rep_of_Vrep(((),), 1) self.coatoms = facets_tuple_to_bit_rep_of_facets(((),), 1) - self.face_length = self.coatoms.face_length else: self.atoms = face_iter.atoms self.coatoms = face_iter.coatoms - cdef size_t n_atoms = self.atoms.n_faces - self.atom_rep = self._mem.allocarray(self.coatoms.n_atoms, sizeof(size_t)) - self.coatom_rep = self._mem.allocarray(self.coatoms.n_faces, sizeof(size_t)) - - # Initialize the data for ``faces``: - cdef ListOfFaces coatoms_mem - self.faces_mem = tuple(ListOfFaces(self.f_vector[i+1], n_atoms) - for i in range(-1, self.dimension-1)) - if self.dimension > -1: - # the coatoms - self.faces_mem += (self.coatoms,) - self.faces_mem += (ListOfFaces(1, n_atoms),) # the full polyhedron + + cdef size_t n_atoms = self.atoms.n_faces() + self.atom_rep = self._mem.allocarray(self.coatoms.n_atoms(), sizeof(size_t)) + self.coatom_rep = self._mem.allocarray(self.coatoms.n_faces(), sizeof(size_t)) # Setting up a pointer to raw data of ``faces``: - self.faces = self._mem.allocarray(self.dimension + 2, sizeof(uint64_t **)) - cdef ListOfFaces some_list # assuming a type + self.faces = self._mem.allocarray(self.dimension + 2, sizeof(face_list_t)) for i in range(self.dimension + 2): - some_list = self.faces_mem[i] - self.faces[i] = some_list.data - - if self.dimension != 0: - # Initialize the empty face. - # In case ``dimension == 0``, we would overwrite the coatoms. - Vrep_list_to_bit_rep((), self.faces[0][0], self.face_length) - # Initialize the full polyhedron - Vrep_list_to_bit_rep(tuple(j for j in range(n_atoms)), - self.faces[self.dimension + 1][0], - self.face_length) + if i == self.dimension and self.dimension > 0: + face_list_shallow_init(self.faces[i], + self.f_vector[i], self.coatoms.n_atoms(), + self.coatoms.n_coatoms(), self._mem) + else: + face_list_init(self.faces[i], + self.f_vector[i], self.coatoms.n_atoms(), + self.coatoms.n_coatoms(), self._mem) + + # The universe. + for j in range(self.coatoms.n_atoms()): + face_add_atom(self.faces[self.dimension+1].faces[0], j) + + # The coatoms. + if self.dimension > 0: + # Note that in the other cases, this was fully initialized above. + # Not just shallow. + face_list_shallow_copy(self.faces[self.dimension], self.coatoms.data) # Attributes for iterating over the incidences. self.is_incidence_initialized = 0 - cdef ListOfFaces incidence_face_mem = ListOfFaces(1, n_atoms) - self.incidence_face = incidence_face_mem.data[0] - self.faces_mem += (incidence_face_mem,) # needs to be stored somewhere + face_init(self.incidence_face, self.coatoms.n_atoms(), self.coatoms.n_coatoms(), self._mem) # Adding all faces, using the iterator. + for i in range(1, self.dimension): + self.faces[i].n_faces = 0 + cdef int d if face_iter.structure.current_dimension != self.dimension: # If there are proper faces. @@ -231,27 +202,12 @@ cdef class PolyhedronFaceLattice: # We already have the coatoms. d = face_iter.next_dimension() while (d < self.dimension): - self._add_face(d, face_iter.structure.face) + add_face_deep(self.faces[d+1], face_iter.structure.face) d = face_iter.next_dimension() # Sorting the faces, except for coatoms. self._sort() - cdef int _add_face(self, int face_dim, uint64_t *face) except -1: - r""" - Add a face to :class:`PolyhedronFaceLattice`. - - This method is used at initialization only. - """ - cdef size_t counter = self.face_counter[face_dim + 1] - cdef size_t max_number = self.f_vector[face_dim + 1] - if unlikely(counter >= max_number): - raise IOError("trying to add too many faces to ``PolyhedronFaceLattice``") - - # Actually add the face by copying its data. - bitset_copy(self.faces[face_dim + 1][counter], face, self.face_length) - self.face_counter[face_dim + 1] += 1 - cdef int _sort(self) except -1: r""" Sort each list of ``self.faces`` (except for coatoms). @@ -261,85 +217,12 @@ cdef class PolyhedronFaceLattice: cdef int dim = self.dimension cdef int i for i in range(dim + 2): - if unlikely(self.f_vector[i] != self.face_counter[i]): + if unlikely(self.f_vector[i] != self.faces[i].n_faces): raise ValueError("``PolyhedronFaceLattice`` does not contain all faces") - for i in range(0, dim): - # Sort each level set, except for coatoms, full- and empty polyhedron. - self._sort_one_list(self.faces[i], self.f_vector[i]) - - cdef int _sort_one_list(self, uint64_t **faces, size_t n_faces) except -1: - r""" - Sort ``faces`` of length ``n_faces``. - - See :meth:`sort`. - """ - cdef MemoryAllocator mem = MemoryAllocator() - - # Merge sort needs a second list of pointers. - cdef uint64_t **extra_mem = mem.allocarray(n_faces, sizeof(uint64_t *)) - - # Sort the faces using merge sort. - self._sort_one_list_loop(faces, faces, extra_mem, n_faces) - - cdef int _sort_one_list_loop( - self, uint64_t **inp, uint64_t **output1, - uint64_t **output2, size_t n_faces) except -1: - r""" - This is merge sort. - - Sorts ``inp`` and returns it in ``output1``. - - .. WARNING:: - - Input is the same as output1 or output2 - - See :meth:`sort`. - """ - if unlikely(n_faces == 0): - # Prevent it from crashing. - # In this case there is nothing to do anyway. - return 0 - - if n_faces == 1: - # The final case, where there is only one element. - output1[0] = inp[0] - return 0 - - cdef size_t middle = n_faces//2 - cdef size_t len_upper_half = n_faces - middle - - # Sort the upper and lower half of ``inp`` iteratively into ``output2``. - self._sort_one_list_loop(inp, output2, output1, middle) - self._sort_one_list_loop(&(inp[middle]), &(output2[middle]), - &(output1[middle]), len_upper_half) - - # Merge lower and upper half into ``output1``. - cdef size_t i = 0 # index through lower half - cdef size_t j = middle # index through upper half - cdef size_t counter = 0 # counts how many elements have been "merged" already - while i < middle and j < n_faces: - # Compare the lowest elements of lower and upper half. - if self.is_smaller(output2[i], output2[j]): - output1[counter] = output2[i] - i += 1 - counter += 1 - else: - output1[counter] = output2[j] - j += 1 - counter += 1 - if i < middle: - # Add the remaining elements of lower half. - while i < middle: - output1[counter] = output2[i] - i += 1 - counter += 1 - else: - # Add the remaining elements of upper half. - while j < n_faces: - output1[counter] = output2[j] - j += 1 - counter += 1 + for i in range(0, dim-1): + # Sort each level set, except for the facets, the full- and empty polyhedron. + sort_faces_list(self.faces[i+1]) def _find_face_from_combinatorial_face(self, CombinatorialFace face): r""" @@ -360,19 +243,20 @@ cdef class PolyhedronFaceLattice: Traceback (most recent call last): ... ValueError: cannot find a facet, as those are not sorted + """ if not (self.dual == face._dual): raise ValueError("iterator and allfaces not in same mode") - return self.find_face(face.dimension(), face.face) + cdef size_t face_index = self.find_face(face.dimension(), face.face) + if face_index == -1: + raise ValueError("face is not in the face lattice") + return face_index - cdef inline size_t find_face(self, int dimension, uint64_t *face) except -1: + cdef inline size_t find_face(self, int dimension, face_t face) except -2: r""" Return the index of ``face``, if it is of dimension ``dimension``. - .. NOTE:: - - Will give an index no matter if ``face`` is actual of dimension - ``dimension``. Check the result with :meth:`is_equal`. + Return -1 if the face is not contained. EXAMPLES:: @@ -390,47 +274,11 @@ cdef class PolyhedronFaceLattice: raise ValueError("cannot find a facet, as those are not sorted") # of course one can easily add a function to search for a facet as # well, but there seems to be no need for that + if unlikely(dimension < -1 or dimension > self.dimension): raise IndexError("dimension out of range") - cdef size_t start = 0 - cdef size_t middle - cdef n_faces = self.f_vector[dimension + 1] - cdef uint64_t **faces = self.faces[dimension + 1] - - while (n_faces > 1): - # In each iteration step, we will look for ``face`` in - # ``faces[start:start+n_faces]``. - middle = n_faces//2 - if self.is_smaller(face, faces[middle + start]): - # If face is in the list, then in the lower half. - # Look for face in ``faces[start : start + middle]`` in next step. - n_faces = middle - else: - # If face is in the list, then in the upper half. - # Look for face in ``faces[start+middle:start+n_faces]``, i.e. - # ``faces[start + middle : (start + middle) + n_faces - middle]``. - n_faces -= middle - start += middle - return start - - cdef inline bint is_smaller(self, uint64_t *one, uint64_t *two): - r""" - Return `1` if ``one`` is smaller than ``two``, otherwise `0`. - """ - return bitset_cmp(one, two, self.face_length) < 0 - cdef inline int is_equal(self, int dimension, size_t index, uint64_t *face) except -1: - r""" - Check wether ``face`` is of dimension ``dimension`` with index ``index``. - - This is used to validate the output of :meth:`find_face`. - """ - if unlikely(dimension < -1 or dimension > self.dimension - or index >= self.f_vector[dimension + 1]): - raise IndexError() - cdef uint64_t *face2 = self.faces[dimension+1][index] - cdef size_t i - return (0 == bitset_cmp(face, face2, self.face_length)) + return find_face(face, self.faces[dimension+1]) cpdef CombinatorialFace get_face(self, int dimension, size_t index): r""" @@ -496,15 +344,11 @@ cdef class PolyhedronFaceLattice: raise ValueError("no face of dimension %s"%dimension) if unlikely(index >= self.f_vector[dimension + 1]): raise IndexError("no %s-th face of dimension %s"%(index, dimension)) - if unlikely(self.coatoms.n_faces == 0): + if unlikely(self.coatoms.n_faces() == 0): return 0 - cdef size_t n_coatoms = self.f_vector[self.dimension] - cdef uint64_t **coatoms = self.faces[self.dimension] - cdef size_t face_length = self.face_length - cdef uint64_t *face = self.faces[dimension+1][index] - return bit_rep_to_coatom_rep(face, coatoms, n_coatoms, - face_length, self.coatom_rep) + cdef face_t face = self.faces[dimension+1].faces[index] + return bit_rep_to_coatom_rep(face, self.coatoms.data, self.coatom_rep) cdef size_t set_atom_rep(self, int dimension, size_t index) except -1: r""" @@ -519,9 +363,8 @@ cdef class PolyhedronFaceLattice: if unlikely(index >= self.f_vector[dimension + 1]): raise IndexError("no %s-th face of dimension %s"%(index, dimension)) - cdef size_t face_length = self.face_length - cdef uint64_t *face = self.faces[dimension+1][index] - return bit_rep_to_Vrep_list(face, self.atom_rep, face_length) + cdef face_t face = self.faces[dimension+1].faces[index] + return bit_rep_to_Vrep_list(face, self.atom_rep) cdef void incidence_init(self, int dimension_one, int dimension_two): r""" @@ -616,37 +459,34 @@ cdef class PolyhedronFaceLattice: See :meth:`next_incidence`. """ - cdef uint64_t **coatoms = self.faces[self.dimension] - cdef uint64_t *dimension_one_face # depending on the index ``incidence_counter_one`` + cdef face_list_t coatoms + coatoms[0] = self.coatoms.data[0] + cdef face_t dimension_one_face # depending on the index ``incidence_counter_one`` cdef size_t location # the index the intersection has, if of correct dimension - cdef int is_it_equal # checks if face with index ``location`` is intersection - if self.is_incidence_initialized == 1: # The standard case, where # ``0 < self.dimension_two + 1 == self.dimension_one < self.dimension``. one[0] = self.incidence_counter_one - dimension_one_face = self.faces[self.incidence_dim_one + 1][self.incidence_counter_one] + dimension_one_face = self.faces[self.incidence_dim_one + 1].faces[self.incidence_counter_one] # Get the intersection of ``dimension_one_face`` with the # ``self.incidence_counter_two``-th coatom. - intersection(self.incidence_face, dimension_one_face, - coatoms[self.incidence_counter_two], self.face_length) + face_intersection(self.incidence_face, dimension_one_face, + coatoms.faces[self.incidence_counter_two]) # Get the location of the intersection and # check, wether it is correct. location = self.find_face(self.incidence_dim_two, self.incidence_face) two[0] = location - is_it_equal = self.is_equal(self.incidence_dim_two, - location, self.incidence_face) # Set counters for next function call. self.incidence_counter_two += 1 if self.incidence_counter_two == self.f_vector[self.dimension]: self.incidence_counter_one += 1 self.incidence_counter_two = 0 - return is_it_equal + return location != -1 if self.is_incidence_initialized == 2: # the case where ``dimension_one`` is dimension of polyhedron. From 15f5e9b65a03388c439db0d75268d280739e4efc Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 18 Sep 2020 13:11:27 -0700 Subject: [PATCH 093/713] build/pkgs/gfortran/distros/void.txt: New --- build/pkgs/gfortran/distros/void.txt | 1 + 1 file changed, 1 insertion(+) create mode 100644 build/pkgs/gfortran/distros/void.txt diff --git a/build/pkgs/gfortran/distros/void.txt b/build/pkgs/gfortran/distros/void.txt new file mode 100644 index 00000000000..8f962328b1e --- /dev/null +++ b/build/pkgs/gfortran/distros/void.txt @@ -0,0 +1 @@ +gcc-fortran From d34dc7cfac8a47844aac347061ec58af1757772e Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 18 Sep 2020 13:11:46 -0700 Subject: [PATCH 094/713] build/pkgs/xz/distros/void.txt: New --- build/pkgs/xz/distros/void.txt | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 build/pkgs/xz/distros/void.txt diff --git a/build/pkgs/xz/distros/void.txt b/build/pkgs/xz/distros/void.txt new file mode 100644 index 00000000000..c5fa156f8a0 --- /dev/null +++ b/build/pkgs/xz/distros/void.txt @@ -0,0 +1,2 @@ +xz +liblzma-devel From 26dfb5130ab083a2a6e05500c2b5e6e549efa472 Mon Sep 17 00:00:00 2001 From: Joshua Campbell Date: Thu, 17 Sep 2020 15:16:22 -0700 Subject: [PATCH 095/713] Allow the title of Three.js plot pages to be customized Also, use title as suggested filename when saving as HTML. --- src/doc/en/reference/plot3d/threejs.rst | 3 +++ src/sage/ext_data/threejs/threejs_template.html | 14 ++++++++++++-- src/sage/plot/plot3d/base.pyx | 15 +++++++++++++++ 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/src/doc/en/reference/plot3d/threejs.rst b/src/doc/en/reference/plot3d/threejs.rst index 3c1cda92343..ea226908543 100644 --- a/src/doc/en/reference/plot3d/threejs.rst +++ b/src/doc/en/reference/plot3d/threejs.rst @@ -45,6 +45,9 @@ Options currently supported by the viewer: - ``opacity`` -- (default: 1) numeric value for transparency of lines and surfaces +- ``page_title`` -- (default: None) string containing the title of the generated HTML page; often + displayed in the browser window's title bar, its tab list, and/or the operating system's task bar + - ``projection`` -- (default: 'perspective') the type of camera projection to use; 'perspective' or 'orthographic' diff --git a/src/sage/ext_data/threejs/threejs_template.html b/src/sage/ext_data/threejs/threejs_template.html index 62dc0c44a93..f3f3adf1ed5 100644 --- a/src/sage/ext_data/threejs/threejs_template.html +++ b/src/sage/ext_data/threejs/threejs_template.html @@ -1,7 +1,7 @@ - +SAGE_TITLE