CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

| Download
Project: Dynamics 2019
Views: 1
Visibility: Unlisted (only visible to those who know the link)
Image: ubuntu2004
Kernel: SageMath 9.6

Conjugacy in the Logistic Family

The case of μ∈(1,2)\mu \in (1,2).

Pick two parameters in the interval (1,2)(1,2). We will find an explicit topological conjugacy between the two maps restricted to [0,1][0,1]. (It is not hard to extend the conjugacy outside the interval, but this will be enough to understand how to build such conjugacies.)

mu = 4/3 nu = 7/4
F(x) = mu*x*(1-x) F
x |--> -4/3*(x - 1)*x
G(x) = nu * x * (1-x) G
x |--> -7/4*(x - 1)*x

Below we plot FF with the diagonal over the interval [0,1][0,1].

plot(F, 0, 1, aspect_ratio=1) + plot(x,(x, 0, 1), color="red")
Image in a Jupyter notebook

Here is the plot of GG:

plot(G, 0, 1, aspect_ratio=1) + plot(x,(x, 0, 1), color="red")
Image in a Jupyter notebook

Recall that these maps have fixed points at zero and at pμ=μ−1μp_\mu=\frac{\mu-1}{\mu}.

p = (mu-1)/mu p
1/4

Check that pp is fixed by FF:

bool(F(p) == p)
True

We'll use qq for the corresponding fixed point of GG:

q = (nu-1)/nu print("q = " + str(q)) bool(G(q) == q)
q = 3/7
True

Notice that FF restricts to a map F∣(0,p):(0,p)→(0,p),F|_{(0,p)} : (0,p) \to (0,p), and this map is a homeomorphism.

Furthermore, we have F(x)>xF(x)>x for x∈(0,p)x \in (0,p). Therefore, all orbits in (0,p)(0,p) converge monotonically to pp.

We choose any point a∈(0,p)a \in (0,p) to construct a fundamental domain [a,b)\big[a,b\big) for F∣(0,p)F|_{(0,p)}. This means that for every point x∈(0,p)x \in (0,p) there is a unique n∈Zn \in {\mathbb Z} such that F∣(0,p)n(x)∈[a,b).F|_{(0,p)}^n(x) \in [a,b). To do this we must take b=F(a)b=F(a).

a = 1/8 b = F(a) b
7/48

Now we find a fundamental domain [c,d)[c,d) for the restriction G∣(0,q)G|_{(0,q)}.

c = 1/4 d = G(c) d
21/64

The first stage of the topological conjugacy

We can now produce a topological conjugacy h1:[0,p]→[0,q]h_1:[0,p] \to [0,q], which should satisfy G∘h1=h1∘FG \circ h_1 = h_1 \circ F.

To do this, we first select a homeomorphism h0:[a,b)→[c,d)h_0:[a,b) \to [c,d) between the fundamental domains. For simplicity, we'll just use an affine linear map (though any orientation-preserving homeomorphism will work):

h0(x) = (d-c)/(b-a)*(x-a) + c h0
x |--> 15/4*x - 7/32

Let's check that this does what we want:

bool(h0(a)==c)
True
bool(h0(b)==d)
True

The topological conjugacy h1h_1 must satisfy 0↦00 \mapsto 0 and p↦qp \mapsto q.

For x∈(0,p)x \in (0,p), we will be defining h1(x)=G−n∘h0∘Fn(x)h_1(x) = G^{-n} \circ h_0 \circ F^n(x) where nn is the integer such that Fn(x)∈[a,b)F^n(x) \in [a,b). Computing this nn and Fn(x)F^n(x) is not too difficult. Basically, if x<ax<a, then we continue applying FF until the point lies in [a,b)[a,b), and if x>bx>b we continue applying F−1F^{-1} until the point lies in [a,b)[a,b).

For this second possibility, we need to an inverse of FF. There really are two inverses, and sage can actually compute them:

y = var('y') # Define a new variable. The variable $x$ was defined automatically. solve(F(x) == y, x)
[x == -1/2*sqrt(-3*y + 1) + 1/2, x == 1/2*sqrt(-3*y + 1) + 1/2]

This will be our left and right inverses:

FLinv(y) = -1/2*sqrt(-3*y + 1) + 1/2 FRinv(y) = 1/2*sqrt(-3*y + 1) + 1/2

Here is a sanity check:

bool( F(FLinv(1/3))==1/3 )
True

Let's do the same for GG:

solve(G(x) == y, x)
[x == -1/14*sqrt(-112*y + 49) + 1/2, x == 1/14*sqrt(-112*y + 49) + 1/2]
GLinv(y) = -1/14*sqrt(-112*y + 49) + 1/2 GRinv(y) = 1/14*sqrt(-112*y + 49) + 1/2

