passagemath-symbolics 10.8.1a1__cp311-cp311-macosx_13_0_arm64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- passagemath_symbolics/.dylibs/libgmp.10.dylib +0 -0
- passagemath_symbolics/__init__.py +3 -0
- passagemath_symbolics-10.8.1a1.dist-info/METADATA +186 -0
- passagemath_symbolics-10.8.1a1.dist-info/RECORD +182 -0
- passagemath_symbolics-10.8.1a1.dist-info/WHEEL +6 -0
- passagemath_symbolics-10.8.1a1.dist-info/top_level.txt +3 -0
- sage/all__sagemath_symbolics.py +17 -0
- sage/calculus/all.py +14 -0
- sage/calculus/calculus.py +2838 -0
- sage/calculus/desolvers.py +1864 -0
- sage/calculus/predefined.py +51 -0
- sage/calculus/tests.py +225 -0
- sage/calculus/var.cpython-311-darwin.so +0 -0
- sage/calculus/var.pyx +401 -0
- sage/dynamics/all__sagemath_symbolics.py +6 -0
- sage/dynamics/complex_dynamics/all.py +5 -0
- sage/dynamics/complex_dynamics/mandel_julia.py +765 -0
- sage/dynamics/complex_dynamics/mandel_julia_helper.cpython-311-darwin.so +0 -0
- sage/dynamics/complex_dynamics/mandel_julia_helper.pyx +1034 -0
- sage/ext/all__sagemath_symbolics.py +1 -0
- sage/ext_data/kenzo/CP2.txt +45 -0
- sage/ext_data/kenzo/CP3.txt +349 -0
- sage/ext_data/kenzo/CP4.txt +4774 -0
- sage/ext_data/kenzo/README.txt +49 -0
- sage/ext_data/kenzo/S4.txt +20 -0
- sage/ext_data/magma/latex/latex.m +1021 -0
- sage/ext_data/magma/latex/latex.spec +1 -0
- sage/ext_data/magma/sage/basic.m +356 -0
- sage/ext_data/magma/sage/sage.spec +1 -0
- sage/ext_data/magma/spec +9 -0
- sage/geometry/all__sagemath_symbolics.py +8 -0
- sage/geometry/hyperbolic_space/all.py +5 -0
- sage/geometry/hyperbolic_space/hyperbolic_coercion.py +755 -0
- sage/geometry/hyperbolic_space/hyperbolic_constants.py +5 -0
- sage/geometry/hyperbolic_space/hyperbolic_geodesic.py +2419 -0
- sage/geometry/hyperbolic_space/hyperbolic_interface.py +206 -0
- sage/geometry/hyperbolic_space/hyperbolic_isometry.py +1083 -0
- sage/geometry/hyperbolic_space/hyperbolic_model.py +1502 -0
- sage/geometry/hyperbolic_space/hyperbolic_point.py +621 -0
- sage/geometry/riemannian_manifolds/all.py +7 -0
- sage/geometry/riemannian_manifolds/parametrized_surface3d.py +1632 -0
- sage/geometry/riemannian_manifolds/surface3d_generators.py +461 -0
- sage/interfaces/all__sagemath_symbolics.py +1 -0
- sage/interfaces/magma.py +2991 -0
- sage/interfaces/magma_free.py +90 -0
- sage/interfaces/maple.py +1402 -0
- sage/interfaces/mathematica.py +1345 -0
- sage/interfaces/mathics.py +1312 -0
- sage/interfaces/sympy.py +1398 -0
- sage/interfaces/sympy_wrapper.py +197 -0
- sage/interfaces/tides.py +938 -0
- sage/libs/all__sagemath_symbolics.py +6 -0
- sage/manifolds/all.py +7 -0
- sage/manifolds/calculus_method.py +553 -0
- sage/manifolds/catalog.py +437 -0
- sage/manifolds/chart.py +4010 -0
- sage/manifolds/chart_func.py +3416 -0
- sage/manifolds/continuous_map.py +2183 -0
- sage/manifolds/continuous_map_image.py +155 -0
- sage/manifolds/differentiable/affine_connection.py +2475 -0
- sage/manifolds/differentiable/all.py +1 -0
- sage/manifolds/differentiable/automorphismfield.py +1383 -0
- sage/manifolds/differentiable/automorphismfield_group.py +604 -0
- sage/manifolds/differentiable/bundle_connection.py +1445 -0
- sage/manifolds/differentiable/characteristic_cohomology_class.py +1840 -0
- sage/manifolds/differentiable/chart.py +1241 -0
- sage/manifolds/differentiable/curve.py +1028 -0
- sage/manifolds/differentiable/de_rham_cohomology.py +541 -0
- sage/manifolds/differentiable/degenerate.py +559 -0
- sage/manifolds/differentiable/degenerate_submanifold.py +1668 -0
- sage/manifolds/differentiable/diff_form.py +1660 -0
- sage/manifolds/differentiable/diff_form_module.py +1062 -0
- sage/manifolds/differentiable/diff_map.py +1315 -0
- sage/manifolds/differentiable/differentiable_submanifold.py +291 -0
- sage/manifolds/differentiable/examples/all.py +1 -0
- sage/manifolds/differentiable/examples/euclidean.py +2517 -0
- sage/manifolds/differentiable/examples/real_line.py +897 -0
- sage/manifolds/differentiable/examples/sphere.py +1186 -0
- sage/manifolds/differentiable/examples/symplectic_space.py +187 -0
- sage/manifolds/differentiable/examples/symplectic_space_test.py +40 -0
- sage/manifolds/differentiable/integrated_curve.py +4035 -0
- sage/manifolds/differentiable/levi_civita_connection.py +841 -0
- sage/manifolds/differentiable/manifold.py +4254 -0
- sage/manifolds/differentiable/manifold_homset.py +1826 -0
- sage/manifolds/differentiable/metric.py +3032 -0
- sage/manifolds/differentiable/mixed_form.py +1507 -0
- sage/manifolds/differentiable/mixed_form_algebra.py +559 -0
- sage/manifolds/differentiable/multivector_module.py +800 -0
- sage/manifolds/differentiable/multivectorfield.py +1522 -0
- sage/manifolds/differentiable/poisson_tensor.py +268 -0
- sage/manifolds/differentiable/pseudo_riemannian.py +755 -0
- sage/manifolds/differentiable/pseudo_riemannian_submanifold.py +1839 -0
- sage/manifolds/differentiable/scalarfield.py +1343 -0
- sage/manifolds/differentiable/scalarfield_algebra.py +472 -0
- sage/manifolds/differentiable/symplectic_form.py +912 -0
- sage/manifolds/differentiable/symplectic_form_test.py +220 -0
- sage/manifolds/differentiable/tangent_space.py +412 -0
- sage/manifolds/differentiable/tangent_vector.py +616 -0
- sage/manifolds/differentiable/tensorfield.py +4665 -0
- sage/manifolds/differentiable/tensorfield_module.py +963 -0
- sage/manifolds/differentiable/tensorfield_paral.py +2450 -0
- sage/manifolds/differentiable/tensorfield_paral_test.py +16 -0
- sage/manifolds/differentiable/vector_bundle.py +1725 -0
- sage/manifolds/differentiable/vectorfield.py +1717 -0
- sage/manifolds/differentiable/vectorfield_module.py +2445 -0
- sage/manifolds/differentiable/vectorframe.py +1832 -0
- sage/manifolds/family.py +270 -0
- sage/manifolds/local_frame.py +1490 -0
- sage/manifolds/manifold.py +3090 -0
- sage/manifolds/manifold_homset.py +452 -0
- sage/manifolds/operators.py +359 -0
- sage/manifolds/point.py +994 -0
- sage/manifolds/scalarfield.py +3718 -0
- sage/manifolds/scalarfield_algebra.py +629 -0
- sage/manifolds/section.py +3111 -0
- sage/manifolds/section_module.py +831 -0
- sage/manifolds/structure.py +229 -0
- sage/manifolds/subset.py +2721 -0
- sage/manifolds/subsets/all.py +1 -0
- sage/manifolds/subsets/closure.py +131 -0
- sage/manifolds/subsets/pullback.py +883 -0
- sage/manifolds/topological_submanifold.py +891 -0
- sage/manifolds/trivialization.py +733 -0
- sage/manifolds/utilities.py +1348 -0
- sage/manifolds/vector_bundle.py +1347 -0
- sage/manifolds/vector_bundle_fiber.py +332 -0
- sage/manifolds/vector_bundle_fiber_element.py +111 -0
- sage/matrix/all__sagemath_symbolics.py +1 -0
- sage/matrix/matrix_symbolic_dense.cpython-311-darwin.so +0 -0
- sage/matrix/matrix_symbolic_dense.pxd +6 -0
- sage/matrix/matrix_symbolic_dense.pyx +1030 -0
- sage/matrix/matrix_symbolic_sparse.cpython-311-darwin.so +0 -0
- sage/matrix/matrix_symbolic_sparse.pxd +6 -0
- sage/matrix/matrix_symbolic_sparse.pyx +1038 -0
- sage/modules/all__sagemath_symbolics.py +1 -0
- sage/modules/vector_callable_symbolic_dense.py +105 -0
- sage/modules/vector_symbolic_dense.py +116 -0
- sage/modules/vector_symbolic_sparse.py +118 -0
- sage/rings/all__sagemath_symbolics.py +4 -0
- sage/rings/asymptotic/all.py +6 -0
- sage/rings/asymptotic/asymptotic_expansion_generators.py +1485 -0
- sage/rings/asymptotic/asymptotic_ring.py +4858 -0
- sage/rings/asymptotic/asymptotics_multivariate_generating_functions.py +4106 -0
- sage/rings/asymptotic/growth_group.py +5373 -0
- sage/rings/asymptotic/growth_group_cartesian.py +1400 -0
- sage/rings/asymptotic/term_monoid.py +5205 -0
- sage/rings/function_field/all__sagemath_symbolics.py +2 -0
- sage/rings/polynomial/all__sagemath_symbolics.py +1 -0
- sage/symbolic/all.py +15 -0
- sage/symbolic/assumptions.py +987 -0
- sage/symbolic/benchmark.py +93 -0
- sage/symbolic/callable.py +456 -0
- sage/symbolic/callable.pyi +66 -0
- sage/symbolic/comparison_impl.pyi +38 -0
- sage/symbolic/complexity_measures.py +35 -0
- sage/symbolic/constants.py +1286 -0
- sage/symbolic/constants_c_impl.pyi +10 -0
- sage/symbolic/expression_conversion_algebraic.py +310 -0
- sage/symbolic/expression_conversion_sympy.py +317 -0
- sage/symbolic/expression_conversions.py +1727 -0
- sage/symbolic/function_factory.py +355 -0
- sage/symbolic/function_factory.pyi +41 -0
- sage/symbolic/getitem_impl.pyi +24 -0
- sage/symbolic/integration/all.py +1 -0
- sage/symbolic/integration/external.py +271 -0
- sage/symbolic/integration/integral.py +1075 -0
- sage/symbolic/maxima_wrapper.py +162 -0
- sage/symbolic/operators.py +267 -0
- sage/symbolic/operators.pyi +61 -0
- sage/symbolic/pynac_constant_impl.pyi +13 -0
- sage/symbolic/pynac_function_impl.pyi +8 -0
- sage/symbolic/random_tests.py +461 -0
- sage/symbolic/relation.py +2062 -0
- sage/symbolic/ring.cpython-311-darwin.so +0 -0
- sage/symbolic/ring.pxd +5 -0
- sage/symbolic/ring.pyi +110 -0
- sage/symbolic/ring.pyx +1393 -0
- sage/symbolic/series_impl.pyi +10 -0
- sage/symbolic/subring.py +1025 -0
- sage/symbolic/symengine.py +19 -0
- sage/symbolic/tests.py +40 -0
- sage/symbolic/units.py +1468 -0
|
@@ -0,0 +1,1034 @@
|
|
|
1
|
+
# sage_setup: distribution = sagemath-symbolics
|
|
2
|
+
# sage.doctest: needs sage.plot
|
|
3
|
+
r"""
|
|
4
|
+
Mandelbrot and Julia sets (Cython helper)
|
|
5
|
+
|
|
6
|
+
This is the helper file providing functionality for mandel_julia.py.
|
|
7
|
+
|
|
8
|
+
AUTHORS:
|
|
9
|
+
|
|
10
|
+
- Ben Barros
|
|
11
|
+
"""
|
|
12
|
+
# ****************************************************************************
|
|
13
|
+
# Copyright (C) 2017 BEN BARROS <bbarros@slu.edu>
|
|
14
|
+
#
|
|
15
|
+
# This program is free software: you can redistribute it and/or modify
|
|
16
|
+
# it under the terms of the GNU General Public License as published by
|
|
17
|
+
# the Free Software Foundation, either version 2 of the License, or
|
|
18
|
+
# (at your option) any later version.
|
|
19
|
+
# https://www.gnu.org/licenses/
|
|
20
|
+
# ****************************************************************************
|
|
21
|
+
|
|
22
|
+
from sage.plot.colors import Color
|
|
23
|
+
from sage.repl.image import Image
|
|
24
|
+
from copy import copy
|
|
25
|
+
from cysignals.signals cimport sig_check
|
|
26
|
+
from sage.rings.complex_mpfr import ComplexField
|
|
27
|
+
from sage.functions.log import exp
|
|
28
|
+
from sage.symbolic.relation import solve
|
|
29
|
+
from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
|
|
30
|
+
from sage.rings.cc import CC
|
|
31
|
+
from sage.rings.real_double import RDF
|
|
32
|
+
from sage.rings.complex_double import CDF
|
|
33
|
+
from sage.ext.fast_callable import fast_callable
|
|
34
|
+
from sage.calculus.expr import symbolic_expression
|
|
35
|
+
from sage.symbolic.ring import SR
|
|
36
|
+
from sage.calculus.var import var
|
|
37
|
+
from sage.rings.fraction_field import FractionField_generic
|
|
38
|
+
from sage.categories.function_fields import FunctionFields
|
|
39
|
+
from cypari2.handle_error import PariError
|
|
40
|
+
from math import sqrt
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _color_to_RGB(color):
|
|
44
|
+
"""
|
|
45
|
+
Convert a color to an RGB triple with values in the interval [0,255].
|
|
46
|
+
|
|
47
|
+
EXAMPLES::
|
|
48
|
+
|
|
49
|
+
sage: from sage.dynamics.complex_dynamics.mandel_julia_helper import _color_to_RGB
|
|
50
|
+
sage: _color_to_RGB("aquamarine")
|
|
51
|
+
(127, 255, 212)
|
|
52
|
+
sage: _color_to_RGB(Color([0, 1/2, 1]))
|
|
53
|
+
(0, 127, 255)
|
|
54
|
+
sage: _color_to_RGB([0, 100, 200])
|
|
55
|
+
(0, 100, 200)
|
|
56
|
+
"""
|
|
57
|
+
if not isinstance(color, (list, tuple)):
|
|
58
|
+
color = [int(255.0 * k) for k in Color(color)]
|
|
59
|
+
return tuple(color)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
cpdef fast_mandelbrot_plot(double x_center, double y_center,
|
|
63
|
+
double image_width, long max_iteration,
|
|
64
|
+
long pixel_count,
|
|
65
|
+
long level_sep, long color_num, base_color):
|
|
66
|
+
r"""
|
|
67
|
+
Plot the Mandelbrot set in the complex plane for the map `Q_c(z) = z^2 + c`.
|
|
68
|
+
|
|
69
|
+
ALGORITHM:
|
|
70
|
+
|
|
71
|
+
Let each pixel in the image be a point `c \in \mathbb{C}` and define the
|
|
72
|
+
map `Q_c(z) = z^2 + c`. If `|Q_{c}^{k}(c)| > 2` for some `k \geq 0`, it
|
|
73
|
+
follows that `Q_{c}^{n}(c) \to \infty`. Let `N` be the maximum number of
|
|
74
|
+
iterations. Compute the first `N` points on the orbit of `0` under `Q_c`.
|
|
75
|
+
If for any `k < N`, `|Q_{c}^{k}(0)| > 2`, we stop the iteration and assign
|
|
76
|
+
a color to the point `c` based on how quickly `0` escaped to infinity under
|
|
77
|
+
iteration of `Q_c`. If `|Q_{c}^{i}(0)| \leq 2` for all `i \leq N`, we assume
|
|
78
|
+
`c` is in the Mandelbrot set and assign the point `c` the color black.
|
|
79
|
+
|
|
80
|
+
INPUT:
|
|
81
|
+
|
|
82
|
+
- ``x_center`` -- double; real part of the center point in the complex plane
|
|
83
|
+
|
|
84
|
+
- ``y_center`` -- double; imaginary part of the center point in the complex
|
|
85
|
+
plane
|
|
86
|
+
|
|
87
|
+
- ``image_width`` -- double; width of the image in the complex plane
|
|
88
|
+
|
|
89
|
+
- ``max_iteration`` -- long; maximum number of iterations the map `Q_c(z)`
|
|
90
|
+
considered
|
|
91
|
+
|
|
92
|
+
- ``pixel_count`` -- long; side length of image in number of pixels
|
|
93
|
+
|
|
94
|
+
- ``level_sep`` -- long; number of iterations between each color level
|
|
95
|
+
|
|
96
|
+
- ``color_num`` -- long; number of colors used to plot image
|
|
97
|
+
|
|
98
|
+
- ``base_color`` -- list; RGB color used to determine the coloring of set
|
|
99
|
+
|
|
100
|
+
OUTPUT: 24-bit RGB image of the Mandelbrot set in the complex plane
|
|
101
|
+
|
|
102
|
+
EXAMPLES:
|
|
103
|
+
|
|
104
|
+
Plot the Mandelbrot set with the center point `-1 + 0i`::
|
|
105
|
+
|
|
106
|
+
sage: from sage.dynamics.complex_dynamics.mandel_julia_helper import fast_mandelbrot_plot
|
|
107
|
+
sage: fast_mandelbrot_plot(-1, 0, 4, 500, 600, 1, 20, [40, 40, 40])
|
|
108
|
+
600x600px 24-bit RGB image
|
|
109
|
+
|
|
110
|
+
We can focus on smaller parts of the set by adjusting image_width::
|
|
111
|
+
|
|
112
|
+
sage: from sage.dynamics.complex_dynamics.mandel_julia_helper import fast_mandelbrot_plot
|
|
113
|
+
sage: fast_mandelbrot_plot(-1.11, 0.2283, 1/128, 2000, 500, 1, 500, [40, 100, 100])
|
|
114
|
+
500x500px 24-bit RGB image
|
|
115
|
+
"""
|
|
116
|
+
|
|
117
|
+
cdef:
|
|
118
|
+
M, pixel, color_list
|
|
119
|
+
long i, j, col, row, level, iteration
|
|
120
|
+
double x_corner, y_corner, step_size, x_coor, y_coor, new_x, new_y
|
|
121
|
+
|
|
122
|
+
# Make sure image_width is positive
|
|
123
|
+
image_width = abs(image_width)
|
|
124
|
+
|
|
125
|
+
# Initialize an image to the color black and access the pixels
|
|
126
|
+
M = Image("RGB", (pixel_count, pixel_count), 'black')
|
|
127
|
+
pixel = M.pixels()
|
|
128
|
+
|
|
129
|
+
# Take the given base color and create a list of evenly spaced
|
|
130
|
+
# colors between the given base color and white. The number of
|
|
131
|
+
# colors in the list depends on the variable color_num.
|
|
132
|
+
base_color = _color_to_RGB(base_color)
|
|
133
|
+
color_list = []
|
|
134
|
+
for i in range(color_num):
|
|
135
|
+
sig_check()
|
|
136
|
+
color = [base_color[j] + i * (255 - base_color[j]) // color_num
|
|
137
|
+
for j in range(3)]
|
|
138
|
+
color_list.append(tuple(color))
|
|
139
|
+
|
|
140
|
+
# First, we determine the complex coordinates of the point in the top left
|
|
141
|
+
# corner of the image. Then, we loop through each pixel in the image and
|
|
142
|
+
# assign it complex coordinates relative to the image's top left corner.
|
|
143
|
+
x_corner = x_center - image_width/2
|
|
144
|
+
y_corner = y_center + image_width/2
|
|
145
|
+
step_size = image_width / pixel_count
|
|
146
|
+
for col in range(pixel_count):
|
|
147
|
+
x_coor = x_corner + col*step_size
|
|
148
|
+
for row in range(pixel_count):
|
|
149
|
+
sig_check()
|
|
150
|
+
y_coor = y_corner - row*step_size
|
|
151
|
+
|
|
152
|
+
# We compute the orbit of 0 under the map Q(z) = z^2 + c
|
|
153
|
+
# until we either reach the maximum number of iterations
|
|
154
|
+
# or find a point in the orbit with modulus greater than 2
|
|
155
|
+
new_x, new_y = 0.0, 0.0
|
|
156
|
+
iteration = 0
|
|
157
|
+
while (new_x**2 + new_y**2 <= 4.0 and iteration < max_iteration):
|
|
158
|
+
sig_check()
|
|
159
|
+
new_x, new_y = new_x**2 - new_y**2 + x_coor, \
|
|
160
|
+
2*new_x*new_y + y_coor
|
|
161
|
+
iteration += 1
|
|
162
|
+
|
|
163
|
+
# If the point escapes to infinity, assign the point a color
|
|
164
|
+
# based on how fast it escapes. The more iterations it takes for
|
|
165
|
+
# a point to escape to infinity, the lighter its color will be.
|
|
166
|
+
# Otherwise, assume the point is in the Mandelbrot set and leave
|
|
167
|
+
# it black.
|
|
168
|
+
if iteration != max_iteration:
|
|
169
|
+
# Assign each point a level based on its number of iterations.
|
|
170
|
+
level = iteration // level_sep
|
|
171
|
+
# Assign the pixel a color based on it's level. If we run out
|
|
172
|
+
# of colors, assign it the last color in the list.
|
|
173
|
+
if level < color_num:
|
|
174
|
+
pixel[col, row] = color_list[level]
|
|
175
|
+
else:
|
|
176
|
+
pixel[col, row] = color_list[-1]
|
|
177
|
+
return M
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
cpdef fast_external_ray(double theta, long D=30, long S=10, long R=100,
|
|
181
|
+
long pixel_count=500, double image_width=4,
|
|
182
|
+
long prec=300):
|
|
183
|
+
r"""
|
|
184
|
+
Return a list of points that approximate the external ray for a given angle.
|
|
185
|
+
|
|
186
|
+
INPUT:
|
|
187
|
+
|
|
188
|
+
- ``theta`` -- double; angle between 0 and 1 inclusive
|
|
189
|
+
|
|
190
|
+
- ``D`` -- long (default: ``25``); depth of the approximation.
|
|
191
|
+
As ``D`` increases, the external ray gets closer to the boundary of the
|
|
192
|
+
Mandelbrot set.
|
|
193
|
+
|
|
194
|
+
- ``S`` -- long (default: ``10``); sharpness of the approximation.
|
|
195
|
+
Adjusts the number of points used to approximate the external ray (number
|
|
196
|
+
of points is equal to ``S*D``).
|
|
197
|
+
|
|
198
|
+
- ``R`` -- long (default: ``100``); radial parameter. If ``R`` is
|
|
199
|
+
sufficiently large, the external ray reaches enough close to infinity.
|
|
200
|
+
|
|
201
|
+
- ``pixel_count`` -- long (default: ``500``); side length of image
|
|
202
|
+
in number of pixels
|
|
203
|
+
|
|
204
|
+
- ``image_width`` -- double (default: ``4``); width of the image
|
|
205
|
+
in the complex plane
|
|
206
|
+
|
|
207
|
+
- ``prec`` -- long (default: ``300``); specifies the bits of
|
|
208
|
+
precision used by the Complex Field when using Newton's method to compute
|
|
209
|
+
points on the external ray
|
|
210
|
+
|
|
211
|
+
OUTPUT: list of tuples of Real Interval Field Elements
|
|
212
|
+
|
|
213
|
+
EXAMPLES::
|
|
214
|
+
|
|
215
|
+
sage: from sage.dynamics.complex_dynamics.mandel_julia_helper import fast_external_ray
|
|
216
|
+
sage: fast_external_ray(0,S=1,D=1)
|
|
217
|
+
[(100.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000,
|
|
218
|
+
0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000),
|
|
219
|
+
(9.51254777713729174697578576623132297117784691109499464854806785133621315075854778426714908,
|
|
220
|
+
0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000)]
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
::
|
|
224
|
+
|
|
225
|
+
sage: from sage.dynamics.complex_dynamics.mandel_julia_helper import fast_external_ray
|
|
226
|
+
sage: fast_external_ray(1/3,S=1,D=1)
|
|
227
|
+
[(-49.9999999999999786837179271969944238662719726562500000000000000000000000000000000000000000,
|
|
228
|
+
86.6025403784438765342201804742217063903808593750000000000000000000000000000000000000000000),
|
|
229
|
+
(-5.50628047023173006234970878097113901879832542655926629309001652388544528575532346900138516,
|
|
230
|
+
8.64947510053972513843999918917106032664030380426885745306040284140385975750462108180377187)]
|
|
231
|
+
|
|
232
|
+
::
|
|
233
|
+
|
|
234
|
+
sage: from sage.dynamics.complex_dynamics.mandel_julia_helper import fast_external_ray
|
|
235
|
+
sage: fast_external_ray(0.75234,S=1,D=1)
|
|
236
|
+
[(1.47021239172637052661229972727596759796142578125000000000000000000000000000000000000000000,
|
|
237
|
+
-99.9891917935294287644865107722580432891845703125000000000000000000000000000000000000000000),
|
|
238
|
+
(-0.352790406744857508500937144524776555433184352559852962308757189778284058275081335121601384,
|
|
239
|
+
-9.98646630765023514178761177926164047797465369576787921409326037870837930920646860774032363)]
|
|
240
|
+
"""
|
|
241
|
+
|
|
242
|
+
cdef:
|
|
243
|
+
CF = ComplexField(prec)
|
|
244
|
+
PI = CF.pi()
|
|
245
|
+
I = CF.gen()
|
|
246
|
+
r_m, t_m, temp_c, C_k, D_k, old_c, x, y, dist
|
|
247
|
+
int k, j, t
|
|
248
|
+
double difference, m
|
|
249
|
+
double error = pixel_count * 0.0001
|
|
250
|
+
|
|
251
|
+
double pixel_width = image_width / pixel_count
|
|
252
|
+
|
|
253
|
+
# initialize list with c_0
|
|
254
|
+
c_list = [CF(R*exp(2*PI*I*theta))]
|
|
255
|
+
|
|
256
|
+
# Loop through each subinterval and approximate point on external ray.
|
|
257
|
+
for k in range(1, D+1):
|
|
258
|
+
for j in range(1, S+1):
|
|
259
|
+
m = (k-1)*S + j
|
|
260
|
+
r_m = CF(R**(2**(-m/S)))
|
|
261
|
+
t_m = CF(r_m**(2**k) * exp(2*PI*I*theta * 2**k))
|
|
262
|
+
temp_c = c_list[-1]
|
|
263
|
+
difference = error
|
|
264
|
+
|
|
265
|
+
# Repeat Newton's method until points are close together.
|
|
266
|
+
while error <= difference:
|
|
267
|
+
sig_check()
|
|
268
|
+
old_c = temp_c
|
|
269
|
+
# Recursive formula for iterates of q(z) = z^2 + c
|
|
270
|
+
C_k, D_k = CF(old_c), CF(1)
|
|
271
|
+
for t in range(k):
|
|
272
|
+
C_k, D_k = C_k**2 + old_c, CF(2)*D_k*C_k + CF(1)
|
|
273
|
+
temp_c = old_c - (C_k - t_m) / D_k # Newton map
|
|
274
|
+
difference = abs(old_c) - abs(temp_c)
|
|
275
|
+
|
|
276
|
+
dist = (2*C_k.abs()*(C_k.abs()).log()) / D_k.abs()
|
|
277
|
+
if dist < pixel_width:
|
|
278
|
+
break
|
|
279
|
+
c_list.append(CF(temp_c))
|
|
280
|
+
if dist < pixel_width:
|
|
281
|
+
break
|
|
282
|
+
|
|
283
|
+
# Convert Complex Field elements into tuples.
|
|
284
|
+
for k in range(len(c_list)):
|
|
285
|
+
x, y = c_list[k].real(), c_list[k].imag()
|
|
286
|
+
c_list[k] = (x, y)
|
|
287
|
+
|
|
288
|
+
return c_list
|
|
289
|
+
|
|
290
|
+
cpdef convert_to_pixels(point_list, double x_0, double y_0, double width,
|
|
291
|
+
long number_of_pixels):
|
|
292
|
+
r"""
|
|
293
|
+
Convert cartesian coordinates to pixels within a specified window.
|
|
294
|
+
|
|
295
|
+
INPUT:
|
|
296
|
+
|
|
297
|
+
- ``point_list`` -- list of tuples; points in cartesian coordinates
|
|
298
|
+
|
|
299
|
+
- ``x_0`` -- double; x-coordinate of the center of the image
|
|
300
|
+
|
|
301
|
+
- ``y_0`` -- double; y-coordinate of the center of the image
|
|
302
|
+
|
|
303
|
+
- ``width`` -- double; width of visible window in cartesian coordinates
|
|
304
|
+
|
|
305
|
+
- ``number_of_pixels`` -- long; width of image in pixels
|
|
306
|
+
|
|
307
|
+
OUTPUT: list of tuples of integers representing pixels
|
|
308
|
+
|
|
309
|
+
EXAMPLES::
|
|
310
|
+
|
|
311
|
+
sage: from sage.dynamics.complex_dynamics.mandel_julia_helper import convert_to_pixels
|
|
312
|
+
sage: convert_to_pixels([(-1,3),(0,-4),(5,0)], 0, 0, 12, 100)
|
|
313
|
+
[(42, 25), (50, 83), (92, 50)]
|
|
314
|
+
"""
|
|
315
|
+
cdef:
|
|
316
|
+
k, pixel_list, x_corner, y_corner, step_size
|
|
317
|
+
long x_pixel, y_pixel
|
|
318
|
+
pixel_list = []
|
|
319
|
+
|
|
320
|
+
# Compute top left corner of window and step size
|
|
321
|
+
x_corner = x_0 - width/2
|
|
322
|
+
y_corner = y_0 + width/2
|
|
323
|
+
step_size = number_of_pixels / width
|
|
324
|
+
|
|
325
|
+
# Convert each point in list to pixel coordinates
|
|
326
|
+
for k in point_list:
|
|
327
|
+
sig_check()
|
|
328
|
+
x_pixel = round((k[0] - x_corner) * step_size)
|
|
329
|
+
y_pixel = round((y_corner - k[1]) * step_size)
|
|
330
|
+
pixel_list.append((x_pixel, y_pixel))
|
|
331
|
+
return pixel_list
|
|
332
|
+
|
|
333
|
+
cpdef get_line(start, end):
|
|
334
|
+
r"""
|
|
335
|
+
Produce a list of pixel coordinates approximating a line from a starting
|
|
336
|
+
point to an ending point using the Bresenham's Line Algorithm.
|
|
337
|
+
|
|
338
|
+
REFERENCE:
|
|
339
|
+
|
|
340
|
+
[Br2016]_
|
|
341
|
+
|
|
342
|
+
INPUT:
|
|
343
|
+
|
|
344
|
+
- ``start`` -- tuple; starting point of line
|
|
345
|
+
|
|
346
|
+
- ``end`` -- tuple; ending point of line
|
|
347
|
+
|
|
348
|
+
OUTPUT: list of tuples of integers approximating the line between two pixels
|
|
349
|
+
|
|
350
|
+
EXAMPLES::
|
|
351
|
+
|
|
352
|
+
sage: from sage.dynamics.complex_dynamics.mandel_julia_helper import get_line
|
|
353
|
+
sage: get_line((0, 0), (3, 4))
|
|
354
|
+
[(0, 0), (1, 1), (1, 2), (2, 3), (3, 4)]
|
|
355
|
+
|
|
356
|
+
::
|
|
357
|
+
|
|
358
|
+
sage: from sage.dynamics.complex_dynamics.mandel_julia_helper import get_line
|
|
359
|
+
sage: get_line((3, 4), (0, 0))
|
|
360
|
+
[(3, 4), (2, 3), (1, 2), (1, 1), (0, 0)]
|
|
361
|
+
"""
|
|
362
|
+
# Setup initial conditions
|
|
363
|
+
cdef:
|
|
364
|
+
long x1, x2, y1, y2, dx, dy, error, ystep, y
|
|
365
|
+
is_steep, swapped, points
|
|
366
|
+
x1, y1 = start
|
|
367
|
+
x2, y2 = end
|
|
368
|
+
dx, dy = x2 - x1, y2 - y1
|
|
369
|
+
|
|
370
|
+
# Determine how steep the line is
|
|
371
|
+
is_steep = abs(dy) > abs(dx)
|
|
372
|
+
|
|
373
|
+
# Rotate line
|
|
374
|
+
if is_steep:
|
|
375
|
+
x1, y1 = y1, x1
|
|
376
|
+
x2, y2 = y2, x2
|
|
377
|
+
|
|
378
|
+
# Swap start and end points if necessary and store swap state
|
|
379
|
+
swapped = False
|
|
380
|
+
if x1 > x2:
|
|
381
|
+
x1, x2 = x2, x1
|
|
382
|
+
y1, y2 = y2, y1
|
|
383
|
+
swapped = True
|
|
384
|
+
|
|
385
|
+
# Recalculate differentials
|
|
386
|
+
dx, dy = x2 - x1, y2 - y1
|
|
387
|
+
|
|
388
|
+
# Calculate error
|
|
389
|
+
error = int(dx / 2.0)
|
|
390
|
+
ystep = 1 if y1 < y2 else -1
|
|
391
|
+
|
|
392
|
+
# Iterate over bounding box generating points between start and end
|
|
393
|
+
y = y1
|
|
394
|
+
points = []
|
|
395
|
+
for x in range(x1, x2 + 1):
|
|
396
|
+
sig_check()
|
|
397
|
+
coord = (y, x) if is_steep else (x, y)
|
|
398
|
+
points.append(coord)
|
|
399
|
+
error -= abs(dy)
|
|
400
|
+
if error < 0:
|
|
401
|
+
y += ystep
|
|
402
|
+
error += dx
|
|
403
|
+
|
|
404
|
+
# Reverse the list if the coordinates were swapped
|
|
405
|
+
if swapped:
|
|
406
|
+
points.reverse()
|
|
407
|
+
return points
|
|
408
|
+
|
|
409
|
+
# Commented out temporarily for safekeeping, but probably should be deleted
|
|
410
|
+
# def fast_julia_plot(double c_real, double c_imag,
|
|
411
|
+
# double x_center, double y_center, double image_width,
|
|
412
|
+
# long max_iteration, long pixel_count, long level_sep,
|
|
413
|
+
# long color_num, base_color):
|
|
414
|
+
|
|
415
|
+
cpdef fast_julia_plot(double c_real, double c_imag,
|
|
416
|
+
double x_center=0, double y_center=0,
|
|
417
|
+
double image_width=4,
|
|
418
|
+
long max_iteration=500, long pixel_count=500,
|
|
419
|
+
long level_sep=2,
|
|
420
|
+
long color_num=40, base_color=[50, 50, 50]):
|
|
421
|
+
r"""
|
|
422
|
+
Plot the Julia set for a given `c` value in the complex plane for the map `Q_c(z) = z^2 + c`.
|
|
423
|
+
|
|
424
|
+
INPUT:
|
|
425
|
+
|
|
426
|
+
- ``c_real`` -- double; Real part of `c` value that determines Julia set
|
|
427
|
+
|
|
428
|
+
- ``c_imag`` -- double; Imaginary part of `c` value that determines Julia
|
|
429
|
+
set
|
|
430
|
+
|
|
431
|
+
- ``x_center`` -- double (default: ``0.0``); real part of center
|
|
432
|
+
point
|
|
433
|
+
|
|
434
|
+
- ``y_center`` -- double (default: ``0.0``); imaginary part of
|
|
435
|
+
center point
|
|
436
|
+
|
|
437
|
+
- ``image_width`` -- double (default: ``4.0``); width of image
|
|
438
|
+
in the complex plane
|
|
439
|
+
|
|
440
|
+
- ``max_iteration`` -- long (default: ``500``); maximum number of
|
|
441
|
+
iterations the map ``Q_c(z)``
|
|
442
|
+
|
|
443
|
+
- ``pixel_count`` -- long (default: ``500``); side length of
|
|
444
|
+
image in number of pixels
|
|
445
|
+
|
|
446
|
+
- ``level_sep`` -- long (default: ``2``); number of iterations
|
|
447
|
+
between each color level
|
|
448
|
+
|
|
449
|
+
- ``color_num`` -- long (default: ``40``); number of colors used
|
|
450
|
+
to plot image
|
|
451
|
+
|
|
452
|
+
- ``base_color`` -- RGB color (default: ``[50, 50, 50]``); color
|
|
453
|
+
used to determine the coloring of set
|
|
454
|
+
|
|
455
|
+
OUTPUT: 24-bit RGB image of the Julia set in the complex plane
|
|
456
|
+
|
|
457
|
+
EXAMPLES:
|
|
458
|
+
|
|
459
|
+
Plot the Julia set for `c=-1+0i`::
|
|
460
|
+
|
|
461
|
+
sage: from sage.dynamics.complex_dynamics.mandel_julia_helper import fast_julia_plot
|
|
462
|
+
sage: fast_julia_plot(-1, 0, 0, 0, 4, 500, 200, 1, 20, [40, 40, 40])
|
|
463
|
+
200x200px 24-bit RGB image
|
|
464
|
+
"""
|
|
465
|
+
|
|
466
|
+
cdef:
|
|
467
|
+
color_list
|
|
468
|
+
long i, j, col, row, level, iteration
|
|
469
|
+
double x_corner, y_corner, step_size, x_coor, y_coor, new_x, new_y, escape_radius_squared
|
|
470
|
+
|
|
471
|
+
# Make sure image_width is positive
|
|
472
|
+
image_width = abs(image_width)
|
|
473
|
+
|
|
474
|
+
# Calculate the escape radius. (Actually, we store the square of this radius.) If any
|
|
475
|
+
# iterate is farther from the origin than this distance, then the orbit goes to infinity.
|
|
476
|
+
escape_radius_squared = ((1.0 + sqrt(1.0 + 4.0*sqrt(c_real**2 + c_imag**2))))**2/4.0
|
|
477
|
+
|
|
478
|
+
# Initialize an image to the color black and access the pixels
|
|
479
|
+
J = Image("RGB", (pixel_count, pixel_count), 'black')
|
|
480
|
+
Jp = J.pixels()
|
|
481
|
+
|
|
482
|
+
# Take the given base color and create a list of evenly spaced
|
|
483
|
+
# colors between the given base color and white. The number of
|
|
484
|
+
# colors in the list depends on the variable color_num.
|
|
485
|
+
base_color = _color_to_RGB(base_color)
|
|
486
|
+
color_list = []
|
|
487
|
+
for i in range(color_num):
|
|
488
|
+
sig_check()
|
|
489
|
+
color = [base_color[j] + i * (255 - base_color[j]) // color_num
|
|
490
|
+
for j in range(3)]
|
|
491
|
+
color_list.append(tuple(color))
|
|
492
|
+
|
|
493
|
+
# First, we determine the complex coordinates of the point in the top left
|
|
494
|
+
# corner of the image. Then, we loop through each pixel in the image and
|
|
495
|
+
# assign it complex coordinates relative to the image's top left corner.
|
|
496
|
+
x_corner = x_center - image_width/2
|
|
497
|
+
y_corner = y_center + image_width/2
|
|
498
|
+
step_size = image_width / pixel_count
|
|
499
|
+
for col in range(pixel_count):
|
|
500
|
+
x_coor = x_corner + col*step_size
|
|
501
|
+
for row in range(pixel_count):
|
|
502
|
+
sig_check()
|
|
503
|
+
y_coor = y_corner - row*step_size
|
|
504
|
+
|
|
505
|
+
# We compute the orbit of each pixel under the map Q(z) = z^2 + c
|
|
506
|
+
# until we either reach the maximum number of iterations
|
|
507
|
+
# or find a point in the orbit with modulus greater than
|
|
508
|
+
# the escape radius.
|
|
509
|
+
new_x, new_y = x_coor, y_coor
|
|
510
|
+
iteration = 0
|
|
511
|
+
while (new_x**2 + new_y**2 <= escape_radius_squared and iteration < max_iteration):
|
|
512
|
+
sig_check()
|
|
513
|
+
new_x, new_y = new_x**2 - new_y**2 + c_real, \
|
|
514
|
+
2*new_x*new_y + c_imag
|
|
515
|
+
iteration += 1
|
|
516
|
+
|
|
517
|
+
# If the point escapes to infinity, assign the point a color
|
|
518
|
+
# based on how fast it escapes. The more iterations it takes for
|
|
519
|
+
# a point to escape to infinity, the lighter its color will be.
|
|
520
|
+
# Otherwise, assume the point is in the Julia set and leave
|
|
521
|
+
# it black.
|
|
522
|
+
if iteration != max_iteration:
|
|
523
|
+
# Assign each point a level based on its number of iterations.
|
|
524
|
+
level = iteration // level_sep
|
|
525
|
+
# Assign the pixel a color based on it's level. If we run out
|
|
526
|
+
# of colors, assign it the last color in the list.
|
|
527
|
+
if level < color_num:
|
|
528
|
+
Jp[col, row] = color_list[level]
|
|
529
|
+
else:
|
|
530
|
+
Jp[col, row] = color_list[-1]
|
|
531
|
+
|
|
532
|
+
return J
|
|
533
|
+
|
|
534
|
+
cpdef julia_helper(double c_real, double c_imag, double x_center=0,
|
|
535
|
+
double y_center=0, double image_width=4,
|
|
536
|
+
long max_iteration=500,
|
|
537
|
+
long pixel_count=500, long level_sep=2, long color_num=40,
|
|
538
|
+
base_color=[50, 50, 50], point_color=[255, 0, 0]):
|
|
539
|
+
r"""
|
|
540
|
+
Helper function that returns the image of a Julia set for a given
|
|
541
|
+
`c` value side by side with the Mandelbrot set with a point denoting
|
|
542
|
+
the `c` value.
|
|
543
|
+
|
|
544
|
+
INPUT:
|
|
545
|
+
|
|
546
|
+
- ``c_real`` -- double; Real part of `c` value that determines Julia set
|
|
547
|
+
|
|
548
|
+
- ``c_imag`` -- double; Imaginary part of `c` value that determines Julia
|
|
549
|
+
set
|
|
550
|
+
|
|
551
|
+
- ``x_center`` -- double (default: ``0.0``); Real part of center
|
|
552
|
+
point
|
|
553
|
+
|
|
554
|
+
- ``y_center`` -- double (default: ``0.0``); Imaginary part of
|
|
555
|
+
center point
|
|
556
|
+
|
|
557
|
+
- ``image_width`` -- double (default: ``4.0``); width of image in
|
|
558
|
+
the complex plane
|
|
559
|
+
|
|
560
|
+
- ``max_iteration`` -- long (default: ``500``); maximum number of
|
|
561
|
+
iterations the map ``Q_c(z)``
|
|
562
|
+
|
|
563
|
+
- ``pixel_count`` -- long (default: ``500``); side length of
|
|
564
|
+
image in number of pixels
|
|
565
|
+
|
|
566
|
+
- ``level_sep`` -- long (default: ``2``); number of iterations
|
|
567
|
+
between each color level
|
|
568
|
+
|
|
569
|
+
- ``color_num`` -- long (default: ``40``); number of colors used
|
|
570
|
+
to plot image
|
|
571
|
+
|
|
572
|
+
- ``base_color`` -- RGB color (default: ``[50, 50, 50]``); color
|
|
573
|
+
used to determine the coloring of set
|
|
574
|
+
|
|
575
|
+
- ``point_color`` -- RGB color (default: ``[255, 0, 0]``); color
|
|
576
|
+
of the point `c` in the Mandelbrot set
|
|
577
|
+
|
|
578
|
+
OUTPUT: 24-bit RGB image of the Julia and Mandelbrot sets in the complex plane
|
|
579
|
+
|
|
580
|
+
EXAMPLES:
|
|
581
|
+
|
|
582
|
+
Plot the Julia set for `c=-1+0i`::
|
|
583
|
+
|
|
584
|
+
sage: from sage.dynamics.complex_dynamics.mandel_julia_helper import julia_helper
|
|
585
|
+
sage: julia_helper(-1, 0, 0, 0, 4, 500, 200, 1, 20, [40, 40, 40], [255, 0, 0])
|
|
586
|
+
401x200px 24-bit RGB image
|
|
587
|
+
"""
|
|
588
|
+
cdef int i, j
|
|
589
|
+
|
|
590
|
+
# Initialize the Julia set
|
|
591
|
+
J = fast_julia_plot(c_real, c_imag, x_center, y_center, image_width,
|
|
592
|
+
max_iteration, pixel_count, level_sep,
|
|
593
|
+
color_num, base_color)
|
|
594
|
+
Jp = J.pixels()
|
|
595
|
+
|
|
596
|
+
# Initialize the image with Julia set on left side
|
|
597
|
+
# Add white border between images
|
|
598
|
+
G = Image("RGB", (2*pixel_count+1, pixel_count), 'white')
|
|
599
|
+
Gp = G.pixels()
|
|
600
|
+
for i in range(pixel_count):
|
|
601
|
+
for j in range(pixel_count):
|
|
602
|
+
Gp[i, j] = Jp[i, j]
|
|
603
|
+
|
|
604
|
+
# Plot the Mandelbrot set on the right side
|
|
605
|
+
M = fast_mandelbrot_plot(-1, 0, 4, 500, pixel_count, 1, 30, base_color)
|
|
606
|
+
Mp = M.pixels()
|
|
607
|
+
for i in range(pixel_count+1, 2*pixel_count):
|
|
608
|
+
for j in range(pixel_count):
|
|
609
|
+
Gp[i, j] = Mp[int(i-pixel_count), j]
|
|
610
|
+
|
|
611
|
+
point_color = _color_to_RGB(point_color)
|
|
612
|
+
|
|
613
|
+
# Add a cross representing c-value to the Mandelbrot set.
|
|
614
|
+
CP = convert_to_pixels([(c_real, c_imag)], -1, 0, 4, pixel_count)
|
|
615
|
+
for i in range(-3, 4):
|
|
616
|
+
# Loop through x and y coordinates and check if they are in image
|
|
617
|
+
if min(CP[0][0]+i, CP[0][1]) >= 0 and \
|
|
618
|
+
max(CP[0][0]+i, CP[0][1]) < pixel_count:
|
|
619
|
+
Gp[CP[0][0]+i+pixel_count+1, CP[0][1]] = tuple(point_color)
|
|
620
|
+
if min(CP[0][0], CP[0][1]+i) >= 0 and \
|
|
621
|
+
max(CP[0][0], CP[0][1]+i) < pixel_count:
|
|
622
|
+
Gp[CP[0][0]+pixel_count+1, CP[0][1]+i] = tuple(point_color)
|
|
623
|
+
|
|
624
|
+
return G
|
|
625
|
+
|
|
626
|
+
cpdef polynomial_mandelbrot(f, parameter=None, double x_center=0,
|
|
627
|
+
double y_center=0, image_width=4,
|
|
628
|
+
int max_iteration=50, int pixel_count=500,
|
|
629
|
+
int level_sep=1, int color_num=30,
|
|
630
|
+
base_color=Color('red')):
|
|
631
|
+
r"""
|
|
632
|
+
Plot the Mandelbrot set in the complex plane for a family of polynomial maps.
|
|
633
|
+
|
|
634
|
+
INPUT:
|
|
635
|
+
|
|
636
|
+
- ``f`` -- a one parameter family of polynomial maps defined over the
|
|
637
|
+
multivariate polynomial ring in z, c over the Complex field
|
|
638
|
+
|
|
639
|
+
- ``parameter`` -- designates which variable is used as the parameter
|
|
640
|
+
If no parameter is provided, ``c`` will be used as the parameter
|
|
641
|
+
|
|
642
|
+
- ``x_center`` -- double, real part of the center point in the complex plane
|
|
643
|
+
|
|
644
|
+
- ``y_center`` -- double, imaginary part of the center point in the complex
|
|
645
|
+
plane
|
|
646
|
+
|
|
647
|
+
- ``image_width`` -- double, width of the image in the complex plane
|
|
648
|
+
|
|
649
|
+
- ``max_iteration`` -- long, maximum number of iterations the map `f(z)`
|
|
650
|
+
considered
|
|
651
|
+
|
|
652
|
+
- ``pixel_count`` -- long, side length of image in number of pixels
|
|
653
|
+
|
|
654
|
+
- ``level_sep`` -- long, number of iterations between each color level
|
|
655
|
+
|
|
656
|
+
- ``color_num`` -- long, number of colors used to plot image
|
|
657
|
+
|
|
658
|
+
- ``base_color`` -- list; RGB color used to determine the coloring of set
|
|
659
|
+
|
|
660
|
+
OUTPUT: 24-bit RGB image of a Mandelbrot set in the complex plane
|
|
661
|
+
|
|
662
|
+
EXAMPLES::
|
|
663
|
+
|
|
664
|
+
sage: from sage.dynamics.complex_dynamics.mandel_julia_helper import polynomial_mandelbrot
|
|
665
|
+
sage: R.<z,c> = CC[]
|
|
666
|
+
sage: f = z^5 + c
|
|
667
|
+
sage: polynomial_mandelbrot(f, pixel_count=100)
|
|
668
|
+
100x100px 24-bit RGB image
|
|
669
|
+
|
|
670
|
+
::
|
|
671
|
+
|
|
672
|
+
sage: from sage.dynamics.complex_dynamics.mandel_julia_helper import polynomial_mandelbrot
|
|
673
|
+
sage: B.<c> = CC[]
|
|
674
|
+
sage: R.<z> = B[]
|
|
675
|
+
sage: f = z^4 - z + c
|
|
676
|
+
sage: polynomial_mandelbrot(f, pixel_count=100)
|
|
677
|
+
100x100px 24-bit RGB image
|
|
678
|
+
|
|
679
|
+
::
|
|
680
|
+
|
|
681
|
+
sage: from sage.dynamics.complex_dynamics.mandel_julia_helper import polynomial_mandelbrot
|
|
682
|
+
sage: B.<c> = CC[]
|
|
683
|
+
sage: R.<z> = B[]
|
|
684
|
+
sage: f = z^2*(z-c) + c
|
|
685
|
+
sage: polynomial_mandelbrot(f, pixel_count=100)
|
|
686
|
+
100x100px 24-bit RGB image
|
|
687
|
+
"""
|
|
688
|
+
|
|
689
|
+
cdef:
|
|
690
|
+
z, c, x, y, w, cr, ci, J, M, S, S2, R, P, phi, t, im, re, a_n, df
|
|
691
|
+
pt, c_pts, cp_real, cp_imag, pixel, color_list, critical_pts
|
|
692
|
+
f_real, f_imag, f_temp, escape_time, cf_list, cf
|
|
693
|
+
int i, j, d, k, col, row, iteration
|
|
694
|
+
double C, L, Rad, x_corner, y_corner, x_coor, y_coor, \
|
|
695
|
+
step_size, new_x, new_y
|
|
696
|
+
I = CDF.gen()
|
|
697
|
+
constant_c = True
|
|
698
|
+
|
|
699
|
+
if parameter is None:
|
|
700
|
+
c = var('c')
|
|
701
|
+
parameter = c
|
|
702
|
+
|
|
703
|
+
P = f.parent()
|
|
704
|
+
|
|
705
|
+
if P.base_ring() is CC:
|
|
706
|
+
if isinstance(P, FractionField_generic):
|
|
707
|
+
raise NotImplementedError("coefficients must be polynomials in the parameter")
|
|
708
|
+
gen_list = list(P.gens())
|
|
709
|
+
parameter = gen_list.pop(gen_list.index(parameter))
|
|
710
|
+
variable = gen_list.pop()
|
|
711
|
+
|
|
712
|
+
elif P.base_ring().base_ring() is CC:
|
|
713
|
+
if isinstance(P.base_ring(), FractionField_generic):
|
|
714
|
+
raise NotImplementedError("coefficients must be polynomials in the parameter")
|
|
715
|
+
phi = P.flattening_morphism()
|
|
716
|
+
f = phi(f)
|
|
717
|
+
gen_list = list(f.parent().gens())
|
|
718
|
+
parameter = gen_list.pop(gen_list.index(parameter))
|
|
719
|
+
variable = gen_list.pop()
|
|
720
|
+
|
|
721
|
+
elif P.base_ring() in FunctionFields():
|
|
722
|
+
raise NotImplementedError("coefficients must be polynomials in the parameter")
|
|
723
|
+
|
|
724
|
+
else:
|
|
725
|
+
raise ValueError("base ring must be a complex field")
|
|
726
|
+
|
|
727
|
+
# Make sure image_width is positive
|
|
728
|
+
image_width = abs(image_width)
|
|
729
|
+
|
|
730
|
+
# Initialize an image to the color black and access the pixels
|
|
731
|
+
M = Image("RGB", (pixel_count, pixel_count), 'black')
|
|
732
|
+
pixel = M.pixels()
|
|
733
|
+
|
|
734
|
+
# Take the given base color and create a list of evenly spaced
|
|
735
|
+
# colors between the given base color and white. The number of
|
|
736
|
+
# colors in the list depends on the variable color_num.
|
|
737
|
+
if isinstance(base_color, Color):
|
|
738
|
+
# Convert Color to RGB list
|
|
739
|
+
base_color = [int(k*255) for k in base_color]
|
|
740
|
+
color_list = []
|
|
741
|
+
for i in range(color_num):
|
|
742
|
+
sig_check()
|
|
743
|
+
color_list.append(copy(base_color))
|
|
744
|
+
for j in range(3):
|
|
745
|
+
color_list[i][j] += i * (255 - color_list[i][j]) // color_num
|
|
746
|
+
color_list[i] = tuple(color_list[i])
|
|
747
|
+
|
|
748
|
+
# Split function into real and imaginary parts
|
|
749
|
+
R = PolynomialRing(CC, [variable, parameter])
|
|
750
|
+
if len(R.gens()) > 2:
|
|
751
|
+
raise NotImplementedError("base ring must have only 2 variables")
|
|
752
|
+
z, c = R.gens()
|
|
753
|
+
f = R(str(f))
|
|
754
|
+
S = PolynomialRing(f.base_ring(), 'x,y,J,cr,ci')
|
|
755
|
+
x, y, J, cr, ci = S.gens()
|
|
756
|
+
S2 = S.quotient_ring(J**2+1)
|
|
757
|
+
phi = R.hom([x+y*J, cr+ci*J], S2)
|
|
758
|
+
t = phi(f).lift()
|
|
759
|
+
im = t.coefficient(J)
|
|
760
|
+
re = t - im*J
|
|
761
|
+
|
|
762
|
+
f_real = fast_callable(re, vars=[x, y, cr, ci, J], domain=RDF)
|
|
763
|
+
f_imag = fast_callable(im, vars=[x, y, cr, ci, J], domain=RDF)
|
|
764
|
+
|
|
765
|
+
# Compute critical points
|
|
766
|
+
try:
|
|
767
|
+
df = f.derivative(z).univariate_polynomial()
|
|
768
|
+
critical_pts = df.roots(multiplicities=False)
|
|
769
|
+
constant_c = True
|
|
770
|
+
except (PariError, TypeError):
|
|
771
|
+
constant_c = False
|
|
772
|
+
|
|
773
|
+
# If c is in the constant term of the polynomial, then the critical points
|
|
774
|
+
# will be independent of c.
|
|
775
|
+
if constant_c:
|
|
776
|
+
c_pts = [[pt.real(), pt.imag()] for pt in critical_pts]
|
|
777
|
+
|
|
778
|
+
# Calculate escape condition
|
|
779
|
+
d = f.degree(z)
|
|
780
|
+
cf_list = f.coefficients()
|
|
781
|
+
a_n = cf_list.pop(0).abs()
|
|
782
|
+
C = 0
|
|
783
|
+
for cf in cf_list:
|
|
784
|
+
C += cf.abs()
|
|
785
|
+
L = 1.00000000000001
|
|
786
|
+
if d >= 2:
|
|
787
|
+
Rad = max(1, 2*C / a_n, (2*L / a_n**(1/(d-1))))
|
|
788
|
+
else:
|
|
789
|
+
Rad = max(1, 2*C / a_n)
|
|
790
|
+
|
|
791
|
+
# First, we determine the complex coordinates of the point in the top
|
|
792
|
+
# left corner of the image. Then, we loop through each pixel in the
|
|
793
|
+
# image and assign it complex coordinates relative to the image's top
|
|
794
|
+
# left corner.
|
|
795
|
+
x_corner = x_center - image_width/2
|
|
796
|
+
y_corner = y_center + image_width/2
|
|
797
|
+
step_size = image_width*1.0 / pixel_count
|
|
798
|
+
for col in range(pixel_count):
|
|
799
|
+
x_coor = x_corner + col*step_size
|
|
800
|
+
for row in range(pixel_count):
|
|
801
|
+
sig_check()
|
|
802
|
+
y_coor = y_corner - row*step_size
|
|
803
|
+
|
|
804
|
+
# Initialize escape_time to be maximum number of iterations.
|
|
805
|
+
escape_time = max_iteration
|
|
806
|
+
|
|
807
|
+
# Loop though each critical point. If all of the critical
|
|
808
|
+
# points are bounded for a particular c value, then c is in
|
|
809
|
+
# the mandelbrot set.
|
|
810
|
+
for pt in c_pts:
|
|
811
|
+
cp_real = pt[0]
|
|
812
|
+
cp_imag = pt[1]
|
|
813
|
+
|
|
814
|
+
# Iterate the map f using the critical point as the initial
|
|
815
|
+
# value.
|
|
816
|
+
new_x, new_y = cp_real, cp_imag
|
|
817
|
+
iteration = 0
|
|
818
|
+
while new_x**2 + new_y**2 <= Rad**2 and iteration < escape_time:
|
|
819
|
+
sig_check()
|
|
820
|
+
new_x, new_y = f_real(new_x, new_y, x_coor, y_coor, 1), \
|
|
821
|
+
f_imag(new_x, new_y, x_coor, y_coor, 1)
|
|
822
|
+
iteration += 1
|
|
823
|
+
|
|
824
|
+
# For each point, we take the minimum number of iterations
|
|
825
|
+
# over all the critical points and use this minimum to
|
|
826
|
+
# color the point.
|
|
827
|
+
if iteration < escape_time:
|
|
828
|
+
escape_time = iteration
|
|
829
|
+
|
|
830
|
+
# If the point escapes to infinity, assign the point a color
|
|
831
|
+
# based on how fast it escapes. The more iterations it takes for
|
|
832
|
+
# a point to escape to infinity, the lighter its color will be.
|
|
833
|
+
# Otherwise, assume the point is in the Mandelbrot set and leave
|
|
834
|
+
# it black.
|
|
835
|
+
if iteration != max_iteration:
|
|
836
|
+
# Assign each point a level based on its number of iterations.
|
|
837
|
+
level = iteration // level_sep
|
|
838
|
+
# Assign the pixel a color based on it's level. If we run out
|
|
839
|
+
# of colors, assign it the last color in the list.
|
|
840
|
+
if level < color_num:
|
|
841
|
+
pixel[col, row] = color_list[level]
|
|
842
|
+
else:
|
|
843
|
+
pixel[col, row] = color_list[-1]
|
|
844
|
+
|
|
845
|
+
# If the critical points of f depend on c, we must compute the different
|
|
846
|
+
# critical points for each c.
|
|
847
|
+
else:
|
|
848
|
+
# Solve for critical points symbollically.
|
|
849
|
+
with SR.temp_var() as w:
|
|
850
|
+
df = f.derivative(z).polynomial(z).subs({z: w})
|
|
851
|
+
critical_pts = solve(symbolic_expression(df) == 0, w)
|
|
852
|
+
c_pts = []
|
|
853
|
+
for pt in critical_pts:
|
|
854
|
+
c_pts.append(fast_callable(pt.rhs(), vars=[c], domain=CDF))
|
|
855
|
+
|
|
856
|
+
# Calculate degree of f
|
|
857
|
+
d = f.degree(z)
|
|
858
|
+
|
|
859
|
+
# First, we determine the complex coordinates of the point in the top
|
|
860
|
+
# left corner of the image. Then, we loop through each pixel in the
|
|
861
|
+
# image and assign it complex coordinates relative to the image's top
|
|
862
|
+
# left corner.
|
|
863
|
+
x_corner = x_center - image_width/2
|
|
864
|
+
y_corner = y_center + image_width/2
|
|
865
|
+
step_size = image_width*1.0 / pixel_count
|
|
866
|
+
for col in range(pixel_count):
|
|
867
|
+
x_coor = x_corner + col*step_size
|
|
868
|
+
for row in range(pixel_count):
|
|
869
|
+
|
|
870
|
+
sig_check()
|
|
871
|
+
y_coor = y_corner - row*step_size
|
|
872
|
+
|
|
873
|
+
# Initialize escape time to be maximum number of iterations
|
|
874
|
+
escape_time = max_iteration
|
|
875
|
+
|
|
876
|
+
# Calculate escape condition for each c value
|
|
877
|
+
f_temp = f.subs({c: x_coor+y_coor*I})
|
|
878
|
+
cf_list = f_temp.coefficients()
|
|
879
|
+
a_n = cf_list.pop(0).abs()
|
|
880
|
+
C = 0
|
|
881
|
+
for cf in cf_list:
|
|
882
|
+
C += cf.abs()
|
|
883
|
+
L = 1.00000000000001
|
|
884
|
+
if d >= 2:
|
|
885
|
+
Rad = max(1, 2*C / a_n, (2*L / a_n**(1/(d-1))))
|
|
886
|
+
else:
|
|
887
|
+
Rad = max(1, 2*C / a_n)
|
|
888
|
+
|
|
889
|
+
for f_cp in c_pts:
|
|
890
|
+
|
|
891
|
+
# Compute real and imaginary critical point
|
|
892
|
+
cp_real = f_cp(x_coor + y_coor*I).real()
|
|
893
|
+
cp_imag = f_cp(x_coor + y_coor*I).imag()
|
|
894
|
+
|
|
895
|
+
# Iterate the map f using the critical point as the initial
|
|
896
|
+
# value.
|
|
897
|
+
new_x, new_y = cp_real, cp_imag
|
|
898
|
+
iteration = 0
|
|
899
|
+
while new_x**2 + new_y**2 <= Rad**2 and iteration < escape_time:
|
|
900
|
+
sig_check()
|
|
901
|
+
new_x, new_y = f_real(new_x, new_y, x_coor, y_coor, 1), \
|
|
902
|
+
f_imag(new_x, new_y, x_coor, y_coor, 1)
|
|
903
|
+
iteration += 1
|
|
904
|
+
|
|
905
|
+
# For each point, we take the minimum number of iterations
|
|
906
|
+
# over all the critical points and use this minimum to
|
|
907
|
+
# color the point.
|
|
908
|
+
if iteration < escape_time:
|
|
909
|
+
escape_time = iteration
|
|
910
|
+
|
|
911
|
+
# If the point escapes to infinity, assign the point a color
|
|
912
|
+
# based on how fast it escapes. The more iterations it takes for
|
|
913
|
+
# a point to escape to infinity, the lighter its color will be.
|
|
914
|
+
# Otherwise, assume the point is in the Mandelbrot set and leave
|
|
915
|
+
# it black.
|
|
916
|
+
if iteration != max_iteration:
|
|
917
|
+
# Assign each point a level based on its number of iterations.
|
|
918
|
+
level = iteration // level_sep
|
|
919
|
+
# Assign the pixel a color based on it's level. If we run out
|
|
920
|
+
# of colors, assign it the last color in the list.
|
|
921
|
+
if level < color_num:
|
|
922
|
+
pixel[col, row] = color_list[level]
|
|
923
|
+
else:
|
|
924
|
+
pixel[col, row] = color_list[-1]
|
|
925
|
+
return M
|
|
926
|
+
|
|
927
|
+
cpdef general_julia(f, double x_center=0, double y_center=0, image_width=4,
|
|
928
|
+
int max_iteration=50, int pixel_count=500,
|
|
929
|
+
int level_sep=1, int color_num=30,
|
|
930
|
+
base_color=[50, 50, 50]):
|
|
931
|
+
r"""
|
|
932
|
+
Plot Julia sets for general polynomials.
|
|
933
|
+
|
|
934
|
+
EXAMPLES::
|
|
935
|
+
|
|
936
|
+
sage: from sage.dynamics.complex_dynamics.mandel_julia_helper import general_julia
|
|
937
|
+
sage: from sage.plot.colors import Color
|
|
938
|
+
sage: R.<z> = CC[]
|
|
939
|
+
sage: f = z^3 - z + 1
|
|
940
|
+
sage: general_julia(f)
|
|
941
|
+
500x500px 24-bit RGB image
|
|
942
|
+
|
|
943
|
+
::
|
|
944
|
+
|
|
945
|
+
sage: from sage.dynamics.complex_dynamics.mandel_julia_helper import general_julia
|
|
946
|
+
sage: from sage.plot.colors import Color
|
|
947
|
+
sage: R.<z> = CC[]
|
|
948
|
+
sage: f = z^5 - 1
|
|
949
|
+
sage: general_julia(f)
|
|
950
|
+
500x500px 24-bit RGB image
|
|
951
|
+
"""
|
|
952
|
+
|
|
953
|
+
cdef:
|
|
954
|
+
M, pixel, color_list, z, a_n
|
|
955
|
+
int i, j, d, k, col, row, iteration
|
|
956
|
+
double C, L, Rad, x_corner, y_corner, x_coor, y_coor, step_size
|
|
957
|
+
I = CDF.gen()
|
|
958
|
+
|
|
959
|
+
# Make sure image_width is positive
|
|
960
|
+
image_width = abs(image_width)
|
|
961
|
+
|
|
962
|
+
# Initialize an image to the color black and access the pixels
|
|
963
|
+
M = Image("RGB", (pixel_count, pixel_count), 'black')
|
|
964
|
+
pixel = M.pixels()
|
|
965
|
+
|
|
966
|
+
# Take the given base color and create a list of evenly spaced
|
|
967
|
+
# colors between the given base color and white. The number of
|
|
968
|
+
# colors in the list depends on the variable color_num.
|
|
969
|
+
if isinstance(base_color, Color):
|
|
970
|
+
# Convert Color to RGB list
|
|
971
|
+
base_color = [int(k*255) for k in base_color]
|
|
972
|
+
color_list = []
|
|
973
|
+
for i in range(color_num):
|
|
974
|
+
sig_check()
|
|
975
|
+
color_list.append(copy(base_color))
|
|
976
|
+
for j in range(3):
|
|
977
|
+
color_list[i][j] += i * (255 - color_list[i][j]) // color_num
|
|
978
|
+
color_list[i] = tuple(color_list[i])
|
|
979
|
+
|
|
980
|
+
z = f.variables()[0]
|
|
981
|
+
f_fast = fast_callable(f, vars=[z], domain=CDF)
|
|
982
|
+
|
|
983
|
+
# Calculate escape condition for each c value
|
|
984
|
+
d = f.degree(z)
|
|
985
|
+
cf_list = f.coefficients(sparse=False)
|
|
986
|
+
a_n = cf_list.pop(-1).abs()
|
|
987
|
+
C = 0
|
|
988
|
+
for cf in cf_list:
|
|
989
|
+
C += cf.abs()
|
|
990
|
+
L = 1.00000000000001
|
|
991
|
+
if d >= 2:
|
|
992
|
+
Rad = max(1, 2*C / a_n, (2*L / a_n**(1/(d-1))))
|
|
993
|
+
else:
|
|
994
|
+
Rad = max(1, 2*C / a_n)
|
|
995
|
+
|
|
996
|
+
# First, we determine the complex coordinates of the point in the top left
|
|
997
|
+
# corner of the image. Then, we loop through each pixel in the image and
|
|
998
|
+
# assign it complex coordinates relative to the image's top left corner.
|
|
999
|
+
x_corner = x_center - image_width/2
|
|
1000
|
+
y_corner = y_center + image_width/2
|
|
1001
|
+
step_size = image_width*1.0 / pixel_count
|
|
1002
|
+
for col in range(pixel_count):
|
|
1003
|
+
x_coor = x_corner + col*step_size
|
|
1004
|
+
for row in range(pixel_count):
|
|
1005
|
+
sig_check()
|
|
1006
|
+
y_coor = y_corner - row*step_size
|
|
1007
|
+
|
|
1008
|
+
# We compute the orbit of c under the map f
|
|
1009
|
+
# until we either reach the maximum number of iterations
|
|
1010
|
+
# or find a point in the orbit with modulus greater than
|
|
1011
|
+
# some the escape condition (Rad)
|
|
1012
|
+
|
|
1013
|
+
new_z = x_coor + y_coor*I
|
|
1014
|
+
iteration = 0
|
|
1015
|
+
while new_z.abs() <= Rad**2 and iteration < max_iteration:
|
|
1016
|
+
sig_check()
|
|
1017
|
+
new_z = f_fast(new_z)
|
|
1018
|
+
iteration += 1
|
|
1019
|
+
|
|
1020
|
+
# If the point escapes to infinity, assign the point a color
|
|
1021
|
+
# based on how fast it escapes. The more iterations it takes for
|
|
1022
|
+
# a point to escape to infinity, the lighter its color will be.
|
|
1023
|
+
# Otherwise, assume the point is in the Mandelbrot set and leave
|
|
1024
|
+
# it black.
|
|
1025
|
+
if iteration != max_iteration:
|
|
1026
|
+
# Assign each point a level based on its number of iterations.
|
|
1027
|
+
level = iteration // level_sep
|
|
1028
|
+
# Assign the pixel a color based on it's level. If we run out
|
|
1029
|
+
# of colors, assign it the last color in the list.
|
|
1030
|
+
if level < color_num:
|
|
1031
|
+
pixel[col, row] = color_list[level]
|
|
1032
|
+
else:
|
|
1033
|
+
pixel[col, row] = color_list[-1]
|
|
1034
|
+
return M
|