{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Alguns exemplos de caos " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Importando o básico" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "import matplotlib.pyplot as plt\n", "%matplotlib inline\n", "from decimal import *\n", "from scipy.stats import linregress" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Trajetórias densas" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Um mapa $f(x)$ definido num intervalo $\\mathcal{I}$ é caótico se ele for topologicamente transitivo em um subconjunto invariante $\\mathcal{X}\\subset\\mathcal{I}$.\n", "\n", "Isso implica que existe uma órbita densa em $\\mathcal{X}$ começando em um ponto $x\\in\\mathcal{X}$.\n", "\n", "Infelizmente, essas órbitas são muito difícies de encontrar.\n", "\n", "Na prática, consideramos o mapa caótico se encontramos uma órbita que \"parece\" densa." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Iterando um mapa" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def iterate_map(f, x0, n, precision=200):\n", " \"\"\"Itera o mapa f(x) começando em x0 por n instantes de tempo.\n", " Permite especificar a precisão dos cálculo (não dos valores retornados).\n", " Retorna um numpy.array com n+1 valores x[0], ..., x[n].\"\"\"\n", " x = np.zeros(n+1)\n", " x[0] = x0\n", " oldprec = getcontext().prec\n", " getcontext().prec = precision\n", " xt = Decimal(x0)\n", " for t in range(n):\n", " xt = f(xt)\n", " x[t+1] = xt\n", " getcontext().prec = oldprec\n", " return x" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Criando um diagrama de bifurcação" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Agora, dado um mapa $f(x)$ que depende de um parâmetro $\\mu$, queremos montar o seu diagrama de bifurcação para $\\mu\\in[a,b]$, com $n$ intervalos nessa faixa de valores." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def plot_bifurcation_diagram(f, a, b, npar, x0 = None, precision=200):\n", " \"\"\"Plota o diagrama de bifurcação de f(x, μ) com npar valores de μ no intervalo a <= μ <= b.\n", " Se for especificado um valor de x0, ele será usado como valor inicial. \n", " senão será usado um valor aleatório entre 0 e 1.\n", " Você pode especificar também a precisão.\"\"\"\n", " oldprec = getcontext().prec\n", " getcontext().prec = precision\n", " μs = np.linspace(a, b, npar+1)\n", " if x0 is None:\n", " x0 = np.random.random()\n", " x0 = Decimal(x0)\n", " fig, ax = plt.subplots()\n", " fig.set_size_inches(12, 12)\n", " for μ in μs:\n", " x = iterate_map(lambda x: f(x, Decimal(μ)), x0, 1000, precision)\n", " μrep = np.full_like(x[500:], μ)\n", " ax.plot(μrep, x[500:], ',k')\n", " ax.set_xlabel(r'$\\mu$')\n", " ax.set_ylabel(r'$x$')\n", " getcontext().prec = oldprec" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Exemplo 1" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Consideremos a equação de recorrência\n", "$$x_{t+1} = x_t e^{\\mu(1-x_t)}.$$" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def exemplo_1(x, μ):\n", " \"Implementa o mapa f(x) = x e^(μ(1-x)).\"\n", " return x * (μ * (1 - x)).exp()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Diagrama de bifurcação" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Vamos começar calculando o diagrama de bifurcação para esse mapa na região $[1.5, 3]$." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "plot_bifurcation_diagram(exemplo_1, 1.5, 3, 500)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Vemos que o sistema sofre uma sequência infinita de duplicação de períodos e que as órbitas após essa sequência de duplicações aparentam ser densas para a maioria dos valores de $\\mu$." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Densidade de probabilidade de trajetória na região caótica" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Como exemplo, vamos ver a distribuição de pontos da trajetória para $\\mu=2.9$. Primeiro calculamos uma trajetória longa iniciando em ponto aleatório." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "traj = iterate_map(lambda x: exemplo_1(x, Decimal(2.9)), \n", " np.random.random(), 10000)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Agora descartamos os primeiros 1000 pontos da trajetória e fazemos um histograma dos valores dos restantes." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fig, ax = plt.subplots()\n", "fig.set_size_inches(8, 6)\n", "ax.hist(traj[1000:], bins=50, density=True)\n", "ax.set_xlabel(r'$x$')\n", "ax.set_ylabel(r'$\\rho(x)$');" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Como vemos, existe uma região densamente povoada pela trajetória." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Exemplo 2" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Agora vejamos a equação de recursão\n", "$$x_{t+1} = \\mu x_t (1+x_t)^{-8}.$$" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def exemplo_2(x, μ):\n", " return μ * x * (-8 * (1+x).ln()).exp()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Diagrama de bifurcação" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Vamos calcular o diagrama de difurcação para $5<\\mu<40$." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "plot_bifurcation_diagram(exemplo_2, 5, 40, 500)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Novamente, temos uma sequência de duplicações de período." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Trajetória densa" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Vamos verificar a densidade de probabilidade de uma trajetória para $\\mu=37$." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "traj2 = iterate_map(lambda x: exemplo_2(x, Decimal(37)), \n", " np.random.random(), 10000)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fig, ax = plt.subplots()\n", "fig.set_size_inches(8, 6)\n", "ax.hist(traj2[1000:], bins=50, density=True)\n", "ax.set_xlabel(r'$x$')\n", "ax.set_ylabel(r'$\\rho(x)$');" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Sensibilidade a condições iniciais" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Vimos que uma forma de verificar a sensibilidade a condições iniciais é calcular o expoente de Lyapunov.\n", "\n", "Vamos fazer isso para o exemplo 2, primeiro na parte periódica $\\mu=15$. Vamos usar como ponto inicial $x_0=1$." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "getcontext().prec=200\n", "trajper1 = iterate_map(lambda x: exemplo_2(x, Decimal(15)), \n", " Decimal(1), 40)\n", "trajper2 = iterate_map(lambda x: exemplo_2(x, Decimal(15)), \n", " Decimal(1)+Decimal('0.01'), 40)\n", "absdiffper = np.abs(trajper1 - trajper2)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fig, ax = plt.subplots()\n", "fig.set_size_inches(8, 6)\n", "ax.plot(absdiffper)\n", "ax.set_yscale('log')\n", "ax.set_xlabel(r'$t$')\n", "ax.set_ylabel('Diferença absoluta entre as trajetórias')\n", "ax.set_title('Sistema periódico');" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Vemos que entre os instantes 10 e 30 temos uma região de decaimento exponencial, indicando que o expoente de Lyapunov é negativo e portanto não há sensibilidade a condições iniciais.\n", "\n", "Podemos avaliar o valor do expoente de Lyapunov fazendo uma regressão linear do logaritmo diferença absoluta entre as trajetórias com $t$:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "exppart = np.array(range(10, 31))\n", "slope, intercept, rvalue, pvalue, std_error = linregress(exppart, np.log(absdiffper[exppart]))\n", "print(\"The Lyapunov exponent is\", slope)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Agora vamos fazer o mesmo na parte caótica $\\mu=37$." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "trajcaos1 = iterate_map(lambda x: exemplo_2(x, Decimal(37)), \n", " Decimal(1), 100)\n", "trajcaos2 = iterate_map(lambda x: exemplo_2(x, Decimal(37)), \n", " Decimal(1)+Decimal('0.0000001'), 100)\n", "absdiffcaos = np.abs(trajcaos1 - trajcaos2)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fig, ax = plt.subplots()\n", "fig.set_size_inches(8, 6)\n", "ax.plot(absdiffcaos)\n", "ax.set_yscale('log')\n", "ax.set_xlabel(r'$t$')\n", "ax.set_ylabel('Diferença absoluta entre as trajetórias')\n", "ax.set_title('Sistema caótico');" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Aqui o resultado é mais ruidoso, mas pode-se notar uma tendência clara de crescimento exponencial.\n", "\n", "O crescimento exponencial é limitado, pois o ponto fica preso no atrator caótico, que é finito, e portanto existem uma distância máxima entre as trajetórias.\n", "\n", "O crescimento aparenta ser exponencial na faixa $10 \\le t \\le 50$. Vamos usar essa faixa de valores para avaliar o coeficiente de Lyapunov." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "exppart = np.array(range(10, 51))\n", "slope, intercept, rvalue, pvalue, std_error = linregress(exppart, np.log(absdiffcaos[exppart]))\n", "print(\"The Lyapunov exponent is\", slope)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Portanto o expoente de Lyapunov é positivo, como esperado." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Mapa de Hénon" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Hénon propôs a seguinte equação de recursão para um sistema simples que apresenta atrator estranho:\n", "\\begin{eqnarray*}\n", "x_{t+1} & = & 1 + y_t - a x_t^2\\\\\n", "y_{t+1} & = & b x_t.\n", "\\end{eqnarray*}" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def henon(xy, a, b=Decimal(0.3)):\n", " x, y = xy\n", " return [1 + y - a*x*x, b * x]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Os pontos fixos são dados por\n", "\\begin{eqnarray*}\n", "x & = & 1 + y - ax^2\\\\\n", "y & = & bx,\n", "\\end{eqnarray*}\n", "e portanto\n", "$$ a x^2 - (b - 1) x - 1 = 0$$,\n", "resultando em\n", "$$x_{1,2} = \\frac{(b-1) \\pm \\sqrt{(b-1)^2+4a}}{2a}, \\quad y_{1,2} = bx_{1,2}.$$\n", "\n", "O jacobiano vale\n", "$$\\left[\\begin{array}{cc}-2ax & 1\\\\b & 0\\end{array}\\right]$$\n", "e portanto o seu determinante vale sempre $-b$. Se $\\lvert b \\rvert<1$ o mapa contrai o espaço de estado.\n", "\n", "Hénon usou o valor $b=0.3$ em seus estudos. Também vamos fixar esse valor.\n", "\n", "Vejamos o que acontece com $x_t$ com $b=0.3$ e variando o valor de $a$:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def iterate_henon(xy0, a, n, precision=200):\n", " oldprec = getcontext().prec\n", " getcontext().prec = precision\n", " a = Decimal(a)\n", " x0, y0 = xy0\n", " xt = Decimal(x0)\n", " yt = Decimal(y0)\n", " xy = np.zeros((n+1, 2))\n", " xy[0, :] = [x0, y0]\n", " for t in range(n):\n", " xt, yt = henon([xt, yt], a)\n", " xy[t+1, :] = [xt, yt]\n", " getcontext().prec = oldprec\n", " return xy" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def plot_bifurcation_diagram_henon(a0, af, npar, xy0 = None, precision=200):\n", " oldprec = getcontext().prec\n", " getcontext().prec = precision\n", " avalues = np.linspace(a0, af, npar+1)\n", " if xy0 is None:\n", " xy0 = np.random.random(2)\n", " xy0[:] = [Decimal(xy0[0]), Decimal(xy0[1])]\n", " fig, ax = plt.subplots()\n", " fig.set_size_inches(12, 12)\n", " for a in avalues:\n", " xy = iterate_henon(xy0, a, 1000, precision)\n", " arep = np.full_like(xy[500:, 0], a)\n", " ax.plot(arep, xy[500:, 0], ',k')\n", " ax.set_xlabel(r'$a$')\n", " ax.set_ylabel(r'$x$')\n", " getcontext().prec = oldprec" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "plot_bifurcation_diagram_henon(0, 1.42, 500, [0.63, 0.19])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Novamente temos duplicações de período e uma região de trajetórias densas.\n", "\n", "Mas agora vamos considerar uma outra situação que não acontece nos mapas anteriores. Vejamos a trajetória no espaço de estado para $a=1.4, b=0.3$:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "traj3 = iterate_henon([0.63, 0.19], Decimal(1.4), 10000)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fig, ax = plt.subplots()\n", "fig.set_size_inches(8, 6)\n", "ax.plot(traj3[1000:, 0], traj3[1000:, 1], ',')\n", "ax.set_xlabel(r'$x$')\n", "ax.set_ylabel(r'$y$');" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note que os pontos se concentram em uma região de área zero, que não é um ponto nem um ciclo, nem aparentemente uma linha contínua. Vamos executar por mais tempo, para ver o resultado:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "traj4 = iterate_henon([0.63, 0.19], Decimal('1.4'), 100000)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fig, ax = plt.subplots()\n", "fig.set_size_inches(8, 6)\n", "ax.plot(traj4[1000:, 0], traj4[1000:, 1], ',')\n", "ax.set_xlabel(r'$x$')\n", "ax.set_ylabel(r'$y$');" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "As características parecem se manter, mostrando que esse conjunto é invariante." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Método da contagem de caixas para avaliação da dimensão" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Agora vamos tentar avaliar a dimensão desse conjunto. Para isso, usamos o _insight_ de que se um subconjunto tem dimensão $d$, quando dividimos o espaço em quadrados de lado $\\epsilon$, o número de quadrado que têm pontos do subconjunto deve crescer com $$N(\\epsilon) \\propto \\epsilon^{-d}.$$\n", "\n", "Então vamos cobrir o espaço com quadrados de diversos tamanhos $\\epsilon$ (todos pequenos) tendendo para zero, e contar o número de quadrados que têm pelo menos um ponto do conjunto, Veremos então como o númeero desses quadrados se comporta com $\\epsilon$." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "xmin = np.min(traj4[1000:, 0])\n", "xmax = np.max(traj4[1000:, 0])\n", "ymin = np.min(traj4[1000:, 1])\n", "ymax = np.max(traj4[1000:, 1])\n", "\n", "epsilons = np.logspace(-4, -1, 20, base=10)\n", "nfilled = np.zeros_like(epsilons, dtype=np.int)\n", "for i, ϵ in enumerate(epsilons):\n", " nx = int(np.ceil((xmax - xmin)/ϵ))\n", " ny = int(np.ceil((ymax - ymin)/ϵ))\n", " \n", " boxes = np.zeros((ny, nx), dtype=np.bool)\n", " \n", " boxi = (np.floor((traj4[1000:, 1] - ymin)/ϵ)).astype(np.int)\n", " boxj = (np.floor((traj4[1000:, 0] - xmin)/ϵ)).astype(np.int)\n", " \n", " boxes[boxi, boxj] = True\n", " \n", " nfilled[i] = np.count_nonzero(boxes)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fig, ax = plt.subplots()\n", "fig.set_size_inches(8, 6)\n", "ax.loglog(epsilons, nfilled)\n", "ax.set_xlabel(r'$\\epsilon$')\n", "ax.set_ylabel(r'$N(\\epsilon)$');" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Vemos que para uma boa faixa de valores de $\\epsilon$ temos uma dependência expoenencial $N_\\epsilon \\propto\\epsilon^{-d}$.\n", "\n", "Vamos então tentar avaliar o valor de $d$ fazendo uma regressão linear entre $\\log N_\\epsilon$ e $\\epsilon$ na região $\\epsilon\\in\\left[10^{-3}, 10^{-1}\\right]$." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "chosen = np.logical_and(epsilons >= 1e-3, epsilons <= 1e-1)\n", "chosen_eps = epsilons[chosen]\n", "chosen_n = nfilled[chosen]\n", "\n", "\n", "slope, intercept, rvalue, pvalue, std_error = linregress(np.log(chosen_eps), np.log(chosen_n))\n", "\n", "print(\"Computed a Hausdorff dimension of\", -slope)\n", "print(\"Some statistics: \")\n", "print(\" - correlation coefficient:\", rvalue)\n", "print(\" - p-value for the null hypothesis of slope 0:\", pvalue)\n", "print(\" - standard error of the estimate:\", std_error)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Portanto, concluímos que o atrator do mapa de Hénon para $a=1.4$ e $b=0.3$ é um **atrator estranho**." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.7.6" } }, "nbformat": 4, "nbformat_minor": 4 }