Now lets turn our attention to computing nn and Fn(x)F^n(x). Let's suppose that x<ax<a. Let's pick a particular example and try it.

x = 1/10 bool( x<a )
True

The idea is to repeat applying FF until our point becomes bigger than or equal to aa. At that moment it will lie in [a,b)[a,b).

x = F(x) n = 1 # We've applied F once

Now we need to check if xx is still less than aa.

bool( x<a )
True

We have to repeat it again:

x = F(x) n = n + 1 # We applied $F$ one more time.

Now we check again:

bool(x<a)
False

To be sure, lets see if x∈[a,b)x \in [a,b):

bool( x>=a and x<b)
True

To perform these actions automatically, we can use a while loop:

x0 = 1/10 x = x0 n = 0 while x < a: x = F(x) n = n+1 print(f"F^{n}({x0})={x} lies in the interval.")
F^2(1/10)=88/625 lies in the interval.
x0 = 1/20 x = x0 n = 0 while x < a: x = F(x) n = n+1 print(f"F^{n}({x0})={x} lies in the interval.")
F^5(1/20)=7919123083773007577909379937206597419/57525317769865226000547409057617187500 lies in the interval.

We remark that we are doing exact arithmetic. If our input is floating point, we will get more readable answers:

x0 = 0.01 x = x0 n = 0 while x < a: x = F(x) n = n+1 print(f"F^{n}({x0})={x} lies in the interval.")
F^11(0.0100000000000000)=0.136250844001924 lies in the interval.

Here is a function implementing the full topological conjugacy h1:[0,p]→[0,q]h_1:[0,p] \to [0,q]:

def h1(x0): # The conjugacy sends 0 to 0 if x0==0: return 0 # The conjugacy sends p to q if x0==p: return q # Ensure that x0 lies in the interval [0,p] if x0<0 or x0>p: # This is mainly to avoid infinite loops because of bad input! # It will cause an error when the x0 is not in [0, p] raise ValueError("The point x0 is not in [0,p]") if x0 >= a and x0 < b: # We are already in the interval return h0(x0) if x0 < a: x = x0 n = 0 while x < a: x = F(x) n = n+1 # At this point x=F^n(x_0) and we know what $n$ is. # Apply h0: z = h0(x) # Now we need to apply G^-n. for i in range(n): # This repeats a statement n times. z = GLinv(z) # This applies G^-1 once to z. return z if x0 >= b: # We carry out the same thing if x0 >= b, but apply inverses. x = x0 n = 0 while x >= b: x = FLinv(x) n = n+1 # At this point x=F^-n(x_0) and we know what $n$ is. # Apply h0: z = h0(x) # Now we need to apply G^n. for i in range(n): # This repeats a statement n times. z = G(z) # This applies G^-1 once to z. return z

Let's see what h1h_1 looks like:

plot(h1, 0, p)
Image in a Jupyter notebook

Let's check the conjugacy equation:

x = 0.001 print( G(h1(x)) ) print( h1(F(x)) )
0.0000215300290355477 0.0000215300290355436
plt1 = plot(lambda x: G(h1(x)), 0, p) plt2 = plot(lambda x: h1(F(x)), 0, p, color="green") show(plt1) show(plt2) plt1 + plt2
Image in a Jupyter notebookImage in a Jupyter notebookImage in a Jupyter notebook

Note that h1h_1 is continuous but not differentiable (despite the apparent smoothness!):

def approximate_derivative_of_h1(x, epsilon=0.0001): return (h1(x+epsilon)-h1(x)) / epsilon
plot(approximate_derivative_of_h1,0.05,0.2)
Image in a Jupyter notebook

The second stage:

The point 1/21/2 is special because it is the critical point. Concretely, F(1/2)F(1/2) is the only point with only one preimage. The same holds for GG. So, 1/21/2 must be sent to 1/21/2 by a conjugacy.

Observe that (p,12]⊂Ws(p)(p,\frac{1}{2}] \subset W^s(p). This is because for x∈(p,12]x \in (p, \frac{1}{2}], we have F(x)∈(p,12]F(x) \in (p, \frac{1}{2}] and F(x)<xF(x) < x.

We set a1=F(12)a_1=F(\frac{1}{2}) and b1=G(12)b_1=G(\frac{1}{2}):

a1 = F(1/2) print(f"a1 = {a1}") b1 = G(1/2) print(f"b1 = {b1}")
a1 = 1/3 b1 = 7/16

Now we define a homeomorphism between the fundamental domains: h2:(a1,12]→(b1,12]h_2:(a_1, \frac{1}{2}] \to (b_1, \frac{1}{2}]:

