<jupyter_start><jupyter_text>The Julia set for complex quadratic polynomial In the following example we will introduce  user defined functions and illustrate a few more functionalities of the notebook, matplotlib and numpy Disclaimer : My dynamical system times are long gone, so let's go for an informal definition of the Julia set.The filled-in Julia set of a complex rational function  $$\;f(z) = \frac{P(z)}{Q(z)} $$  is the set of points $z$ so that the $\|z\|$ does not tend to Infinity after repetitive iteration of $\;f(z)$. The Julia set is the boundary of the filled-in set.We want to produce some sort of ( approximate ) visualization of the Julia set for the complex quadratic polynomial $\;f_c(z) = z^2 + c$, and see how it dramatically changes based on the value of $\;c$.<jupyter_code>from __future__ import print_function , division
import matplotlib
import numpy as np
import matplotlib.pyplot as plt
import warnings
warnings.filterwarnings("ignore")
%matplotlib inline
## now, about the Julia set, 
## first we create a grid of points in the complex plane

## linspace generate equally spaced points within the interval specified
x = np.linspace(-1.,1.,500)
## same thing for the Imaginary dimension
y= 1j*x

## fill in the complex 2d array
z = x[:,np.newaxis]+y

### just to be clear, what does that do?
#z=np.zeros((len(x),len(y)),dtype=complex)
#for i in range(len(x)):
#    z[i]=x[i]+y



np.shape(z)<jupyter_output><empty_output><jupyter_text>Now, for each point int the plane we want to iterate the quadratic polynomial function and accumulate a quantity that distinguishes points whose norm will grow indefinitely from the ones which will not<jupyter_code>## we can define the function that computes a quantity relate to 
## how fast our points escape from the center of the plane.

def  how_far_we_go( z , c , rmax = 4. , itermax=100):
    a=np.zeros(np.shape(z))
    for n in  range(1,itermax) :
        ## we add some quantities only if the norm is larger than rmax
        a+=(1./n ) * ( z*np.conj(z) > rmax)
        ## and iterate the polynomial
        z = z*z + c
    return a ;
plt.rcParams['figure.figsize'] = 8, 8 
## select a complex c
c=complex(-.06,.67)
## let's compute how "how far we go" quantity
juliaset=how_far_we_go( z , c ,10.,100)
## we can simply visualize the result as a colormap
plt.imshow(juliaset,cmap='hot')
#imshow(juliaset,cmap='cool')


# we can now explor what happens with different values of c
plt.rcParams['figure.figsize'] = 16,16
fig , axes = plt.subplots(nrows=5,ncols=4)
i = 0
for axs in axes:
    for ax in axs:
        c=complex(-.06,.64+0.003*i)
        juliaset=how_far_we_go( z , c ,10.,50)
        ax.imshow(juliaset,cmap='hot')
        i+=1


import os
cwd = os.getcwd()
working_folder = os.path.join(cwd, 'julia_set_images')
if not os.path.exists(working_folder ):
    os.makedirs( working_folder )
## we can save the images as png to look them later.
plt.rcParams['figure.figsize'] = 6,6

for i in np.arange(20):
    c=complex(-.06,.64+0.0015*i)
    fig = plt.figure()
    juliaset=how_far_we_go( z , c ,10.,50)
    plt.imshow(juliaset,cmap='cool')
    plt.savefig(os.path.join(working_folder, 'juliaset_%s.png' %i) )

%reset<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.11303809228863425, 'fraction_numerical': 0.0192009910188913, 'mean_word_length': 4.06269592476489, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 10, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '2', 'dir': 'jupyter-structured-clean-dedup', 'id': '218753', 'license': 'MIT', 'n_tokens_mistral': 1138, 'n_tokens_neox': 1079, 'n_words': 392, 'path': 'Python/Intro_to_scientific_python/julia_set.ipynb', 'repo_name': 'igorfyago/Andrew-Ng-Machine-Learning', 'size': '3229'}