Permalink
Cannot retrieve contributors at this time
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
3034 lines (2539 sloc)
97.2 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# -*- coding: utf-8 -*- | |
r""" | |
Multidimensional Continued Fraction Algorithms (Cython code) | |
See also the Python code which provides more methods. | |
EXAMPLES:: | |
sage: from slabbe.mult_cont_frac_pyx import Brun | |
sage: algo = Brun() | |
Orbit in the cone (with dual coordinates):: | |
sage: algo.cone_orbit_list((10,23,15), 6) | |
[(((10.0, 8.0, 15.0), (1.0, 1.0, 2.0)), 132), | |
(((10.0, 8.0, 5.0), (3.0, 1.0, 2.0)), 213), | |
(((2.0, 8.0, 5.0), (3.0, 4.0, 2.0)), 321), | |
(((2.0, 3.0, 5.0), (3.0, 4.0, 6.0)), 132), | |
(((2.0, 3.0, 2.0), (3.0, 10.0, 6.0)), 123), | |
(((2.0, 1.0, 2.0), (3.0, 10.0, 16.0)), 132)] | |
Orbit in the simplex:: | |
sage: algo.simplex_orbit_list((10,23,15), 3) | |
[(0.30303030303030304, | |
0.24242424242424246, | |
0.45454545454545453, | |
0.25, | |
0.25, | |
0.5, | |
132), | |
(0.43478260869565216, | |
0.3478260869565218, | |
0.21739130434782603, | |
0.5, | |
0.16666666666666666, | |
0.3333333333333333, | |
213), | |
(0.13333333333333328, | |
0.5333333333333334, | |
0.3333333333333333, | |
0.33333333333333337, | |
0.4444444444444445, | |
0.22222222222222224, | |
321)] | |
BENCHMARKS: | |
With slabbe-0.2 or earlier, 68.6 ms on my machine. | |
With slabbe-0.3.b1, 62.2 ms on my machine. | |
With slabbe-0.3.b2, 28.6 ms on my machine. | |
With slabbe-0.3.b2, 13.3 ms on priminfo in Liège:: | |
sage: from slabbe.mult_cont_frac_pyx import Brun | |
sage: %time Brun().lyapunov_exponents(n_iterations=10^6) # not tested | |
(0.3049429393152174, -0.1120652699014143, 1.367495867105725) | |
With slabbe-0.3.b1, 74ms on my machine. | |
With slabbe-0.3.b2, 35ms on my machine. | |
With slabbe-0.3.b2, 17ms on priminfo in Liège:: | |
sage: from slabbe.mult_cont_frac_pyx import ARP | |
sage: %time ARP().lyapunov_exponents(n_iterations=10^6) # not tested | |
(0.443493194984839, -0.17269097306340797, 1.3893881011394358) | |
With slabbe-0.2 or earlier, 3.71s at liafa, 4.58s on my machine. | |
With slabbe-0.3.b1, 3.93s on my machine. | |
With slabbe-0.3.b2, 1.93s on my machine. | |
With slabbe-0.3.b2, 1.22s on priminfo in Liège:: | |
sage: %time Brun().lyapunov_exponents(n_iterations=67000000) # not tested | |
(0.30456433843239084, -0.1121770192467067, 1.36831961293987303) | |
With slabbe-0.3.b1, 4.83 s on my machine: | |
With slabbe-0.3.b2, 2.33 s on my machine: | |
With slabbe-0.3.b2, 1.56 s on priminfo in Liège:: | |
sage: %time ARP().lyapunov_exponents(n_iterations=67*10^6) # not tested | |
(0.44296596371477626, -0.17222952278277034, 1.3888098339168744) | |
With slabbe-0.2 or earlier, 660 ms on my machine. | |
With slabbe-0.3.b1, 640 ms on my machine (maybe this test could be made much | |
faster without using list...). | |
With slabbe-0.3.b2, 215 ms on priminfo in Liège:: | |
sage: %time L = Brun().simplex_orbit_list(n_iterations=10^6) # not tested | |
.. TODO:: | |
- Ajout les algo de reuteneaour, nogueira, autres? | |
- Allow 2d, 1d, 4d, algorithms | |
- utilise les vecteurs pour les plus grandes dimensions? | |
- Replace method ``_natural_extension_dict`` by ``simplex_orbit_filtered_list`` | |
- Use ``simplex_orbit_filtered_list`` for ``invariant_measure`` ? | |
- Essayer d'utiliser @staticmethod pour call pour que ARP puisse | |
apeler Poincare. Without the cython Error: Cannot call a static | |
method on an instance variable. | |
https://groups.google.com/d/topic/sage-support/DRI_s31D8ks/discussion | |
- Trouver meilleur nom pour PointFiber + x + a (cotangent, bundle, fiber?) | |
- Create memory inside algorithms (or points?) to perform computations... | |
- See the TODO in the file | |
- Move more methods into the PairPoint class | |
- cone_orbit_list is broken because it does not return distinct | |
points... (21 Feb 2017) | |
- Code qsort and sort and permutation (21 Feb 2017) so that we can use | |
this code in multidimensional case | |
- In order for TestSuite(Brun()).run() to work properly, Brun must be an | |
instance of SageObject. Is this a bug? But then using SageObject does | |
not compile to c. Need to check this... | |
Question: | |
- Comment factoriser le code sans utiliser les yield? | |
- Comment faire un appel de fonction rapide (pour factoriser le code) | |
AUTHORS: | |
- Sébastien Labbé, Invariant measures, Lyapounov exponents and natural | |
extensions for a dozen of algorithms, October 2013. | |
- Sébastien Labbé, Cleaning the code, Fall 2015 | |
- Sébastien Labbé, Making use of PairPoint to prepare for higher dimension, Fall 2016 | |
""" | |
#***************************************************************************** | |
# Copyright (C) 2013-2018 Sébastien Labbé <slabqc@gmail.com> | |
# | |
# Distributed under the terms of the GNU General Public License version 2 (GPLv2) | |
# | |
# The full text of the GPLv2 is available at: | |
# | |
# http://www.gnu.org/licenses/ | |
#***************************************************************************** | |
from __future__ import print_function | |
#from __future__ import absolute_import # creates a failing test in mult_cont_frac.py ?? | |
from libc.math cimport log | |
from cysignals.signals cimport sig_check # ctrl-c interrupt block support | |
from cysignals.memory cimport check_allocarray, sig_free | |
cdef double SQRT3SUR2 = 0.866025403784439 | |
# random | |
# https://groups.google.com/d/msg/cython-users/jc-3UK2Ffoc/GJNVx-CzdKsJ | |
from libc.stdlib cimport rand, RAND_MAX | |
cdef double RAND_SCALE = 1.0 / RAND_MAX | |
cdef inline double random_double_32bit(): | |
return rand() * RAND_SCALE | |
cdef inline double random_double_64bit(): | |
return rand() * RAND_SCALE + rand() * RAND_SCALE * RAND_SCALE | |
# qsort | |
# http://stackoverflow.com/questions/35095914/sorting-in-cython | |
# from libc.stdlib cimport qsort | |
# ... declaring "const void *" type seems problematic | |
# http://stackoverflow.com/questions/8353076/how-do-i-pass-a-pointer-to-a-c-fun$ | |
cdef extern from "stdlib.h": | |
ctypedef void const_void "const void" | |
void qsort(void *base, int nmemb, int size, | |
int(*compar)(const_void *, const_void *)) nogil | |
cdef int cmp_double(const_void * pa, const_void * pb): | |
r""" | |
cmp of doubles | |
""" | |
cdef double a = (<double *>pa)[0] | |
cdef double b = (<double *>pb)[0] | |
if a < b: | |
return -1 | |
elif a > b: | |
return 1 | |
else: | |
return 0 | |
cdef double* _KEY | |
cdef int cmp_int_KEY(const_void * pa, const_void * pb): | |
r""" | |
cmp of integers according to their values in global array _KEY | |
""" | |
cdef int a = (<int *>pa)[0] | |
cdef int b = (<int *>pb)[0] | |
if _KEY[a] < _KEY[b]: | |
return -1 | |
elif _KEY[a] > _KEY[b]: | |
return 1 | |
else: | |
return 0 | |
cdef class PairPoint: | |
r""" | |
EXAMPLES:: | |
sage: from slabbe.mult_cont_frac_pyx import PairPoint | |
sage: PairPoint(3, (.2,.3,.4)) | |
((0.2, 0.3, 0.4), (..., ..., ...)) | |
sage: PairPoint(3, a=(.2,.3,.4)) | |
((..., ..., ...), (0.2, 0.3, 0.4)) | |
""" | |
cdef double* x | |
cdef double* a | |
cdef double* _tmparray | |
cdef int* perm | |
cdef int dim | |
cdef double _tmp | |
def __cinit__(self, int dim, *args, **kwds): | |
self.x = <double*>check_allocarray(dim, sizeof(double)) | |
self.a = <double*>check_allocarray(dim, sizeof(double)) | |
self._tmparray = <double*>check_allocarray(dim, sizeof(double)) | |
self.perm = <int*>check_allocarray(dim, sizeof(int)) | |
self.dim = dim | |
def __init__(self, int dim, x=None, a=None): | |
cdef int i | |
if x is None: | |
for i in range(self.dim): | |
self.x[i] = random_double_64bit() | |
else: | |
for i in range(self.dim): | |
self.x[i] = x[i] | |
if a is None: | |
for i in range(self.dim): | |
self.a[i] = random_double_64bit() | |
else: | |
for i in range(self.dim): | |
self.a[i] = a[i] | |
def __dealloc__(self): | |
sig_free(self.x) | |
sig_free(self.a) | |
sig_free(self._tmparray) | |
def to_dict(self): | |
r""" | |
EXAMPLES:: | |
sage: from slabbe.mult_cont_frac_pyx import PairPoint | |
sage: PairPoint(3, [1,2,3], [4,5,6]).to_dict() | |
{'a': [4.0, 5.0, 6.0], 'x': [1.0, 2.0, 3.0]} | |
""" | |
cdef int i | |
return dict(x=[self.x[i] for i in range(self.dim)], | |
a=[self.a[i] for i in range(self.dim)]) | |
def to_tuple(self): | |
r""" | |
EXAMPLES:: | |
sage: from slabbe.mult_cont_frac_pyx import PairPoint | |
sage: PairPoint(3, [1,2,3], [4,5,6]).to_tuple() | |
((1.0, 2.0, 3.0), (4.0, 5.0, 6.0)) | |
""" | |
cdef int i | |
return (tuple(self.x[i] for i in range(self.dim)), | |
tuple(self.a[i] for i in range(self.dim))) | |
def __repr__(self): | |
r""" | |
EXAMPLES:: | |
sage: from slabbe.mult_cont_frac_pyx import PairPoint | |
sage: PairPoint(5, range(5), range(5)) | |
((0.0, 1.0, 2.0, 3.0, 4.0), (0.0, 1.0, 2.0, 3.0, 4.0)) | |
""" | |
return repr(self.to_tuple()) | |
cpdef void sort(self): | |
r""" | |
Sort array x and sort array a with the same permutation. | |
EXAMPLES:: | |
sage: from slabbe.mult_cont_frac_pyx import PairPoint | |
sage: P = PairPoint(4, [.4, .2, .3, .1], [4,3,2,1]) | |
sage: P.sort() | |
sage: P | |
((0.1, 0.2, 0.3, 0.4), (1.0, 3.0, 2.0, 4.0)) | |
:: | |
sage: P = PairPoint(3, [.3,.6,.2], [.2,.6,.3]) | |
sage: P.sort() | |
sage: P | |
((0.2, 0.3, 0.6), (0.3, 0.2, 0.6)) | |
""" | |
self.sort_a() | |
self.sort_x() | |
cpdef void sort_x(self): # nogil: | |
r""" | |
Sort array x independently of array a. | |
EXAMPLES:: | |
sage: from slabbe.mult_cont_frac_pyx import PairPoint | |
sage: P = PairPoint(4, [.4, .2, .3, .1], [4,3,2,1]) | |
sage: P.sort_x() | |
sage: P | |
((0.1, 0.2, 0.3, 0.4), (4.0, 3.0, 2.0, 1.0)) | |
""" | |
qsort(self.x, self.dim, sizeof(double), cmp_double) | |
cpdef void sort_a(self): | |
r""" | |
Sort array a according to values in array x. | |
EXAMPLES:: | |
sage: from slabbe.mult_cont_frac_pyx import PairPoint | |
sage: P = PairPoint(4, [.4, .2, .3, .1], [4,3,2,1]) | |
sage: P.sort_a() | |
sage: P | |
((0.4, 0.2, 0.3, 0.1), (1.0, 3.0, 2.0, 4.0)) | |
:: | |
sage: P = PairPoint(3, [.3,.6,.2], [.2,.6,.3]) | |
sage: P.sort_a() | |
sage: P | |
((0.3, 0.6, 0.2), (0.3, 0.2, 0.6)) | |
""" | |
cdef int branch = self.permutation() # result written in self.perm | |
# sort a according to self.perm | |
cdef int i | |
for i in range(self.dim): | |
self._tmparray[i] = self.a[self.perm[i]] | |
for i in range(self.dim): | |
self.a[i] = self._tmparray[i] | |
cpdef int permutation(self) except *: | |
r""" | |
http://stackoverflow.com/questions/17554242/how-to-obtain-the-index-permutation-after-the-sorting | |
OUTPUT: | |
int (the permutation, works well if self.dim < 10) | |
Permutation gets written to self.perm | |
EXAMPLES:: | |
sage: from slabbe.mult_cont_frac_pyx import PairPoint | |
sage: P = PairPoint(4, [.4, .2, .3, .1], [4,3,2,1]) | |
sage: P.permutation() | |
4231 | |
""" | |
cdef int i | |
for i in range(self.dim): | |
self.perm[i] = i | |
global _KEY | |
_KEY = self.x | |
qsort(self.perm, self.dim, sizeof(int), cmp_int_KEY) | |
if self.dim >= 10: | |
return -1 | |
cdef int rep = 0 | |
for i in range(self.dim): | |
rep *= 10 | |
rep += self.perm[i] + 1 | |
return rep | |
cdef PairPoint copy(self): | |
r""" | |
""" | |
cdef int i | |
return PairPoint(self.dim, | |
x=[self.x[i] for i in range(self.dim)], | |
a=[self.a[i] for i in range(self.dim)]) | |
cdef void copy_inplace(self, PairPoint P): | |
r""" | |
Copy P into self | |
""" | |
cdef int i | |
assert self.dim == P.dim, "dimension inconsistencies" | |
for i in range(self.dim): | |
self.x[i] = P.x[i] | |
for i in range(self.dim): | |
self.a[i] = P.a[i] | |
cdef double min_x(self): | |
r""" | |
Return the minimum entry of vector x | |
OUTPUT: | |
double | |
""" | |
cdef int i | |
self._tmp = self.x[0] | |
for i in range(1, self.dim): | |
if self._tmp > self.x[i]: | |
self._tmp = self.x[i] | |
return self._tmp | |
cdef double max_x(self): | |
r""" | |
Return the maximum entry of vector x | |
OUTPUT: | |
double | |
""" | |
cdef int i | |
self._tmp = self.x[0] | |
for i in range(1, self.dim): | |
if self._tmp < self.x[i]: | |
self._tmp = self.x[i] | |
return self._tmp | |
cdef double norm_x(self, int p=1): | |
r""" | |
Return the p-norm of vector x | |
INPUT: | |
- ``p`` -- integer, 0 or 1 | |
OUTPUT: | |
double | |
""" | |
cdef int i | |
if p == 1: # 1-norm | |
self._tmp = 0 | |
for i in range(self.dim): | |
self._tmp += abs(self.x[i]) | |
return self._tmp | |
elif p == 0: # sup norm | |
return self.max_x() | |
cdef double norm_a(self, int p=1): | |
r""" | |
Return the p-norm of vector a | |
INPUT: | |
- ``p`` -- integer, 0 or 1 | |
OUTPUT: | |
double | |
.. TODO:: | |
How to deal with the scalar product case? | |
""" | |
cdef int i | |
if p == 1: # 1-norm | |
self._tmp = 0 | |
for i in range(self.dim): | |
self._tmp += abs(self.a[i]) | |
return self._tmp | |
elif p == 0: # sup norm | |
self._tmp = 0 | |
for i in range(self.dim): | |
self._tmp = max(self._tmp, abs(self.a[i])) | |
return self._tmp | |
elif p == -1: # hypersurface | |
return self.dot_product() | |
cdef void normalize_x(self, double value): | |
r""" | |
Normalize vector x by dividing each entry by some value. | |
INPUT: | |
- ``value`` -- positive real number | |
""" | |
cdef int i | |
for i in range(self.dim): | |
self.x[i] /= value | |
cdef void normalize_a(self, double value): | |
r""" | |
Normalize vector a by dividing each entry by some value. | |
INPUT: | |
- ``value`` -- positive real number | |
""" | |
cdef int i | |
for i in range(self.dim): | |
self.a[i] /= value | |
cdef double dot_product(self): | |
r""" | |
TODO: peut-on utiliser _tmp ici? ou aura-t-on des probleme de | |
copie? | |
""" | |
cdef int i | |
self._tmp = 0 | |
for i in range(self.dim): | |
self._tmp += self.x[i] * self.a[i] | |
return self._tmp | |
cdef double dot_product_xx(self): | |
cdef int i | |
cdef double s = 0 | |
for i in range(self.dim): | |
s += self.x[i] * self.x[i] | |
return s | |
cdef void gramm_schmidt(self): | |
r""" | |
Removes x component for vector a. | |
""" | |
cdef int i | |
cdef double p,s | |
p = self.dot_product() | |
s = self.dot_product_xx() | |
for i in range(self.dim): | |
self.a[i] -= p*self.x[i]/s | |
cpdef int number_small_entries(self, double ratio, int p=1): | |
r""" | |
Returns the number of indices i such that x[i]/||x|| < ratio. | |
""" | |
cdef int i,c=0 | |
cdef double norm_ratio = self.norm_x(p) * ratio | |
for i in range(self.dim): | |
if self.x[i] < norm_ratio: | |
c += 1 | |
return c | |
cdef act_by_diagonal_matrix(self): | |
raise NotImplementedError | |
cdef (double, double) projection3to2(self, int p=1): | |
assert self.dim == 3, ("Dimension of point is {} but projection" | |
" implemented only for 3".format(self.dim)) | |
if p == 1: | |
return (-SQRT3SUR2 * self.x[0] + SQRT3SUR2 * self.x[1], | |
-.5 * self.x[0] -.5 * self.x[1] + self.x[2]) | |
elif p == 0: | |
return self.x[0], self.x[1] | |
cdef int _Poincare(self): | |
r""" | |
EXAMPLES:: | |
""" | |
cdef int branch = self.permutation() # result written in self.perm | |
cdef int i, s,t | |
for i in range(self.dim-1, 0, -1): | |
t = self.perm[i] | |
s = self.perm[i-1] | |
self.x[t] -= self.x[s] | |
self.a[s] += self.a[t] | |
return branch | |
cdef int _Selmer(self): | |
r""" | |
EXAMPLES:: | |
""" | |
cdef int branch = self.permutation() # result written in self.perm | |
cdef int MAX = self.perm[self.dim-1] | |
cdef int MIN = self.perm[0] | |
self.x[MAX] -= self.x[MIN] | |
self.a[MIN] += self.a[MAX] | |
return branch | |
cdef int _ArnouxRauzy(self): | |
r""" | |
EXAMPLES:: | |
""" | |
cdef int branch = self.permutation() # result written in self.perm | |
cdef int MAX = self.perm[self.dim-1] | |
cdef int i | |
for i in range(self.dim): | |
if i != MAX: | |
self.x[MAX] -= self.x[i] | |
self.a[i] += self.a[MAX] | |
return MAX + 1 | |
cdef int _ArnouxRauzyPoincare(self): | |
r""" | |
EXAMPLES:: | |
""" | |
cdef int branch = self.permutation() # result written in self.perm | |
cdef int MAX = self.perm[self.dim-1] | |
cdef int i | |
self._tmp = 0 | |
for i in range(self.dim): | |
if i != MAX: | |
self._tmp += self.x[i] | |
if self.x[MAX] > self._tmp: | |
return self._ArnouxRauzy() | |
else: | |
return self._Poincare() | |
cdef int _Poincare3(self): | |
r""" | |
EXAMPLES:: | |
""" | |
cdef int i,j,k | |
if self.x[0] <= self.x[1] <= self.x[2]: | |
i = 0; j = 1; k = 2; | |
elif self.x[0] <= self.x[2] <= self.x[1]: | |
i = 0; j = 2; k = 1; | |
elif self.x[1] <= self.x[0] <= self.x[2]: | |
i = 1; j = 0; k = 2; | |
elif self.x[2] <= self.x[0] <= self.x[1]: | |
i = 2; j = 0; k = 1; | |
elif self.x[1] <= self.x[2] <= self.x[0]: | |
i = 1; j = 2; k = 0; | |
elif self.x[2] <= self.x[1] <= self.x[0]: | |
i = 2; j = 1; k = 0; | |
else: | |
raise ValueError('limit case: reach set of measure zero: {}'.format(self)) | |
self.x[k] -= self.x[j] | |
self.x[j] -= self.x[i] | |
self.a[j] += self.a[k] | |
self.a[i] += self.a[j] | |
return 100*i + 10*j + k + 111 | |
cdef int _Sorted_Poincare(self): | |
r""" | |
EXAMPLES:: | |
""" | |
if self.dim != 3: | |
raise NotImplementedError("sort is implemented only for dim=3") | |
# Apply the algo | |
self.x[2] -= self.x[1] | |
self.x[1] -= self.x[0] | |
self.a[1] += self.a[2] | |
self.a[0] += self.a[1] | |
self.sort() | |
return 200 | |
cdef int _Sorted_ArnouxRauzy(self): | |
r""" | |
EXAMPLES:: | |
""" | |
if self.dim != 3: | |
raise NotImplementedError("sort is implemented only for dim=3") | |
#Arnoux-Rauzy | |
self.x[2] -= self.x[0] + self.x[1] | |
self.a[0] += self.a[2] | |
self.a[1] += self.a[2] | |
self.sort() | |
return 100 | |
cdef int _Sorted_ArnouxRauzyMulti(self): | |
r""" | |
EXAMPLES:: | |
""" | |
if self.dim != 3: | |
raise NotImplementedError("sort is implemented only for dim=3") | |
#Arnoux-Rauzy Multi | |
cdef int m | |
m = <int>(self.x[2] / (self.x[0] + self.x[1])) | |
self.x[2] -= m * (self.x[0] + self.x[1]) | |
self.a[1] += m * self.a[2]; | |
self.a[0] += m * self.a[2]; | |
self.sort() | |
return 100 | |
cdef int _Sorted_FullySubtractive(self): | |
r""" | |
EXAMPLES:: | |
""" | |
if self.dim != 3: | |
raise NotImplementedError("sort is implemented only for dim=3") | |
# Apply the algo | |
self.x[1] -= self.x[0] | |
self.x[2] -= self.x[0] | |
self.a[0] += self.a[1] + self.a[2] | |
self.sort() | |
return 100 | |
cdef class MCFAlgorithm(object): | |
cdef int dim | |
def __cinit__(self, int dim=3): | |
self.dim = dim | |
#def __init__(self, int dim=3): | |
# self.dim = dim | |
def __reduce__(self): | |
r""" | |
Default pickle support | |
TESTS:: | |
sage: from slabbe.mult_cont_frac_pyx import Brun | |
sage: algo = Brun() | |
sage: algo.__reduce__() | |
(<type 'slabbe.mult_cont_frac_pyx.Brun'>, ()) | |
""" | |
return self.__class__, tuple() | |
######################################## | |
# METHODS IMPLEMENTED IN HERITED CLASSES | |
######################################## | |
cdef int call(self, PairPoint P) except *: | |
r""" | |
This method must be implemented in the inherited classes. | |
EXAMPLES:: | |
""" | |
raise NotImplementedError | |
def substitutions(self): | |
r""" | |
This method must be implemented in the inherited classes. | |
EXAMPLES:: | |
sage: from slabbe.mult_cont_frac_pyx import Brun | |
sage: Brun().substitutions() | |
{123: WordMorphism: 1->1, 2->23, 3->3, | |
132: WordMorphism: 1->1, 2->2, 3->32, | |
213: WordMorphism: 1->13, 2->2, 3->3, | |
231: WordMorphism: 1->1, 2->2, 3->31, | |
312: WordMorphism: 1->12, 2->2, 3->3, | |
321: WordMorphism: 1->1, 2->21, 3->3} | |
""" | |
raise NotImplementedError | |
def dual_substitutions(self): | |
r""" | |
This method must be implemented in the inherited classes. | |
EXAMPLES:: | |
sage: from slabbe.mult_cont_frac_pyx import Brun | |
sage: Brun().dual_substitutions() | |
{123: WordMorphism: 1->1, 2->2, 3->32, | |
132: WordMorphism: 1->1, 2->23, 3->3, | |
213: WordMorphism: 1->1, 2->2, 3->31, | |
231: WordMorphism: 1->13, 2->2, 3->3, | |
312: WordMorphism: 1->1, 2->21, 3->3, | |
321: WordMorphism: 1->12, 2->2, 3->3} | |
""" | |
raise NotImplementedError | |
def branches(self, int n_iterations=1000): | |
r""" | |
Returns the branches labels of the algorithm. | |
This method is an heuristic and should be implemented in the | |
inherited classes. | |
EXAMPLES:: | |
sage: import slabbe.mult_cont_frac_pyx as mcf | |
sage: mcf.Brun().branches() | |
{123, 132, 213, 231, 312, 321} | |
sage: mcf.ARP().branches() | |
{1, 2, 3, 123, 132, 213, 231, 312, 321} | |
""" | |
cdef int i # loop counter | |
cdef PairPoint P | |
cdef int branch | |
S = set() | |
# Loop | |
for i in range(n_iterations): | |
# Check for Keyboard interupt | |
sig_check() | |
# random initial values | |
P = PairPoint(self.dim) | |
# Apply Algo | |
branch = self.call(P) | |
S.add(branch) | |
return S | |
###################### | |
# TEST METHODS | |
###################### | |
def _test_definition(self, int n_iterations=10000): | |
r""" | |
INPUT: | |
- ``n_iterations`` -- integer | |
EXAMPLES:: | |
sage: from slabbe.mult_cont_frac_pyx import Brun | |
sage: Brun()._test_definition(10000) | |
""" | |
cdef double s,t # temporary variables | |
cdef int i # loop counter | |
cdef PairPoint P,R | |
cdef int branch | |
# Loop | |
for i in range(n_iterations): | |
sig_check() # Check for Keyboard interupt | |
# random initial values | |
P = PairPoint(self.dim) | |
s = P.dot_product() | |
R = P.copy() | |
# Apply Algo | |
try: | |
branch = self.call(R) | |
except ValueError: | |
continue | |
t = R.dot_product() | |
if not abs(s - t) < 0.0000001: | |
m = 'This algo does not preserve the scalar product\n' | |
m += '{} != {}\n'.format(s,t) | |
m += 'The problem is on branch {}\n'.format(branch) | |
m += 'on the {}-th iteration\n'.format(i) | |
m += 'INPUT: {}\n'.format(P) | |
m += 'OUTPUT: {}\n'.format(R) | |
raise Exception(m) | |
return | |
def _test_dual_substitution_definition(self): | |
r""" | |
EXAMPLES:: | |
sage: from slabbe.mult_cont_frac_pyx import Brun | |
sage: t = Brun()._test_dual_substitution_definition() | |
""" | |
A = self.substitutions() | |
B = self.dual_substitutions() | |
assert set(A.keys()) == set(B.keys()) | |
for key in A: | |
a = A[key].incidence_matrix() | |
b = B[key].incidence_matrix() | |
if not a == b.transpose(): | |
raise ValueError("Transpose of substitution {} do not " | |
"match with dual substitution for algo " | |
" {}".format(key, self.name())) | |
return | |
def _test_coherence(self, int n_iterations=1000): | |
r""" | |
Check coherence between substitutions and the algo. | |
INPUT: | |
- ``n_iterations`` -- integer | |
EXAMPLES:: | |
sage: from slabbe.mult_cont_frac_pyx import Brun | |
sage: t = Brun()._test_coherence() | |
""" | |
from sage.modules.free_module_element import vector | |
cdef int i # loop counter | |
cdef PairPoint P,R | |
cdef int branch | |
A = dict((k,s.incidence_matrix()) for k,s in self.substitutions().items()) | |
# Loop | |
for i in range(n_iterations): | |
# Check for Keyboard interupt | |
sig_check() | |
# random initial values | |
P = PairPoint(self.dim) | |
R = P.copy() # TODO is the copy needed here? | |
# Apply Algo | |
try: | |
branch = self.call(R) | |
except ValueError: | |
continue | |
M = A[branch] | |
# Check the algo | |
s = M * vector((R.x[0], R.x[1], R.x[2])) | |
t = vector((P.x[0], P.x[1], P.x[2])) | |
if not abs(s - t) < 0.0000001: | |
m = 'Incoherence between the definition of algo \n' | |
m += 'and the associated substitutions.\n' | |
m += 'M * v_{{n+1}} = {} !=\nv_n = {}\n'.format(s,t) | |
m += 'The problem is on branch {}\n'.format(branch) | |
m += 'on the {}-th iteration\n'.format(i) | |
m += 'INPUT: {}\n'.format(P) | |
m += 'OUTPUT: {}\n'.format(R) | |
raise Exception(m) | |
# Check the dual coordinates | |
Mt = M.transpose() | |
s = Mt * vector((P.a[0], P.a[1], P.a[2])) | |
t = vector((R.a[0], R.a[1], R.a[2])) | |
if not abs(s - t) < 0.0000001: | |
m = 'Incoherence between the definition of algo (dual) \n' | |
m += 'and the associated substitutions.\n' | |
m += '{} != {}\n'.format(s,t) | |
m += 'The problem is on branch {}\n'.format(branch) | |
m += 'on the {}-th iteration\n'.format(i) | |
m += 'INPUT: {}\n'.format(P) | |
m += 'OUTPUT: {}\n'.format(R) | |
raise Exception(m) | |
return | |
###################### | |
# METHODS FOR THE USER: | |
###################### | |
def class_name(self): | |
r""" | |
The name of the class. | |
.. NOTE:: | |
This might not be the same as the name of the algorithm. | |
EXAMPLES:: | |
sage: from slabbe.mult_cont_frac_pyx import Reverse, Brun, ARP | |
sage: Reverse().class_name() | |
'Reverse' | |
sage: Brun().class_name() | |
'Brun' | |
sage: ARP().class_name() | |
'ARP' | |
""" | |
return self.__class__.__name__ | |
def name(self): | |
r""" | |
EXAMPLES:: | |
sage: from slabbe.mult_cont_frac_pyx import Reverse, Brun, ARP | |
sage: Reverse().name() | |
'Reverse' | |
sage: Brun().name() | |
'Brun' | |
sage: ARP().name() | |
"Arnoux-Rauzy-Poincar\\'e" | |
""" | |
return self.class_name() | |
def dimension(self): | |
r""" | |
Return the dimension of the linear space for this algorithm | |
EXAMPLES:: | |
sage: from slabbe.mult_cont_frac_pyx import Brun | |
sage: Brun().dimension() | |
3 | |
sage: Brun(3).dimension() | |
3 | |
sage: Brun(4).dimension() | |
4 | |
sage: Brun(25).dimension() | |
25 | |
""" | |
return self.dim | |
def __repr__(self): | |
r""" | |
EXAMPLES:: | |
sage: from slabbe.mult_cont_frac_pyx import Reverse, Brun | |
sage: Reverse() | |
Reverse 3-dimensional continued fraction algorithm | |
sage: Brun() | |
Brun 3-dimensional continued fraction algorithm | |
""" | |
return "{} {}-dimensional continued fraction algorithm".format(self.name(), self.dim) | |
def __hash__(self): | |
r""" | |
EXAMPLES:: | |
sage: from slabbe.mult_cont_frac_pyx import Reverse, Brun | |
sage: hash(Reverse()) # random | |
-232089942010536275 | |
sage: hash(Brun()) # random | |
-3664667051634349645 | |
""" | |
return hash((self.class_name(), self.dim)) | |
def __call__(self, PairPoint P): | |
r""" | |
Wrapper for the cdef call method. | |
EXAMPLES:: | |
sage: from slabbe.mult_cont_frac_pyx import Brun | |
sage: from slabbe.mult_cont_frac_pyx import PairPoint | |
sage: P = PairPoint(3, [.3,.6,.8], [.2,.3,.3]) | |
sage: Brun()(P) | |
((0.3, 0.6, 0.20000000000000007), (0.2, 0.6, 0.3)) | |
:: | |
sage: P = PairPoint(3, [3,6,8], [.2,.3,.3]) | |
sage: Brun()(P) | |
((3.0, 6.0, 2.0), (0.2, 0.6, 0.3)) | |
""" | |
# TODO should __call__ return a copy or changes P? | |
cdef PairPoint R = P.copy() | |
self.call(R) | |
return R | |
def __richcmp__(self, other, op): | |
r""" | |
INPUT: | |
- ``other`` -- | |
- ``op`` -- int, from 0 to 5 | |
EXAMPLES:: | |
sage: from slabbe.mult_cont_frac_pyx import Brun, ARP | |
sage: Brun() == ARP() | |
False | |
sage: Brun() == Brun() | |
True | |
""" | |
# 0: < | |
# 1: <= | |
# 2: == | |
# 3: != | |
# 4: > | |
# 5: >= | |
if not isinstance(other, MCFAlgorithm): | |
return NotImplemented | |
if op == 2 or op == 3: | |
return other.class_name() == self.class_name() | |
else: | |
return NotImplemented | |
###################### | |
# DYNAMICS METHODS | |
###################### | |
def coding_iterator(self, start): | |
r""" | |
INPUT: | |
- ``start`` -- iterable of three real numbers | |
OUTPUT: | |
iterator | |
EXAMPLES:: | |
sage: from slabbe.mult_cont_frac_pyx import ARP | |
sage: it = ARP().coding_iterator((1,e,pi)) | |
sage: [next(it) for _ in range(20)] | |
[123, 2, 1, 123, 1, 231, 3, 3, 3, 3, 123, 1, 1, 1, 231, 2, 321, 2, 3, 312] | |
:: | |
sage: from slabbe.mult_cont_frac_pyx import Poincare | |
sage: algo = Poincare(4) | |
sage: it = algo.coding_iterator((1,e,pi,sqrt(2))) | |
sage: [next(it) for _ in range(10)] | |
[1423, 4312, 3241, 3412, 3142, 3214, 4312, 1342, 3412, 1342] | |
""" | |
cdef PairPoint P = PairPoint(self.dim, start) | |
cdef int branch | |
while True: | |
branch = self.call(P) | |
yield branch | |
P.normalize_x(P.norm_x()) | |
def simplex_orbit_iterator(self, start=None, int norm_xyz=0, int norm_uvw=1): | |
r""" | |
INPUT: | |
- ``start`` - initial vector (default: ``None``), if None, then | |
initial point is random | |
- ``norm_xyz`` -- integer (default: ``0``), either ``0`` or ``1``, the | |
norm used for the orbit of points `(x,y,z)` of the algo | |
- ``norm_uvw`` -- integer (default: ``1``), either ``0`` or | |
``1`` or ``'hypersurfac'``, the norm used for the orbit of dual | |
coordinates `(u,v,w)`. | |
NOTE: | |
This iterator is 10x slower because of the yield statement. So | |
avoid using this when writing fast code. Just copy paste the | |
loop or use simplex_orbit_list or simplex_orbit_filtered_list method. | |
OUTPUT: | |
iterator | |
EXAMPLES:: | |
sage: from slabbe.mult_cont_frac_pyx import Brun | |
sage: it = Brun().simplex_orbit_iterator((.414578,.571324,.65513)) | |
sage: for _ in range(4): next(it) | |
((0.7256442929056017, 1.0, 0.14668734378391243), | |
(0.25, 0.5, 0.25), | |
123) | |
((1.0, 0.37808566783572695, 0.20214772612150184), | |
(0.5, 0.3333333333333333, 0.16666666666666666), | |
312) | |
((1.0, 0.6079385025908344, 0.32504111204194974), | |
(0.3333333333333333, 0.5555555555555555, 0.1111111111111111), | |
321) | |
((0.6449032192209051, 1.0, 0.534661171576946), | |
(0.25, 0.6666666666666666, 0.08333333333333333), | |
321) | |
:: | |
sage: from slabbe.mult_cont_frac_pyx import Brun | |
sage: it = Brun().simplex_orbit_iterator((.414578,.571324,.65513), norm_xyz=1) | |
sage: for _ in range(4): next(it) | |
((0.3875618393056797, 0.5340934161472103, 0.07834474454711005), | |
(0.25, 0.5, 0.25), | |
123) | |
((0.6328179140018012, 0.23925938363378257, 0.12792270236441622), | |
(0.5, 0.3333333333333333, 0.16666666666666666), | |
312) | |
((0.5173360300491189, 0.3145084914443481, 0.16815547850653312), | |
(0.3333333333333333, 0.5555555555555555, 0.1111111111111111), | |
321) | |
((0.2958862889959549, 0.45880727553726447, 0.24530643546678058), | |
(0.25, 0.6666666666666666, 0.08333333333333333), | |
321) | |
""" | |
cdef double s # temporary variables | |
cdef PairPoint P | |
cdef int branch, i | |
if start is None: | |
P = PairPoint(self.dim) | |
else: | |
P = PairPoint(self.dim, start, [1./self.dim for i in range(self.dim)]) | |
P.normalize_x(P.norm_x()) | |
# Loop | |
while True: | |
# Apply Algo | |
branch = self.call(P) | |
P.normalize_x(P.norm_x(p=norm_xyz)) | |
P.normalize_a(P.norm_a(p=norm_uvw)) | |
yield (P.x[0], P.x[1], P.x[2]), (P.a[0], P.a[1], P.a[2]), branch | |
def simplex_orbit_list(self, start=None, int n_iterations=100, | |
int norm_xyz=1, int norm_uvw=1): | |
r""" | |
INPUT: | |
- ``start`` - initial vector (default: ``None``), if None, then | |
initial point is random | |
- ``n_iterations`` - integer, number of iterations | |
- ``norm_xyz`` -- integer (default: ``1``), either ``0`` or ``1``, the | |
norm used for the orbit of points `(x,y,z)` of the algo | |
- ``norm_uvw`` -- integer (default: ``1``), either ``0`` or | |
``1`` or ``'hypersurfac'``, the norm used for the orbit of dual | |
coordinates `(u,v,w)`. | |
OUTPUT: | |
list | |
.. NOTE:: | |
It could be 10 times faster because 10^6 iterations can be done in | |
about 60ms on this machine. But for drawing images, it does not | |
matter to be 10 times slower:: | |
sage: %time L = Brun().simplex_orbit_list(10^6) # not tested | |
CPU times: user 376 ms, sys: 267 ms, total: 643 ms | |
Wall time: 660 ms | |
EXAMPLES:: | |
sage: from slabbe.mult_cont_frac_pyx import Brun | |
sage: L = Brun().simplex_orbit_list(n_iterations=10^5) | |
sage: L[-1] # random | |
(0.7307002153148079, | |
1.0, | |
0.31588474491578816, | |
0.29055326655584235, | |
0.4690741038784866, | |
0.24037262956567113, | |
321) | |
""" | |
cdef double s # temporary variables | |
cdef PairPoint P | |
cdef int i | |
cdef int branch | |
if start is None: | |
P = PairPoint(self.dim) | |
else: | |
P = PairPoint(self.dim, start, [1./self.dim for i in range(self.dim)]) | |
P.normalize_x(P.norm_x(p=norm_xyz)) | |
L = [] | |
# Loop | |
for i in range(n_iterations): | |
# Check for Keyboard interupt | |
sig_check() | |
# Apply Algo | |
branch = self.call(P) | |
P.normalize_x(P.norm_x(p=norm_xyz)) | |
P.normalize_a(P.norm_a(p=norm_uvw)) | |
L.append( (P.x[0], P.x[1], P.x[2], P.a[0], P.a[1], P.a[2], branch)) | |
return L | |
def simplex_orbit_filtered_list(self, start=None, int n_iterations=100, | |
int norm_xyz=1, int norm_uvw=1, | |
double xmin=-float('inf'), double xmax=float('inf'), | |
double ymin=-float('inf'), double ymax=float('inf'), | |
double umin=-float('inf'), double umax=float('inf'), | |
double vmin=-float('inf'), double vmax=float('inf'), | |
int ndivs=0): | |
r""" | |
Return a list of the orbit filtered to fit into a rectangle. | |
INPUT: | |
- ``start`` - initial vector (default: ``None``), if None, then | |
initial point is random | |
- ``n_iterations`` - integer, number of iterations | |
- ``norm_xyz`` -- integer (default: ``1``), either ``0`` or ``1``, the | |
norm used for the orbit of points `(x,y,z)` of the algo | |
- ``norm_uvw`` -- integer (default: ``1``), either ``0`` or | |
``1`` or ``'hypersurfac'``, the norm used for the orbit of dual | |
coordinates `(u,v,w)`. | |
- ``xmin`` - double | |
- ``ymin`` - double | |
- ``umin`` - double | |
- ``vmin`` - double | |
- ``xmax`` - double | |
- ``ymax`` - double | |
- ``umax`` - double | |
- ``vmax`` - double | |
- ``ndvis`` - integer, number of divisions | |
OUTPUT: | |
list | |
BENCHMARK:: | |
sage: from slabbe.mult_cont_frac_pyx import Brun | |
sage: %time D = Brun().simplex_orbit_filtered_list(10^6) # not tested | |
CPU times: user 366 ms, sys: 203 ms, total: 568 ms | |
Wall time: 570 ms | |
EXAMPLES:: | |
sage: from slabbe.mult_cont_frac_pyx import Brun | |
sage: start=(.414578,.571324,.65513) | |
sage: D = Brun().simplex_orbit_filtered_list(start, 3) | |
sage: D # random | |
[(0.3049590483124023, | |
-0.36889249928767137, | |
-0.21650635094610976, | |
-0.125, | |
312, | |
312), | |
(0.08651831333735083, | |
-0.31784823591841554, | |
-0.34641016151377557, | |
-0.2, | |
312, | |
312), | |
(-0.41045591033143647, | |
-0.20171750067080554, | |
-0.4330127018922195, | |
-0.25000000000000006, | |
312, | |
231)] | |
:: | |
sage: Brun().simplex_orbit_filtered_list(n_iterations=3, norm_xyz=1,ndivs=1000) | |
Traceback (most recent call last): | |
... | |
ValueError: when ndivs is specified, you must provide a value | |
for xmin, xmax, ymin, ymax, umin, umax, vmin and vmax | |
:: | |
sage: Brun().simplex_orbit_filtered_list(n_iterations=7, # random | |
....: norm_xyz=1, ndivs=100, | |
....: xmin=-.866, xmax=.866, ymin=-.5, ymax=1., | |
....: umin=-.866, umax=.866, vmin=-.5, vmax=1.) | |
[(30, 47, 50, 50, 132, 213), | |
(15, 83, 33, 66, 213, 231), | |
(18, 80, 38, 44, 231, 231), | |
(22, 75, 41, 33, 231, 231), | |
(30, 68, 43, 26, 231, 231), | |
(44, 53, 44, 22, 231, 213), | |
(41, 78, 24, 56, 213, 321)] | |
""" | |
cdef double s,x,y,u,v # temporary variables | |
s = x = y = u = v = 0 # initialize to avoid a warning | |
cdef PairPoint P | |
cdef int branch | |
cdef int previous_branch | |
cdef int xa,ya,ua,va | |
cdef int i | |
cdef double xlen = xmax - xmin | |
cdef double ylen = ymax - ymin | |
cdef double ulen = umax - umin | |
cdef double vlen = vmax - vmin | |
if start is None: | |
P = PairPoint(self.dim) | |
else: | |
P = PairPoint(self.dim, start, [1./self.dim for i in range(self.dim)]) | |
if ndivs and float('inf') in [-xmin, -ymin, -umin, -vmin, xmax, ymax, umax, vmax]: | |
raise ValueError("when ndivs is specified, you must provide a" | |
" value for xmin, xmax, ymin, ymax, umin, umax, vmin" | |
" and vmax") | |
P.normalize_x(P.norm_x(p=norm_xyz)) | |
L = [] | |
# Apply Algo once | |
branch = self.call(P) | |
# Loop | |
for i in range(n_iterations): | |
# Check for Keyboard interupt | |
sig_check() | |
P.normalize_x(P.norm_x(p=norm_xyz)) | |
P.normalize_a(P.norm_a(p=norm_uvw)) | |
# Projection | |
if norm_xyz == 1: | |
x = -SQRT3SUR2 * P.x[0] + SQRT3SUR2 * P.x[1] | |
y = -.5 * P.x[0] -.5 * P.x[1] + P.x[2] | |
elif norm_xyz == 0: | |
x = P.x[0] | |
y = P.x[1] | |
if norm_uvw == 1: | |
u = -SQRT3SUR2 * P.a[0] + SQRT3SUR2 * P.a[1] | |
v = -.5 * P.a[0] -.5 * P.a[1] + P.a[2] | |
elif norm_uvw == 0: | |
u = P.a[0] | |
v = P.a[1] | |
# Apply Algo | |
previous_branch = branch | |
branch = self.call(P) | |
# filter | |
if not (xmin < x < xmax and ymin < y < ymax and | |
umin < u < umax and vmin < v < vmax): | |
continue | |
# ndivs | |
if ndivs: | |
xa = int( (x-xmin) / xlen * ndivs ) | |
ya = int( (ymax-y) / ylen * ndivs ) | |
ua = int( (u-umin) / ulen * ndivs ) | |
va = int( (vmax-v) / vlen * ndivs ) | |
L.append( (xa,ya,ua,va, previous_branch, branch)) | |
else: | |
L.append( (x,y,u,v, previous_branch, branch)) | |
return L | |
def cone_orbit_iterator(self, start=None): | |
r""" | |
INPUT: | |
- ``start`` - initial vector (default: ``None``), if None, then | |
initial point is random | |
NOTE: | |
This iterator is 10x slower because of the yield statement. So | |
avoid using this when writing fast code. Just copy paste the | |
loop or use simplex_orbit_list or simplex_orbit_filtered_list method. | |
OUTPUT: | |
iterator of tuples (PairPoint, integer) | |
EXAMPLES:: | |
sage: from slabbe.mult_cont_frac_pyx import Brun | |
sage: it = Brun().cone_orbit_iterator((13,17,29)) | |
sage: for _ in range(10): next(it) | |
(((13.0, 17.0, 12.0), (1.0, 2.0, 1.0)), 123) | |
(((13.0, 4.0, 12.0), (3.0, 2.0, 1.0)), 312) | |
(((1.0, 4.0, 12.0), (3.0, 2.0, 4.0)), 231) | |
(((1.0, 4.0, 8.0), (3.0, 6.0, 4.0)), 123) | |
(((1.0, 4.0, 4.0), (3.0, 10.0, 4.0)), 123) | |
(((1.0, 4.0, 0.0), (3.0, 14.0, 4.0)), 123) | |
(((1.0, 3.0, 0.0), (17.0, 14.0, 4.0)), 312) | |
(((1.0, 2.0, 0.0), (31.0, 14.0, 4.0)), 312) | |
(((1.0, 1.0, 0.0), (45.0, 14.0, 4.0)), 312) | |
(((1.0, 0.0, 0.0), (59.0, 14.0, 4.0)), 312) | |
""" | |
cdef int branch,i | |
cdef PairPoint P | |
if start is None: | |
P = PairPoint(self.dim) | |
else: | |
P = PairPoint(self.dim, start, [1 for i in range(self.dim)]) | |
# Loop | |
while True: | |
sig_check() # Check for Keyboard interupt | |
branch = self.call(P) | |
yield P.copy(), branch | |
def cone_orbit_list(self, start=None, int n_iterations=100): | |
r""" | |
INPUT: | |
- ``start`` - initial vector (default: ``None``), if None, then | |
initial point is random | |
- ``n_iterations`` - integer, number of iterations | |
OUTPUT: | |
list of tuples (PairPoint, integer) | |
EXAMPLES:: | |
sage: from slabbe.mult_cont_frac_pyx import Brun | |
sage: L = Brun().cone_orbit_list((10, 21, 37), 20) | |
sage: L[-1] | |
(((1.0, 0.0, 0.0), (68.0, 55.0, 658.0)), 231) | |
.. TODO:: | |
Check for a fixed point stop then. | |
""" | |
cdef PairPoint P | |
cdef int branch, i | |
if start is None: | |
P = PairPoint(self.dim) | |
else: | |
P = PairPoint(self.dim, start, [1 for i in range(self.dim)]) | |
# Loop | |
L = [] | |
for i in range(n_iterations): | |
sig_check() # Check for Keyboard interupt | |
branch = self.call(P) | |
L.append( (P.copy(), branch) ) | |
return L | |
def image(self, start, int n_iterations=1): | |
r""" | |
Return the image of a vector in R^3 after n iterations. | |
INPUT: | |
- ``start`` - initial vector | |
- ``n_iterations`` - integer, number of iterations (default: 1) | |
OUTPUT: | |
tuple of three floats | |
EXAMPLES:: | |
sage: from slabbe.mult_cont_frac_pyx import Brun | |
sage: Brun().image((10, 21, 37)) | |
(10.0, 21.0, 16.0) | |
sage: Brun().image((10, 21, 37), 2) | |
(10.0, 5.0, 16.0) | |
sage: Brun().image((10, 21, 37), 3) | |
(10.0, 5.0, 6.0) | |
sage: Brun().image((10, 21, 37), 10) | |
(1.0, 1.0, 0.0) | |
""" | |
cdef PairPoint P = PairPoint(self.dim, start) | |
cdef int i, branch | |
for i in range(n_iterations): | |
sig_check() # Check for Keyboard interupt | |
branch = self.call(P) | |
return (P.x[0], P.x[1], P.x[2]) | |
def _invariant_measure_dict(self, unsigned int n_iterations, int ndivs, v=None, | |
int norm=1, verbose=False): | |
r""" | |
INPUT: | |
- ``n_iterations`` - integer, number of iterations | |
- ``ndvis`` - integer (less than or equal to 256), number of divisions per dimension | |
- ``v`` - initial vector (default: ``None``) | |
- ``norm`` -- integer (default: ``1``), either ``0`` or ``1``, the p-norm | |
used for the orbit of the algo | |
- ``verbose`` -- bool (default: ``False``) | |
OUTPUT: | |
dict | |
.. NOTE:: | |
This method should be erased and replaced by code in the spirit of | |
simplex_orbit_list. Or otherwise read [1] and change cpdef int | |
C[NDIVS][NDIVS][NDIVS] | |
[1] https://groups.google.com/forum/?fromgroups=#!topic/sage-devel/NCBmj2KjwEM | |
EXAMPLES:: | |
sage: from slabbe.mult_cont_frac_pyx import Brun | |
sage: D = Brun()._invariant_measure_dict(4, 10, verbose=True) # random | |
0.0799404500357 0.199341464229 0.720718085735 | |
0 1 | |
0.0998433745026 0.248971884172 0.651184741325 | |
0 2 | |
0.132942259282 0.331508073966 0.535549666752 | |
1 3 | |
0.198868907918 0.495904379777 0.305226712305 | |
1 4 | |
:: | |
sage: D = Brun()._invariant_measure_dict(100000, 5) | |
sage: sorted(D.items()) | |
[((0, 0), ...), | |
((0, 1), ...), | |
((0, 2), ...), | |
((0, 3), ...), | |
((0, 4), ...), | |
((1, 1), ...), | |
((1, 2), ...), | |
((1, 3), ...), | |
((2, 2), ...)] | |
It is 1000 times faster using C counter instead of a python dict counter:: | |
sage: D = Brun()._invariant_measure_dict(1000000, 10) # 0.05s | |
""" | |
# 146 works, 147 causes segmentation error!!! | |
DEF NDIVS = 100 | |
assert ndivs <= NDIVS, "ndivs(=%s) must be less or equal to %s" % (ndivs, NDIVS) | |
cdef double s | |
cdef unsigned int i | |
cdef int j,k | |
cdef int X,Y | |
cdef int branch | |
# initialization of the counter | |
# change this to something else | |
# see https://groups.google.com/forum/?fromgroups=#!topic/sage-devel/NCBmj2KjwEM | |
cpdef int C[NDIVS][NDIVS] | |
for k from 0 <= k <= ndivs: | |
for j from 0 <= j <= ndivs: | |
C[j][j] = 0 | |
cdef PairPoint P = PairPoint(self.dim) | |
P.sort() | |
P.normalize_x(P.norm_x(p=norm)) | |
for i in range(n_iterations): | |
sig_check() # Check for Keyboard interupt | |
branch = self.call(P) # Apply Algo | |
P.normalize_x(P.norm_x(p=norm)) | |
# Increase by one the counter for that part | |
X = int(P.x[0]*ndivs) | |
Y = int(P.x[1]*ndivs) | |
C[X][Y] += 1 | |
if verbose: | |
print(P.x[0],P.x[1],P.x[2]) | |
print(X,Y) | |
# Translate the counter into a python dict | |
D = {} | |
for k from 0 <= k <= ndivs: | |
for j from 0 <= j <= ndivs: | |
c = C[j][k] | |
if c > 0: | |
D[(j,k)] = c | |
return D | |
def _natural_extension_dict(self, unsigned int n_iterations, int norm_xyz=0, | |
int norm_uvw=1, verbose=False): | |
r""" | |
INPUT: | |
- ``n_iterations`` -- integer | |
- ``norm_xyz`` -- integer (default: ``0``), either ``0`` or ``1``, the | |
norm used for the orbit of points `(x,y,z)` of the algo | |
- ``norm_uvw`` -- integer (default: ``1``), either ``0`` or | |
``1`` or ``'hypersurfac'``, the norm used for the orbit of dual | |
coordinates `(u,v,w)`. | |
- ``verbose`` -- bool (default: ``False``) | |
OUTPUT: | |
dict, dict, dict, dict | |
.. NOTE:: | |
This method should be erased and replaced by simplex_orbit_list | |
EXAMPLES:: | |
sage: from slabbe.mult_cont_frac_pyx import ARP | |
sage: t = ARP()._natural_extension_dict(10000) | |
sage: [type(d) for d in t] | |
[<type 'collections.defaultdict'>, | |
<type 'collections.defaultdict'>, | |
<type 'collections.defaultdict'>, | |
<type 'collections.defaultdict'>] | |
:: | |
sage: from slabbe.mult_cont_frac_pyx import Brun | |
sage: t = Brun()._natural_extension_dict(3000, norm_xyz=1, norm_uvw=1) | |
sage: [len(a) for a in t] | |
[6, 6, 6, 6] | |
""" | |
cdef double x,y,z # vector (x,y,z) | |
cdef double u,v,w # vector (u,v,w) | |
cdef double p,s,t # temporary variables | |
cdef unsigned int i # loop counter | |
cdef double x_new,y_new,z_new | |
cdef double u_new,v_new,w_new | |
cdef int branch | |
cdef PairPoint P = PairPoint(self.dim) | |
P.sort() | |
P.normalize_x(P.norm_x(1)) | |
P.normalize_a(P.norm_a(1)) | |
cdef PairPoint R = P.copy() | |
import collections | |
domain_right = collections.defaultdict(list) | |
image_right = collections.defaultdict(list) | |
domain_left = collections.defaultdict(list) | |
image_left = collections.defaultdict(list) | |
# Loop | |
for i in range(n_iterations): | |
# Check for Keyboard interupt | |
sig_check() | |
# Apply Algo | |
branch = self.call(R) | |
if verbose: | |
print("x=%f, y=%f, z=%f" % (R.x[0],R.x[1],R.x[2])) | |
#print("u=%f, v=%f, w=%f" % (u,v,w)) | |
#s = x*u + y*v + z*w | |
#print("scal prod <(x,y,z),(u,v,w)> = %f (after algo)" % s) | |
R.normalize_x(R.norm_x(p=norm_xyz)) | |
R.normalize_a(R.norm_a(p=norm_uvw)) | |
# Projection | |
if norm_xyz == 1: | |
s = -SQRT3SUR2 * P.x[0] + SQRT3SUR2 * P.x[1] | |
t = -.5 * P.x[0] -.5 * P.x[1] + P.x[2] | |
domain_left[branch].append((s,t)) | |
s = -SQRT3SUR2 * R.x[0] + SQRT3SUR2 * R.x[1] | |
t = -.5 * R.x[0] -.5 * R.x[1] + R.x[2] | |
image_left[branch].append((s,t)) | |
elif norm_xyz == 0: | |
domain_left[branch].append((P.x[0],P.x[1])) | |
image_left[branch].append((R.x[0],R.x[1])) | |
if norm_uvw == 1: | |
s = -SQRT3SUR2 * P.a[0] + SQRT3SUR2 * P.a[1] | |
t = -.5 * P.a[0] -.5 * P.a[1] + P.a[2] | |
domain_right[branch].append((s,t)) | |
s = -SQRT3SUR2 * R.a[0] + SQRT3SUR2 * R.a[1] | |
t = -.5 * R.a[0] -.5 * R.a[1] + R.a[2] | |
image_right[branch].append((s,t)) | |
elif norm_uvw == 0: | |
domain_right[branch].append((P.a[0],P.a[1])) | |
image_right[branch].append((R.a[0],R.a[1])) | |
P.copy_inplace(R) | |
return domain_left, image_left, domain_right, image_right | |
def lyapunov_exponents(self, start=None, unsigned int n_iterations=1000, verbose=False): | |
r""" | |
Return the lyapunov exponents (theta1, theta2, 1-theta2/theta1) | |
See also the module ``slabbe.lyapunov`` for parallel computations. | |
INPUT: | |
- ``start`` - initial vector (default: ``None``), if None, then | |
initial point is random | |
- ``n_iterations`` -- integer | |
- ``verbose`` -- bool (default: ``False``) | |
OUTPUT: | |
tuple of the first two liapounov exponents and the uniform | |
approximation exponent: | |
(theta1, theta2, 1-theta2/theta1) | |
.. NOTE:: | |
the code of this method was translated from C to cython. The C | |
version is from Vincent Delecroix. | |
EXAMPLES:: | |
sage: from slabbe.mult_cont_frac_pyx import Brun | |
sage: Brun().lyapunov_exponents(n_iterations=1000000) # tol 0.02 | |
(0.3049429393152174, -0.1120652699014143, 1.367495867105725) | |
:: | |
sage: start = (0.2134134, 0.31618415, 0.414514985) | |
sage: Brun().lyapunov_exponents(start=start, n_iterations=10^6) # tol 0.01 | |
(0.3046809303742965, -0.1121152799778245, 1.3679760326322108) | |
""" | |
cdef double theta1=0, theta2=0 # values of Lyapunov exponents | |
cdef double theta1c=0, theta2c=0 # compensation (for Kahan summation algorithm) | |
cdef double p,s,t # temporary variables | |
cdef unsigned int i # unsigned loop counter | |
cdef int j # loop counter | |
cdef double critical_value=0.0001 | |
cdef int branch | |
cdef PairPoint P | |
# initial values | |
P = PairPoint(self.dim, start) | |
for j in range(self.dim): | |
P.a[j] - .5 | |
P.sort() | |
P.normalize_x(P.norm_x(p=1)) | |
P.gramm_schmidt() | |
P.normalize_a(P.norm_a(p=1)) | |
if verbose: | |
print("P = {}\nscal prod <x,a> = {}".format(P, P.dot_product())) | |
# Loop | |
for i in range(n_iterations): | |
# Check for Keyboard interupt | |
sig_check() | |
# Apply Algo | |
branch = self.call(P) | |
if verbose: | |
print("P = {}\nscal prod <x,a> = {} (after algo)".format(P, P.dot_product())) | |
# Save some computations | |
#if i % step == 0: | |
if P.x[0] < critical_value: | |
# see | |
# Sylvie Boldo, Stef Graillat, Jean-Michel Muller | |
# On the robustness of the 2Sum and Fast2Sum algorithms | |
# https://hal.inria.fr/ensl-01310023 | |
# Sum the first lyapunov exponent | |
s = P.norm_x() | |
p = -log(s) - theta1c | |
t = theta1 + p | |
theta1c = (t-theta1) - p # mathematically 0 but not for a computer!! | |
theta1 = t | |
P.normalize_x(s) | |
# Sum the second lyapunov exponent | |
s = P.norm_a() | |
p = log(s) - theta2c | |
t = theta2 + p | |
theta2c = (t-theta2) - p # mathematically 0 but not for a computer!! | |
theta2 = t | |
# the following gramm shimdts seems to be useless, but it is not!!! | |
P.gramm_schmidt() | |
P.normalize_a(s) | |
return theta1/n_iterations, theta2/n_iterations, 1-theta2/theta1 | |
def nsmall_entries_list(self, double ratio, start=None, unsigned int n_iterations=1000, int p=1): | |
r""" | |
INPUT: | |
- ``ratio`` - real number, 0 < ratio < 1 | |
- ``start`` - initial vector (default: ``None``), if None, then | |
initial point is random | |
- ``n_iterations`` -- integer | |
- ``p`` -- integer, p-norm | |
EXAMPLES:: | |
sage: from slabbe.mult_cont_frac_pyx import Poincare | |
sage: algo = Poincare(4) | |
sage: algo.nsmall_entries_list(.1, (1,e,pi,sqrt(2)), n_iterations=20) | |
[0, 1, 1, 1, 1, 0, 0, 1, 0, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3] | |
:: | |
sage: from slabbe.finite_word import run_length_encoding | |
sage: L = algo.nsmall_entries_list(.01, (1,e,pi,sqrt(2)), n_iterations=1000) | |
sage: run_length_encoding(L) | |
[(0, 1), (1, 1), (0, 7), (1, 1), (0, 3), (1, 2), (2, 1), (3, 984)] | |
""" | |
cdef unsigned int i # unsigned loop counter | |
cdef int j # loop counter | |
cdef int branch | |
cdef PairPoint P | |
# initial values | |
P = PairPoint(self.dim, start, [0 for j in range(self.dim)]) | |
P.normalize_x(P.norm_x(p=p)) | |
L = [] | |
for i in range(n_iterations): | |
sig_check() # Check for Keyboard interupt | |
branch = self.call(P) # Apply Algo | |
P.normalize_x(P.norm_x(p=p)) | |
L.append(P.number_small_entries(ratio, p=p)) | |
return L | |
def return_time_to_nsmall_entries(self, double ratio, int n, start=None, int p=1): | |
r""" | |
INPUT: | |
- ``ratio`` - real number, 0 < ratio < 1 | |
- ``n`` - integer, number of small entries | |
- ``start`` - initial vector (default: ``None``), if None, then | |
initial point is random | |
- ``p`` -- integer, p-norm | |
OUTPUT: | |
a tuple (integer, PairPoint) | |
EXAMPLES:: | |
sage: from slabbe.mult_cont_frac_pyx import Poincare | |
sage: algo = Poincare(4) | |
sage: algo.return_time_to_nsmall_entries(.05, 0, (1,e,pi,sqrt(2))) | |
(3, | |
((0.31830988618379064, 0.41509782135371204, | |
0.13474402056773493, 0.1318482718947624), | |
(0.0, 0.0, 0.0, 0.0))) | |
:: | |
sage: algo = Poincare(6) | |
sage: start = (1,e,pi,sqrt(2),sqrt(3),sqrt(5)) | |
sage: algo.return_time_to_nsmall_entries(.05, 0, start) | |
(5, | |
((0.3183098861837907, 0.153493436015088, 0.134744020567735, | |
0.1318482718947624, 0.1011707373432389, 0.16043364799538504), | |
(0.0, 0.0, 0.0, 0.0, 0.0, 0.0))) | |
""" | |
cdef unsigned int i=0 # unsigned loop counter | |
cdef int j=0 # loop counter | |
cdef int branch | |
cdef PairPoint P | |
# initial values | |
P = PairPoint(self.dim, start, [0 for j in range(self.dim)]) | |
P.normalize_x(P.norm_x(p=p)) | |
while True: | |
sig_check() # Check for Keyboard interupt | |
branch = self.call(P) # Apply Algo | |
P.normalize_x(P.norm_x(p=p)) | |
if P.number_small_entries(ratio, p=p) == n: | |
return i, P | |
if P.min_x() == 0: | |
raise ValueError("precision problem at " | |
"i={},\nP={}".format(i, P)) | |
i += 1 | |
cdef class Brun(MCFAlgorithm): | |
r""" | |
EXAMPLES:: | |
sage: from slabbe.mult_cont_frac_pyx import Brun | |
sage: algo = Brun() | |
sage: TestSuite(algo).run() | |
sage: algo._test_dual_substitution_definition() | |
sage: algo._test_coherence() | |
sage: algo._test_definition() | |
""" | |
cdef int call(self, PairPoint P) except *: | |
r""" | |
TODO: Veut-avoir cette methode ici ou dans la classe PairPoint? | |
EXAMPLES:: | |
sage: from slabbe.mult_cont_frac_pyx import Brun | |
sage: from slabbe.mult_cont_frac_pyx import PairPoint | |
sage: P = PairPoint(3, [.3,.6,.8], [.2,.3,.3]) | |
sage: Brun()(P) | |
((0.3, 0.6, 0.20000000000000007), (0.2, 0.6, 0.3)) | |
""" | |
cdef int branch = P.permutation() # result written in P.perm | |
cdef int MAX1 = P.perm[P.dim-1] | |
cdef int MAX2 = P.perm[P.dim-2] | |
P.x[MAX1] -= P.x[MAX2] | |
P.a[MAX2] += P.a[MAX1] | |
return branch | |
def substitutions(self): | |
r""" | |
EXAMPLES:: | |
sage: from slabbe.mult_cont_frac_pyx import Brun | |
sage: Brun().substitutions() | |
{123: WordMorphism: 1->1, 2->23, 3->3, | |
132: WordMorphism: 1->1, 2->2, 3->32, | |
213: WordMorphism: 1->13, 2->2, 3->3, | |
231: WordMorphism: 1->1, 2->2, 3->31, | |
312: WordMorphism: 1->12, 2->2, 3->3, | |
321: WordMorphism: 1->1, 2->21, 3->3} | |
""" | |
from sage.combinat.words.morphism import WordMorphism | |
return {312: WordMorphism({1: [1, 2], 2: [2], 3: [3]}), | |
321: WordMorphism({1: [1], 2: [2, 1], 3: [3]}), | |
213: WordMorphism({1: [1, 3], 2: [2], 3: [3]}), | |
231: WordMorphism({1: [1], 2: [2], 3: [3, 1]}), | |
123: WordMorphism({1: [1], 2: [2, 3], 3: [3]}), | |
132: WordMorphism({1: [1], 2: [2], 3: [3, 2]})} | |
def dual_substitutions(self): | |
r""" | |
EXAMPLES:: | |
sage: from slabbe.mult_cont_frac_pyx import Brun | |
sage: Brun().dual_substitutions() | |
{123: WordMorphism: 1->1, 2->2, 3->32, | |
132: WordMorphism: 1->1, 2->23, 3->3, | |
213: WordMorphism: 1->1, 2->2, 3->31, | |
231: WordMorphism: 1->13, 2->2, 3->3, | |
312: WordMorphism: 1->1, 2->21, 3->3, | |
321: WordMorphism: 1->12, 2->2, 3->3} | |
""" | |
from sage.combinat.words.morphism import WordMorphism | |
return {321: WordMorphism({1: [1, 2], 2: [2], 3: [3]}), | |
312: WordMorphism({1: [1], 2: [2, 1], 3: [3]}), | |
231: WordMorphism({1: [1, 3], 2: [2], 3: [3]}), | |
213: WordMorphism({1: [1], 2: [2], 3: [3, 1]}), | |
132: WordMorphism({1: [1], 2: [2, 3], 3: [3]}), | |
123: WordMorphism({1: [1], 2: [2], 3: [3, 2]})} | |
cdef class Reverse(MCFAlgorithm): | |
r""" | |
EXAMPLES:: | |
sage: from slabbe.mult_cont_frac_pyx import Reverse | |
sage: algo = Reverse() | |
sage: TestSuite(algo).run() | |
sage: algo._test_dual_substitution_definition() | |
sage: algo._test_coherence() | |
sage: algo._test_definition() | |
""" | |
cdef int call(self, PairPoint P) except *: | |
r""" | |
EXAMPLES:: | |
sage: from slabbe.mult_cont_frac_pyx import Reverse | |
sage: from slabbe.mult_cont_frac_pyx import PairPoint | |
sage: P = PairPoint(3, [.3,.6,.8], [.2,.3,.3]) | |
sage: Reverse()(P) | |
((0.55, 0.25, 0.04999999999999993), (0.6, 0.5, 0.5)) | |
""" | |
if P.dim != 3: | |
raise NotImplementedError("implemented only for dim=3") | |
cdef PairPoint R = PairPoint(self.dim) | |
if P.x[0] + P.x[1] < P.x[2]: | |
P.x[2] -= P.x[0] + P.x[1] | |
P.a[1] += P.a[2] | |
P.a[0] += P.a[2] | |
return 3 | |
elif P.x[0] + P.x[2] < P.x[1]: | |
P.x[1] -= P.x[0] + P.x[2] | |
P.a[2] += P.a[1] | |
P.a[0] += P.a[1] | |
return 2 | |
elif P.x[1] + P.x[2] < P.x[0]: | |
P.x[0] -= P.x[1] + P.x[2] | |
P.a[1] += P.a[0] | |
P.a[2] += P.a[0] | |
return 1 | |
else: | |
# R.x[0] = 0.629960524947437 * (-P.x[0] + P.x[1] + P.x[2]) | |
# R.x[1] = 0.629960524947437 * ( P.x[0] - P.x[1] + P.x[2]) | |
# R.x[2] = 0.629960524947437 * ( P.x[0] + P.x[1] - P.x[2]) | |
# # 0.793700525984100 = 1/2*4^(1/3) | |
# # 0.629960524947437 = 1/4*4^(2/3) | |
# R.a[0] = 0.793700525984100 * (P.a[1] + P.a[2]) | |
# R.a[1] = 0.793700525984100 * (P.a[0] + P.a[2]) | |
# R.a[2] = 0.793700525984100 * (P.a[0] + P.a[1]) | |
R.x[0] = 0.5 * (-P.x[0] + P.x[1] + P.x[2]) | |
R.x[1] = 0.5 * ( P.x[0] - P.x[1] + P.x[2]) | |
R.x[2] = 0.5 * ( P.x[0] + P.x[1] - P.x[2]) | |
R.a[0] = P.a[1] + P.a[2] | |
R.a[1] = P.a[0] + P.a[2] | |
R.a[2] = P.a[0] + P.a[1] | |
P.copy_inplace(R) | |
return 4 | |
def substitutions(self): | |
r""" | |
EXAMPLES:: | |
sage: from slabbe.mult_cont_frac_pyx import Reverse | |
sage: Reverse().substitutions() | |
{1: WordMorphism: 1->1, 2->21, 3->31, | |
2: WordMorphism: 1->12, 2->2, 3->32, | |
3: WordMorphism: 1->13, 2->23, 3->3, | |
4: WordMorphism: 1->23, 2->31, 3->12} | |
""" | |
from sage.combinat.words.morphism import WordMorphism | |
return {1: WordMorphism({1: [1], 2: [2, 1], 3: [3, 1]}), | |
2: WordMorphism({1: [1, 2], 2: [2], 3: [3, 2]}), | |
3: WordMorphism({1: [1, 3], 2: [2, 3], 3: [3]}), | |
4: WordMorphism({1: [2,3], 2: [3,1], 3: [1,2]})} | |
def dual_substitutions(self): | |
r""" | |
EXAMPLES:: | |
sage: from slabbe.mult_cont_frac_pyx import Reverse | |
sage: Reverse().dual_substitutions() | |
{1: WordMorphism: 1->123, 2->2, 3->3, | |
2: WordMorphism: 1->1, 2->231, 3->3, | |
3: WordMorphism: 1->1, 2->2, 3->312, | |
4: WordMorphism: 1->23, 2->13, 3->12} | |
""" | |
from sage.combinat.words.morphism import WordMorphism | |
return {1: WordMorphism({1: [1,2,3], 2: [2], 3: [3]}), | |
2: WordMorphism({1: [1], 2: [2,3,1], 3: [3]}), | |
3: WordMorphism({1: [1], 2: [2], 3: [3,1,2]}), | |
4: WordMorphism({1: [2,3], 2: [1,3], 3: [1,2]})} | |
cdef class ARP(MCFAlgorithm): | |
r""" | |
EXAMPLES:: | |
sage: from slabbe.mult_cont_frac_pyx import ARP | |
sage: algo = ARP() | |
sage: TestSuite(algo).run() | |
sage: algo._test_dual_substitution_definition() | |
sage: algo._test_coherence() | |
sage: algo._test_definition() | |
""" | |
cdef int call(self, PairPoint P) except *: | |
r""" | |
EXAMPLES:: | |
sage: from slabbe.mult_cont_frac_pyx import ARP | |
sage: from slabbe.mult_cont_frac_pyx import PairPoint | |
sage: P = PairPoint(3, [.3,.6,.8], [.2,.3,.3]) | |
sage: ARP()(P) | |
((0.3, 0.3, 0.20000000000000007), (0.8, 0.6, 0.3)) | |
""" | |
return P._ArnouxRauzyPoincare() | |
def name(self): | |
r""" | |
EXAMPLES:: | |
sage: from slabbe.mult_cont_frac_pyx import ARP | |
sage: ARP().name() | |
"Arnoux-Rauzy-Poincar\\'e" | |
""" | |
return r"Arnoux-Rauzy-Poincar\'e" | |
def substitutions(self): | |
r""" | |
EXAMPLES:: | |
sage: from slabbe.mult_cont_frac_pyx import ARP | |
sage: ARP().substitutions() | |
{1: WordMorphism: 1->1, 2->21, 3->31, | |
2: WordMorphism: 1->12, 2->2, 3->32, | |
3: WordMorphism: 1->13, 2->23, 3->3, | |
123: WordMorphism: 1->123, 2->23, 3->3, | |
132: WordMorphism: 1->132, 2->2, 3->32, | |
213: WordMorphism: 1->13, 2->213, 3->3, | |
231: WordMorphism: 1->1, 2->231, 3->31, | |
312: WordMorphism: 1->12, 2->2, 3->312, | |
321: WordMorphism: 1->1, 2->21, 3->321} | |
""" | |
from sage.combinat.words.morphism import WordMorphism | |
return {1: WordMorphism({1: [1], 2: [2, 1], 3: [3, 1]}), | |
2: WordMorphism({1: [1, 2], 2: [2], 3: [3, 2]}), | |
3: WordMorphism({1: [1, 3], 2: [2, 3], 3: [3]}), | |
312: WordMorphism({1: [1, 2], 2: [2], 3: [3, 1, 2]}), | |
321: WordMorphism({1: [1], 2: [2, 1], 3: [3, 2, 1]}), | |
213: WordMorphism({1: [1, 3], 2: [2, 1, 3], 3: [3]}), | |
231: WordMorphism({1: [1], 2: [2, 3, 1], 3: [3, 1]}), | |
123: WordMorphism({1: [1, 2, 3], 2: [2, 3], 3: [3]}), | |
132: WordMorphism({1: [1, 3, 2], 2: [2], 3: [3, 2]})} | |
def dual_substitutions(self): | |
r""" | |
EXAMPLES:: | |
sage: from slabbe.mult_cont_frac_pyx import ARP | |
sage: ARP().dual_substitutions() | |
{1: WordMorphism: 1->123, 2->2, 3->3, | |
2: WordMorphism: 1->1, 2->231, 3->3, | |
3: WordMorphism: 1->1, 2->2, 3->312, | |
123: WordMorphism: 1->1, 2->21, 3->321, | |
132: WordMorphism: 1->1, 2->231, 3->31, | |
213: WordMorphism: 1->12, 2->2, 3->312, | |
231: WordMorphism: 1->132, 2->2, 3->32, | |
312: WordMorphism: 1->13, 2->213, 3->3, | |
321: WordMorphism: 1->123, 2->23, 3->3} | |
""" | |
from sage.combinat.words.morphism import WordMorphism | |
return {1: WordMorphism({1: [1,2,3], 2: [2], 3: [3]}), | |
2: WordMorphism({1: [1], 2: [2,3,1], 3: [3]}), | |
3: WordMorphism({1: [1], 2: [2], 3: [3,1,2]}), | |
213: WordMorphism({1: [1, 2], 2: [2], 3: [3, 1, 2]}), | |
123: WordMorphism({1: [1], 2: [2, 1], 3: [3, 2, 1]}), | |
312: WordMorphism({1: [1, 3], 2: [2, 1, 3], 3: [3]}), | |
132: WordMorphism({1: [1], 2: [2, 3, 1], 3: [3, 1]}), | |
321: WordMorphism({1: [1, 2, 3], 2: [2, 3], 3: [3]}), | |
231: WordMorphism({1: [1, 3, 2], 2: [2], 3: [3, 2]})} | |
cdef class ArnouxRauzy(MCFAlgorithm): | |
r""" | |
EXAMPLES:: | |
sage: from slabbe.mult_cont_frac_pyx import ArnouxRauzy | |
sage: algo = ArnouxRauzy() | |
sage: TestSuite(algo).run() | |
sage: algo._test_dual_substitution_definition() | |
sage: algo._test_coherence() | |
sage: algo._test_definition() | |
""" | |
cdef int call(self, PairPoint P) except *: | |
r""" | |
EXAMPLES:: | |
sage: from slabbe.mult_cont_frac_pyx import ArnouxRauzy | |
sage: from slabbe.mult_cont_frac_pyx import PairPoint | |
sage: P = PairPoint(3, [.3,.6,.8], [.2,.3,.3]) | |
sage: ArnouxRauzy()(P) | |
Traceback (most recent call last): | |
... | |
ValueError: Arnoux is not defined on | |
((0.3, 0.6, 0.8), (0.2, 0.3, 0.3)) | |
:: | |
sage: P = PairPoint(3, [.3,.2,.8], [.2,.3,.3]) | |
sage: ArnouxRauzy()(P) | |
((0.3, 0.2, 0.30000000000000004), (0.5, 0.6, 0.3)) | |
""" | |
if P.x[0] + P.x[1] < P.x[2]: | |
P.x[2] -= P.x[0] + P.x[1] | |
P.a[1] += P.a[2] | |
P.a[0] += P.a[2] | |
return 3 | |
elif P.x[0] + P.x[2] < P.x[1]: | |
P.x[1] -= P.x[0] + P.x[2] | |
P.a[2] += P.a[1] | |
P.a[0] += P.a[1] | |
return 2 | |
elif P.x[1] + P.x[2] < P.x[0]: | |
P.x[0] -= P.x[1] + P.x[2] | |
P.a[1] += P.a[0] | |
P.a[2] += P.a[0] | |
return 1 | |
else: | |
raise ValueError('Arnoux is not defined on {}'.format(P)) | |
def substitutions(self): | |
r""" | |
EXAMPLES:: | |
sage: from slabbe.mult_cont_frac_pyx import ArnouxRauzy | |
sage: ArnouxRauzy().substitutions() | |
{1: WordMorphism: 1->1, 2->21, 3->31, | |
2: WordMorphism: 1->12, 2->2, 3->32, | |
3: WordMorphism: 1->13, 2->23, 3->3} | |
""" | |
from sage.combinat.words.morphism import WordMorphism | |
return {1: WordMorphism({1: [1], 2: [2, 1], 3: [3, 1]}), | |
2: WordMorphism({1: [1, 2], 2: [2], 3: [3, 2]}), | |
3: WordMorphism({1: [1, 3], 2: [2, 3], 3: [3]})} | |
def dual_substitutions(self): | |
r""" | |
EXAMPLES:: | |
sage: from slabbe.mult_cont_frac_pyx import ArnouxRauzy | |
sage: ArnouxRauzy().dual_substitutions() | |
{1: WordMorphism: 1->123, 2->2, 3->3, | |
2: WordMorphism: 1->1, 2->231, 3->3, | |
3: WordMorphism: 1->1, 2->2, 3->312} | |
""" | |
from sage.combinat.words.morphism import WordMorphism | |
return {1: WordMorphism({1: [1,2,3], 2: [2], 3: [3]}), | |
2: WordMorphism({1: [1], 2: [2,3,1], 3: [3]}), | |
3: WordMorphism({1: [1], 2: [2], 3: [3,1,2]})} | |
cdef class Poincare(MCFAlgorithm): | |
r""" | |
EXAMPLES:: | |
sage: from slabbe.mult_cont_frac_pyx import Poincare | |
sage: algo = Poincare() | |
sage: TestSuite(algo).run() | |
sage: algo._test_dual_substitution_definition() | |
sage: algo._test_coherence() | |
sage: algo._test_definition() | |
""" | |
cdef int call(self, PairPoint P) except *: | |
r""" | |
EXAMPLES:: | |
sage: from slabbe.mult_cont_frac_pyx import Poincare | |
sage: from slabbe.mult_cont_frac_pyx import PairPoint | |
sage: P = PairPoint(3, [.3,.6,.8], [.2,.3,.3]) | |
sage: Poincare()(P) | |
((0.3, 0.3, 0.20000000000000007), (0.8, 0.6, 0.3)) | |
""" | |
return P._Poincare() | |
def name(self): | |
r""" | |
EXAMPLES:: | |
sage: from slabbe.mult_cont_frac_pyx import Poincare | |
sage: Poincare().name() | |
"Poincar\\'e" | |
""" | |
return r"Poincar\'e" | |
def substitutions(self): | |
r""" | |
EXAMPLES:: | |
sage: from slabbe.mult_cont_frac_pyx import Poincare | |
sage: Poincare().substitutions() | |
{123: WordMorphism: 1->123, 2->23, 3->3, | |
132: WordMorphism: 1->132, 2->2, 3->32, | |
213: WordMorphism: 1->13, 2->213, 3->3, | |
231: WordMorphism: 1->1, 2->231, 3->31, | |
312: WordMorphism: 1->12, 2->2, 3->312, | |
321: WordMorphism: 1->1, 2->21, 3->321} | |
""" | |
from sage.combinat.words.morphism import WordMorphism | |
return {312: WordMorphism({1: [1, 2], 2: [2], 3: [3, 1, 2]}), | |
321: WordMorphism({1: [1], 2: [2, 1], 3: [3, 2, 1]}), | |
213: WordMorphism({1: [1, 3], 2: [2, 1, 3], 3: [3]}), | |
231: WordMorphism({1: [1], 2: [2, 3, 1], 3: [3, 1]}), | |
123: WordMorphism({1: [1, 2, 3], 2: [2, 3], 3: [3]}), | |
132: WordMorphism({1: [1, 3, 2], 2: [2], 3: [3, 2]})} | |
def dual_substitutions(self): | |
r""" | |
EXAMPLES:: | |
sage: from slabbe.mult_cont_frac_pyx import Poincare | |
sage: Poincare().dual_substitutions() | |
{123: WordMorphism: 1->1, 2->21, 3->321, | |
132: WordMorphism: 1->1, 2->231, 3->31, | |
213: WordMorphism: 1->12, 2->2, 3->312, | |
231: WordMorphism: 1->132, 2->2, 3->32, | |
312: WordMorphism: 1->13, 2->213, 3->3, | |
321: WordMorphism: 1->123, 2->23, 3->3} | |
""" | |
from sage.combinat.words.morphism import WordMorphism | |
return {213: WordMorphism({1: [1, 2], 2: [2], 3: [3, 1, 2]}), | |
123: WordMorphism({1: [1], 2: [2, 1], 3: [3, 2, 1]}), | |
312: WordMorphism({1: [1, 3], 2: [2, 1, 3], 3: [3]}), | |
132: WordMorphism({1: [1], 2: [2, 3, 1], 3: [3, 1]}), | |
321: WordMorphism({1: [1, 2, 3], 2: [2, 3], 3: [3]}), | |
231: WordMorphism({1: [1, 3, 2], 2: [2], 3: [3, 2]})} | |
cdef class Selmer(MCFAlgorithm): | |
r""" | |
EXAMPLES:: | |
sage: from slabbe.mult_cont_frac_pyx import Selmer | |
sage: algo = Selmer() | |
sage: TestSuite(algo).run() | |
sage: algo._test_dual_substitution_definition() | |
sage: algo._test_coherence() | |
sage: algo._test_definition() | |
""" | |
cdef int call(self, PairPoint P) except *: | |
r""" | |
EXAMPLES:: | |
sage: from slabbe.mult_cont_frac_pyx import Selmer | |
sage: from slabbe.mult_cont_frac_pyx import PairPoint | |
sage: P = PairPoint(3, [.3,.6,.8], [.2,.3,.3]) | |
sage: Selmer()(P) | |
((0.3, 0.6, 0.5), (0.5, 0.3, 0.3)) | |
""" | |
return P._Selmer() | |
def substitutions(self): | |
r""" | |
EXAMPLES:: | |
sage: from slabbe.mult_cont_frac_pyx import Selmer | |
sage: Selmer().substitutions() | |
{123: WordMorphism: 1->13, 2->2, 3->3, | |
132: WordMorphism: 1->12, 2->2, 3->3, | |
213: WordMorphism: 1->1, 2->23, 3->3, | |
231: WordMorphism: 1->1, 2->21, 3->3, | |
312: WordMorphism: 1->1, 2->2, 3->32, | |
321: WordMorphism: 1->1, 2->2, 3->31} | |
""" | |
from sage.combinat.words.morphism import WordMorphism | |
return {132: WordMorphism({1: [1, 2], 2: [2], 3: [3]}), | |
231: WordMorphism({1: [1], 2: [2, 1], 3: [3]}), | |
123: WordMorphism({1: [1, 3], 2: [2], 3: [3]}), | |
321: WordMorphism({1: [1], 2: [2], 3: [3, 1]}), | |
213: WordMorphism({1: [1], 2: [2, 3], 3: [3]}), | |
312: WordMorphism({1: [1], 2: [2], 3: [3, 2]})} | |
def dual_substitutions(self): | |
r""" | |
EXAMPLES:: | |
sage: from slabbe.mult_cont_frac_pyx import Selmer | |
sage: Selmer().dual_substitutions() | |
{123: WordMorphism: 1->1, 2->2, 3->31, | |
132: WordMorphism: 1->1, 2->21, 3->3, | |
213: WordMorphism: 1->1, 2->2, 3->32, | |
231: WordMorphism: 1->12, 2->2, 3->3, | |
312: WordMorphism: 1->1, 2->23, 3->3, | |
321: WordMorphism: 1->13, 2->2, 3->3} | |
""" | |
from sage.combinat.words.morphism import WordMorphism | |
return {231: WordMorphism({1: [1, 2], 2: [2], 3: [3]}), | |
132: WordMorphism({1: [1], 2: [2, 1], 3: [3]}), | |
321: WordMorphism({1: [1, 3], 2: [2], 3: [3]}), | |
123: WordMorphism({1: [1], 2: [2], 3: [3, 1]}), | |
312: WordMorphism({1: [1], 2: [2, 3], 3: [3]}), | |
213: WordMorphism({1: [1], 2: [2], 3: [3, 2]})} | |
cdef class FullySubtractive(MCFAlgorithm): | |
r""" | |
EXAMPLES:: | |
sage: from slabbe.mult_cont_frac_pyx import FullySubtractive | |
sage: algo = FullySubtractive() | |
sage: TestSuite(algo).run() | |
sage: algo._test_dual_substitution_definition() | |
sage: algo._test_coherence() | |
sage: algo._test_definition() | |
""" | |
cdef int call(self, PairPoint P) except *: | |
r""" | |
EXAMPLES:: | |
sage: from slabbe.mult_cont_frac_pyx import FullySubtractive | |
sage: from slabbe.mult_cont_frac_pyx import PairPoint | |
sage: P = PairPoint(3, [.3,.6,.8], [.2,.3,.3]) | |
sage: FullySubtractive()(P) | |
((0.3, 0.3, 0.5), (0.8, 0.3, 0.3)) | |
""" | |
if P.x[0] <= P.x[1] and P.x[0] <= P.x[2]: | |
P.x[1] -= P.x[0] | |
P.x[2] -= P.x[0] | |
P.a[0] += P.a[1] + P.a[2] | |
return 1 | |
elif P.x[1] <= P.x[0] and P.x[1] <= P.x[2]: | |
P.x[0] -= P.x[1] | |
P.x[2] -= P.x[1] | |
P.a[1] += P.a[0] + P.a[2] | |
return 2 | |
elif P.x[2] <= P.x[0] and P.x[2] <= P.x[1]: | |
P.x[0] -= P.x[2] | |
P.x[1] -= P.x[2] | |
P.a[2] += P.a[0] + P.a[1] | |
return 3 | |
else: | |
raise ValueError('limit case: reach set of measure zero: {}'.format(P)) | |
def name(self): | |
r""" | |
EXAMPLES:: | |
sage: from slabbe.mult_cont_frac_pyx import FullySubtractive | |
sage: FullySubtractive().name() | |
'Fully Subtractive' | |
""" | |
return r"Fully Subtractive" | |
def substitutions(self): | |
r""" | |
EXAMPLES:: | |
sage: from slabbe.mult_cont_frac_pyx import FullySubtractive | |
sage: FullySubtractive().substitutions() | |
{1: WordMorphism: 1->123, 2->2, 3->3, | |
2: WordMorphism: 1->1, 2->231, 3->3, | |
3: WordMorphism: 1->1, 2->2, 3->312} | |
""" | |
from sage.combinat.words.morphism import WordMorphism | |
return {1: WordMorphism({1: [1,2,3], 2: [2], 3: [3]}), | |
2: WordMorphism({1: [1], 2: [2,3,1], 3: [3]}), | |
3: WordMorphism({1: [1], 2: [2], 3: [3,1,2]})} | |
def dual_substitutions(self): | |
r""" | |
EXAMPLES:: | |
sage: from slabbe.mult_cont_frac_pyx import FullySubtractive | |
sage: FullySubtractive().dual_substitutions() | |
{1: WordMorphism: 1->1, 2->21, 3->31, | |
2: WordMorphism: 1->12, 2->2, 3->32, | |
3: WordMorphism: 1->13, 2->23, 3->3} | |
""" | |
from sage.combinat.words.morphism import WordMorphism | |
return {1: WordMorphism({1: [1], 2: [2, 1], 3: [3, 1]}), | |
2: WordMorphism({1: [1, 2], 2: [2], 3: [3, 2]}), | |
3: WordMorphism({1: [1, 3], 2: [2, 3], 3: [3]})} | |
cdef class Cassaigne(MCFAlgorithm): | |
r""" | |
EXAMPLES:: | |
sage: from slabbe.mult_cont_frac_pyx import Cassaigne | |
sage: algo = Cassaigne() | |
sage: TestSuite(algo).run() | |
sage: algo._test_dual_substitution_definition() | |
sage: algo._test_coherence() | |
sage: algo._test_definition() | |
""" | |
cdef int call(self, PairPoint P) except *: | |
r""" | |
This algorithm was provided by Julien Cassaigne during a meeting of | |
the ANR DynA3S on October 12, 2015 held in Paris. It is inspired | |
from a method to generate words of complexity 2n+1 on a three | |
letter alphabet of arbitrary letter frequencies. | |
EXAMPLES:: | |
sage: from slabbe.mult_cont_frac_pyx import Cassaigne | |
sage: from slabbe.mult_cont_frac_pyx import PairPoint | |
sage: P = PairPoint(3, [.3,.6,.8], [.2,.3,.3]) | |
sage: Cassaigne()(P) | |
((0.6, 0.3, 0.5), (0.3, 0.5, 0.3)) | |
""" | |
if P.dim != 3: | |
raise NotImplementedError("implemented only for dim=3") | |
cdef double tmp | |
if P.x[0] >= P.x[2] : | |
P.x[0] -= P.x[2] | |
tmp = P.x[1] | |
P.x[1] = P.x[2] | |
P.x[2] = tmp | |
tmp = P.a[1] | |
P.a[1] = P.a[0] + P.a[2] | |
P.a[2] = tmp | |
return 1 | |
elif P.x[0] < P.x[2] : | |
P.x[2] -= P.x[0] | |
tmp = P.x[1] | |
P.x[1] = P.x[0] | |
P.x[0] = tmp | |
tmp = P.a[1] | |
P.a[1] = P.a[0] + P.a[2] | |
P.a[0] = tmp | |
return 2 | |
else: | |
raise ValueError('limit case: reach set of measure zero: {}'.format(P)) | |
def substitutions(self): | |
r""" | |
EXAMPLES:: | |
sage: from slabbe.mult_cont_frac_pyx import Cassaigne | |
sage: Cassaigne().substitutions() | |
{1: WordMorphism: 1->1, 2->13, 3->2, | |
2: WordMorphism: 1->2, 2->13, 3->3} | |
""" | |
from sage.combinat.words.morphism import WordMorphism | |
return {1: WordMorphism({1: [1], 2: [1, 3], 3: [2]}), | |
2: WordMorphism({1: [2], 2: [1, 3], 3: [3]})} | |
def dual_substitutions(self): | |
r""" | |
EXAMPLES:: | |
sage: from slabbe.mult_cont_frac_pyx import Cassaigne | |
sage: Cassaigne().dual_substitutions() | |
{1: WordMorphism: 1->12, 2->3, 3->2, | |
2: WordMorphism: 1->2, 2->1, 3->23} | |
""" | |
from sage.combinat.words.morphism import WordMorphism | |
return {1: WordMorphism({1: [1,2], 2: [3], 3: [2]}), | |
2: WordMorphism({1: [2], 2: [1], 3: [2,3]})} | |
cdef class Sorted_Brun(MCFAlgorithm): | |
cdef int call(self, PairPoint P) except *: | |
r""" | |
EXAMPLES:: | |
sage: from slabbe.mult_cont_frac_pyx import Sorted_Brun | |
sage: from slabbe.mult_cont_frac_pyx import PairPoint | |
sage: P = PairPoint(3, [.3,.6,.8], [.2,.3,.3]) | |
sage: Sorted_Brun()(P) | |
((0.20000000000000007, 0.3, 0.6), (0.3, 0.2, 0.6)) | |
:: | |
sage: P = PairPoint(3, [.3,.45,.8], [.2,.3,.3]) | |
sage: D = {'x':.3,'y':.45,'z':.8,'u':.2,'v':.3,'w':.3,'branch':999} | |
sage: Sorted_Brun()(P) | |
((0.3, 0.35000000000000003, 0.45), (0.2, 0.3, 0.6)) | |
:: | |
sage: P = PairPoint(3, [.3,.3,.8], [.2,.3,.3]) | |
sage: Sorted_Brun()(P) | |
((0.3, 0.3, 0.5), (0.2, 0.6, 0.3)) | |
""" | |
if P.dim != 3: | |
raise NotImplementedError("implemented only for dim=3") | |
P.x[2] -= P.x[1] | |
P.a[1] += P.a[2] | |
P.sort() | |
return 100 | |
cdef class Sorted_BrunMulti(MCFAlgorithm): | |
cdef int call(self, PairPoint P) except *: | |
r""" | |
EXAMPLES:: | |
sage: from slabbe.mult_cont_frac_pyx import Sorted_BrunMulti | |
sage: from slabbe.mult_cont_frac_pyx import PairPoint | |
sage: P = PairPoint(3, [.3,.3,.8], [.2,.3,.3]) | |
sage: Sorted_BrunMulti()(P) | |
((0.20000000000000007, 0.3, 0.3), (0.3, 0.2, 0.8999999999999999)) | |
""" | |
cdef int m = <int>(P.x[2] / P.x[1]) | |
P.x[2] -= m*P.x[1] | |
P.a[1] += m*P.a[2] | |
P.sort() | |
return 100 | |
cdef class Sorted_Selmer(MCFAlgorithm): | |
cdef int call(self, PairPoint P) except *: | |
r""" | |
EXAMPLES:: | |
sage: from slabbe.mult_cont_frac_pyx import Sorted_Selmer | |
sage: from slabbe.mult_cont_frac_pyx import PairPoint | |
sage: P = PairPoint(3, [.3,.6,.8], [.2,.3,.3]) | |
sage: Sorted_Selmer()(P) | |
((0.3, 0.5, 0.6), (0.5, 0.3, 0.3)) | |
""" | |
if P.dim != 3: | |
raise NotImplementedError("implemented only for dim=3") | |
P.x[2] -= P.x[0] | |
P.a[0] += P.a[2] | |
P.sort() | |
return 100 | |
cdef class Sorted_FullySubtractive(MCFAlgorithm): | |
cdef int call(self, PairPoint P) except *: | |
r""" | |
EXAMPLES:: | |
sage: from slabbe.mult_cont_frac_pyx import Sorted_FullySubtractive | |
sage: from slabbe.mult_cont_frac_pyx import PairPoint | |
sage: P = PairPoint(3, [.3,.6,.8], [.2,.3,.3]) | |
sage: Sorted_FullySubtractive()(P) | |
((0.3, 0.3, 0.5), (0.8, 0.3, 0.3)) | |
""" | |
if P.dim != 3: | |
raise NotImplementedError("implemented only for dim=3") | |
# Apply the algo | |
P.x[1] -= P.x[0] | |
P.x[2] -= P.x[0] | |
P.a[0] += P.a[1] + P.a[2] | |
P.sort() | |
return 100 | |
cdef class Sorted_ArnouxRauzy(MCFAlgorithm): | |
cdef int call(self, PairPoint P) except *: | |
r""" | |
EXAMPLES:: | |
sage: from slabbe.mult_cont_frac_pyx import Sorted_ArnouxRauzy | |
sage: from slabbe.mult_cont_frac_pyx import PairPoint | |
sage: P = PairPoint(3, [.3,.4,.8], [.2,.3,.4]) | |
sage: Sorted_ArnouxRauzy()(P) # tol | |
((0.1, 0.3, 0.4), (0.4, 0.6, 0.7)) | |
:: | |
sage: P = PairPoint(3, [.3,.7,.8], [.2,.3,.4]) | |
sage: Sorted_ArnouxRauzy()(P) | |
((-0.19999999999999996, 0.3, 0.7), (0.4, 0.6000000000000001, 0.7)) | |
""" | |
if P.dim != 3: | |
raise NotImplementedError("implemented only for dim=3") | |
#Arnoux-Rauzy | |
P.x[2] -= P.x[0] + P.x[1] | |
P.a[0] += P.a[2] | |
P.a[1] += P.a[2] | |
P.sort() | |
return 100 | |
cdef class Sorted_ArnouxRauzyMulti(MCFAlgorithm): | |
cdef int call(self, PairPoint P) except *: | |
r""" | |
EXAMPLES:: | |
""" | |
if P.dim != 3: | |
raise NotImplementedError("implemented only for dim=3") | |
#Arnoux-Rauzy Multi | |
cdef int m | |
m = <int>(P.x[2] / (P.x[0] + P.x[1])) | |
P.x[2] -= m * (P.x[0] + P.x[1]) | |
P.a[1] += m * P.a[2]; | |
P.a[0] += m * P.a[2]; | |
P.sort() | |
return 100 | |
cdef class Sorted_ARP(MCFAlgorithm): | |
cdef int call(self, PairPoint P) except *: | |
r""" | |
EXAMPLES:: | |
sage: from slabbe.mult_cont_frac_pyx import Sorted_ARP | |
sage: from slabbe.mult_cont_frac_pyx import PairPoint | |
sage: P = PairPoint(3, [.3,.4,.8], [.2,.3,.4]) | |
sage: Sorted_ARP()(P) # tol | |
((0.1, 0.3, 0.4), (0.4, 0.6, 0.7)) | |
:: | |
sage: P = PairPoint(3, [.3,.7,.8], [.2,.3,.4]) | |
sage: Sorted_ARP()(P) # tol | |
((0.1, 0.3, 0.4), (0.4, 0.9, 0.7)) | |
""" | |
if P.dim != 3: | |
raise NotImplementedError("implemented only for dim=3") | |
# Apply the algo | |
if P.x[2] > P.x[0] + P.x[1]: | |
return P._Sorted_ArnouxRauzy() | |
else: | |
return P._Sorted_Poincare() | |
cdef class Sorted_ARPMulti(MCFAlgorithm): | |
cdef int call(self, PairPoint P) except *: | |
r""" | |
EXAMPLES:: | |
sage: from slabbe.mult_cont_frac_pyx import Sorted_ARPMulti | |
sage: from slabbe.mult_cont_frac_pyx import PairPoint | |
sage: P = PairPoint(3, [.3,.5,.8], [.2,.3,.3]) | |
sage: Sorted_ARPMulti()(P) | |
((0.2, 0.3, 0.30000000000000004), (0.6, 0.8, 0.3)) | |
""" | |
# Apply the algo | |
if P.x[2] > P.x[0] + P.x[1]: | |
return P._Sorted_ArnouxRauzyMulti() | |
else: | |
return P._Sorted_Poincare() | |
cdef class Sorted_Poincare(MCFAlgorithm): | |
cdef int call(self, PairPoint P) except *: | |
r""" | |
EXAMPLES:: | |
sage: from slabbe.mult_cont_frac_pyx import Sorted_Poincare | |
sage: from slabbe.mult_cont_frac_pyx import PairPoint | |
sage: P = PairPoint(3, [.3,.7,.8], [.2,.3,.4]) | |
sage: Sorted_Poincare()(P) # tol | |
((0.1, 0.3, 0.4), (0.4, 0.9, 0.7)) | |
""" | |
# Apply the algo | |
P.x[2] -= P.x[1] | |
P.x[1] -= P.x[0] | |
P.a[1] += P.a[2] | |
P.a[0] += P.a[1] | |
P.sort() | |
return 200 | |
cdef class Sorted_ARrevert(MCFAlgorithm): | |
cdef int call(self, PairPoint P) except *: | |
r""" | |
EXAMPLES:: | |
sage: from slabbe.mult_cont_frac_pyx import Sorted_ARrevert | |
sage: from slabbe.mult_cont_frac_pyx import PairPoint | |
sage: P = PairPoint(3, [.3,.3,.8], [.2,.3,.3]) | |
sage: Sorted_ARrevert()(P) | |
((0.2, 0.3, 0.3), (0.3, 0.5, 0.6)) | |
""" | |
cdef PairPoint R = PairPoint(self.dim) | |