x = var('x') h2(x) = (b1-1/2)/(a1-1/2) * (x-a1) + b1 h2
x |--> 3/8*x + 5/16
bool(h2(1/2)==1/2)
True
bool(h2(a1)==b1)
True

We now extend the previous conjugacy h1:[0,p]→[0,q]h_1:[0,p] \to [0,q] to a conjugacy h3:[0,12]→[0,12]h_3:[0,\frac{1}{2}] \to [0, \frac{1}{2}]:

def h3(x0): if x0 <= p: return h1(x0) if x0 > 1/2: # This is mainly to avoid infinite loops because of bad input! # It will cause an error when the x0 is not in [0, p] raise ValueError("The point x0 is not in [0,1/2]") # At this point we know that p < x <= 1/2 x = x0 n = 0 while x <= a1: x = FLinv(x) n = n+1 # At this point x=F^-n(x_0) and we know what $n$ is. # Apply h2: z = h2(x) # Apply G n times: for i in range(n): # This repeats a statement n times. z = G(z) # This applies G^-1 once to z. return z

Here we plot h3:

plot(h3, 0, 1/2)
Image in a Jupyter notebook

Here we graphically check the conjugacy equation:

plt1 = plot(lambda x: G(h3(x)), 0, 1/2) plt2 = plot(lambda x: h3(F(x)), 0, 1/2, color="green") show(plt1) show(plt2) plt1 + plt2
Image in a Jupyter notebookImage in a Jupyter notebookImage in a Jupyter notebook

We look at the derivative for fun:

def approximate_derivative_of_h3(x, epsilon=0.0001): return (h3(x+epsilon)-h3(x)) / epsilon
plot(approximate_derivative_of_h3,0.05,0.45)
Image in a Jupyter notebook

An interesting thing to look at is what is happening for hh near the fixed point pp. Recall that we showed the conjugacy can not be a C1C^1 homeomorphism in a neighborhood of pp. From the above graph, it looks like h′(p)=0h'(p)=0, which means that hh cannot have a differentiable inverse. Here is a graph of hh in a small neighborhood of pp:

plot(h3, p-1/15, p+1/15, aspect_ratio=1) + point2d([(p,q)], color="red")
Image in a Jupyter notebook

The third stage:

Now we extend to the full interval [0,1][0,1].

Suppose that x∈(12,1]x \in (\frac{1}{2},1]. We'll want out conjugacy hh to send xx to some point y=h(x)∈(12,1]y = h(x) \in (\frac{1}{2},1]. Observe that F(x)=F(1−x)F(x)=F(1-x) and G(y)=G(1−y)G(y)=G(1-y). Our map h3h_3 defined above satisfies h3∘F(1−x)=G∘h3(1−x).h_3 \circ F(1-x) = G \circ h_3(1-x). We claim that if we define h4(x)={h3(x)if x∈[0,12]1−h3(1−x)if x∈(12,1]h_4(x) = \begin{cases} h_3(x) & \text{if }x \in [0, \frac{1}{2}] \\ 1-h_3(1-x) & \text{if } x \in (\frac{1}{2},1] \end{cases} then we'll get a conjugacy. To see this fix an x>12x > \frac{1}{2}. Then h4(x)>12h_4(x)>\frac{1}{2} and so G∘h4(x)=G(1−h3(1−x))=G(h3(1−x))G \circ h_4(x) = G\big(1-h_3(1-x)\big)=G\big(h_3(1-x)\big) and since F(x)<12F(x)<\frac{1}{2} we have h4(F(x))=h3∘F(x)=h3∘F(1−x)=G∘h3(1−x).h_4 \big(F(x)\big)=h_3 \circ F(x)= h_3 \circ F(1-x) = G \circ h_3(1-x). Thus G∘h4(x)=h4∘F(x)G \circ h_4(x) = h_4 \circ F(x) on [0,1][0,1].

def h4(x0): # We return an error if x0 is not in [0, 1]. if x0 < 0 or x0 > 1: # This is mainly to avoid infinite loops because of bad input! # It will cause an error when the x0 is not in [0, p] raise ValueError("The point x0 is not in [0, 1]") # We apply h_3 if x_0 is in [0, 1/2] if x0 <= 1/2: return h3(x0) else: return 1-h3(1-x0)
plot(h4, 0, 1)
Image in a Jupyter notebook

Graphically checking the conjugacy:

plt1 = plot(lambda x: G(h4(x)), 0, 1) plt2 = plot(lambda x: h4(F(x)), 0, 1, color="green") show(plt1) show(plt2) plt1 + plt2
Image in a Jupyter notebookImage in a Jupyter notebookImage in a Jupyter notebook