{
    "cells": [
        {
            "cell_type": "markdown",
            "id": "55d0cc9d",
            "metadata": {},
            "source": [
                "# **Directed percolation in Rust-Python**\n",
                "\n",
                "Demo simulation of directed percolation on a 2d lattice."
            ]
        },
        {
            "cell_type": "markdown",
            "id": "47566d43",
            "metadata": {},
            "source": [
                "<hr>\n",
                "\n",
                "## Preliminaries"
            ]
        },
        {
            "cell_type": "code",
            "execution_count": 1,
            "id": "b8fa3294",
            "metadata": {},
            "outputs": [
                {
                    "data": {
                        "text/plain": [
                            "'2026.3.22'"
                        ]
                    },
                    "execution_count": 1,
                    "metadata": {},
                    "output_type": "execute_result"
                }
            ],
            "source": [
                "from essentials import *\n",
                "dprs.__version__"
            ]
        },
        {
            "cell_type": "markdown",
            "id": "4d04876e",
            "metadata": {},
            "source": [
                "<hr>\n",
                "\n",
                "## Parameters"
            ]
        },
        {
            "cell_type": "code",
            "execution_count": 2,
            "id": "66226d2b",
            "metadata": {},
            "outputs": [],
            "source": [
                "class Parameters:\n",
                "    dim = sim.Dimension.D2\n",
                "    n_x: int = 1000\n",
                "    n_y: int = 1000\n",
                "    n_z: int = 1\n",
                "    p: float = 0.163145\n",
                "    p0: float = 0.99\n",
                "    seed: int = 1\n",
                "    n_iterations: int = 1000\n",
                "    sample_period: int  = 1000\n",
                "    axis_topology_x = sim.Topology.Periodic\n",
                "    axis_topology_y = sim.Topology.Periodic\n",
                "    axis_topology_z = sim.Topology.Unspecified\n",
                "    axis_bcs_x = (sim.BoundaryCondition.Floating, sim.BoundaryCondition.Floating)\n",
                "    axis_bcs_y = (sim.BoundaryCondition.Floating, sim.BoundaryCondition.Floating)\n",
                "    axis_bcs_z = (sim.BoundaryCondition.Unspecified, sim.BoundaryCondition.Unspecified)\n",
                "    axis_bc_values_x = (DUAL.OCCUPIED.state, DUAL.OCCUPIED.state)\n",
                "    axis_bc_values_y = (DUAL.OCCUPIED.state, DUAL.OCCUPIED.state)\n",
                "    axis_bc_values_z = (DUAL.EMPTY.state, DUAL.EMPTY.state)\n",
                "    do_edge_buffering: bool = True\n",
                "    processing = sim.Processing.Parallel\n",
                "    n_threads: int = 16\n",
                "parameters = Parameters()\n",
                "# Just in case we forget to update sample_period to match n_iterations\n",
                "if parameters.sample_period > parameters.n_iterations:\n",
                "    parameters.sample_period = parameters.n_iterations"
            ]
        },
        {
            "cell_type": "markdown",
            "id": "a1c79039",
            "metadata": {},
            "source": [
                "<hr>\n",
                "\n",
                "## Simulation"
            ]
        },
        {
            "cell_type": "markdown",
            "id": "20ee9b5b",
            "metadata": {},
            "source": [
                "Execute the Rust-coded sim (for small grids, parallelization can actually slow down the processing):"
            ]
        },
        {
            "cell_type": "code",
            "execution_count": 3,
            "id": "c3ca941d",
            "metadata": {},
            "outputs": [],
            "source": [
                "n_lattices: int\n",
                "raw_lattices: list[list[bool]] \n",
                "raw_tracking: list[list, list]\n",
                "t_run_time: float"
            ]
        },
        {
            "cell_type": "code",
            "execution_count": 4,
            "id": "df8648d5",
            "metadata": {},
            "outputs": [
                {
                    "name": "stdout",
                    "output_type": "stream",
                    "text": [
                        "\n",
                        "Dimension:   D2\n",
                        "Grid shape:  (1000, 1000, 1)\n",
                        "Probability: 0.163145\n",
                        "Prob @t=0:   0.99\n",
                        "Random seed: 1\n",
                        "Iterations:  1000\n",
                        "Sampling:    1000\n",
                        "Topology x:  Periodic\n",
                        "Topology y:  Periodic\n",
                        "Topology z:  Unspecified\n",
                        "Axis BCs x:  (Floating, Floating)\n",
                        "Axis BCs y:  (Floating, Floating)\n",
                        "Axis BCs z:  (Unspecified, Unspecified)\n",
                        "BC values x: (true, true)\n",
                        "BC values y: (true, true)\n",
                        "BC values z: (false, false)\n",
                        "Edge buffer: true\n",
                        "Processing:  Parallel\n",
                        "Num threads: 16\n",
                        "\n",
                        "\n",
                        "Simulation run time (parallel processing): 0.517s\n"
                    ]
                }
            ],
            "source": [
                "(n_lattices, raw_lattices, raw_tracking, t_run_time)= sim.dk(parameters)"
            ]
        },
        {
            "cell_type": "code",
            "execution_count": 5,
            "id": "df47e336",
            "metadata": {},
            "outputs": [
                {
                    "name": "stdout",
                    "output_type": "stream",
                    "text": [
                        "Total number of lattice time slices = 2\n",
                        "\n"
                    ]
                }
            ],
            "source": [
                "print(f\"Total number of lattice time slices = {n_lattices}\\n\")"
            ]
        },
        {
            "cell_type": "markdown",
            "id": "6e816286",
            "metadata": {},
            "source": [
                "<hr>\n",
                "\n",
                "## Post-processing"
            ]
        },
        {
            "cell_type": "markdown",
            "id": "a687aca4",
            "metadata": {},
            "source": [
                "The function `sim.dk` returns a subset of the time-series of lattices generated by the simulation, with subsetting given by the `sample_rate` parameter, such that the total number provided is `n_lattices`. Each lattice is returned in flattened form as a 1d list, with all of them concatenated into one long 1d list."
            ]
        },
        {
            "cell_type": "markdown",
            "id": "41d86021",
            "metadata": {},
            "source": [
                "We convert this long list into a 2+1d numpy array, with the last index being the lattice slice number. Care must be taken to reshape by the grid dimensions in the correct order: the fastest index in the list is the x coordinate, etc., so the reshaping parameters need to be given in reverse order `(n_lattices, y, x)`."
            ]
        },
        {
            "cell_type": "code",
            "execution_count": 6,
            "id": "a4167ef1",
            "metadata": {},
            "outputs": [
                {
                    "data": {
                        "text/plain": [
                            "(1000, 1000, 2)"
                        ]
                    },
                    "execution_count": 6,
                    "metadata": {},
                    "output_type": "execute_result"
                }
            ],
            "source": [
                "lattices: NDArray\n",
                "if n_lattices>0:\n",
                "    lattices = np.array(raw_lattices, dtype=np.bool,).reshape(\n",
                "        n_lattices, parameters.n_y, parameters.n_x,\n",
                "    ).T\n",
                "else:\n",
                "    lattices = np.zeros((0,))\n",
                "lattices.shape"
            ]
        },
        {
            "cell_type": "markdown",
            "id": "ec37d47b",
            "metadata": {},
            "source": [
                "Tracking data, which for now is the vector pair (t; mean ρ):"
            ]
        },
        {
            "cell_type": "code",
            "execution_count": 7,
            "id": "0cd39968",
            "metadata": {},
            "outputs": [
                {
                    "data": {
                        "text/plain": [
                            "(2, 1001)"
                        ]
                    },
                    "execution_count": 7,
                    "metadata": {},
                    "output_type": "execute_result"
                }
            ],
            "source": [
                "tracking: NDArray = np.array(raw_tracking, dtype=np.float64,) \n",
                "tracking.shape"
            ]
        },
        {
            "cell_type": "markdown",
            "id": "37843af4",
            "metadata": {},
            "source": [
                "<hr>\n",
                "\n",
                "## Visualization"
            ]
        },
        {
            "cell_type": "markdown",
            "id": "ed89edd7",
            "metadata": {},
            "source": [
                "### Prep"
            ]
        },
        {
            "cell_type": "code",
            "execution_count": 8,
            "id": "2a752a13",
            "metadata": {},
            "outputs": [],
            "source": [
                "viz = Viz(dpi=100)\n",
                "i_slice: int\n",
                "name: str"
            ]
        },
        {
            "cell_type": "code",
            "execution_count": 9,
            "id": "ce0818d5",
            "metadata": {},
            "outputs": [],
            "source": [
                "image_lattice = partial(\n",
                "    viz.image_lattice,\n",
                "    lattices=lattices, \n",
                "    p=parameters, \n",
                "    x=min(300, parameters.n_x),\n",
                "    y=min(200, parameters.n_y),\n",
                "    fig_size=(6, 4,),\n",
                ")"
            ]
        },
        {
            "cell_type": "markdown",
            "id": "bd886321",
            "metadata": {},
            "source": [
                "### Slices"
            ]
        },
        {
            "cell_type": "markdown",
            "id": "1fef16c9",
            "metadata": {},
            "source": [
                "Visualize the initial lattice:"
            ]
        },
        {
            "cell_type": "code",
            "execution_count": 10,
            "id": "6f0410ee",
            "metadata": {},
            "outputs": [
                {
                    "name": "stdout",
                    "output_type": "stream",
                    "text": [
                        "lattice_p0p163145_s1_nx1000_ny1000_i00000\n"
                    ]
                },
                {
                    "data": {
                        "image/png": "[encoded data, 125696 characters]",
                        "text/plain": [
                            "<Figure size 600x400 with 2 Axes>"
                        ]
                    },
                    "metadata": {
                        "image/png": {
                            "height": 352,
                            "width": 518
                        }
                    },
                    "output_type": "display_data"
                }
            ],
            "source": [
                "if n_lattices>0:\n",
                "    i_slice = 0\n",
                "    name = make_name(parameters, \"lattice\", i_slice,)\n",
                "    print(name)\n",
                "    image_lattice(\n",
                "        name=name, title=make_title(parameters, i_slice), i_lattice=i_slice,\n",
                "    )\n",
                "    plt.show()"
            ]
        },
        {
            "cell_type": "markdown",
            "id": "00af89a5",
            "metadata": {},
            "source": [
                "Visualize the final lattice:"
            ]
        },
        {
            "cell_type": "code",
            "execution_count": 11,
            "id": "16918fba",
            "metadata": {},
            "outputs": [
                {
                    "name": "stdout",
                    "output_type": "stream",
                    "text": [
                        "lattice_p0p163145_s1_nx1000_ny1000_i01000\n"
                    ]
                },
                {
                    "data": {
                        "image/png": "[encoded data, 92284 characters]",
                        "text/plain": [
                            "<Figure size 600x400 with 2 Axes>"
                        ]
                    },
                    "metadata": {
                        "image/png": {
                            "height": 352,
                            "width": 518
                        }
                    },
                    "output_type": "display_data"
                }
            ],
            "source": [
                "if n_lattices>=1:\n",
                "    i_slice = (n_lattices-1)\n",
                "    name = make_name(parameters, \"lattice\", i_slice,)\n",
                "    print(name)\n",
                "    image_lattice(\n",
                "        name=name, title=make_title(parameters, i_slice), i_lattice=i_slice,\n",
                "    )\n",
                "    plt.show()"
            ]
        },
        {
            "cell_type": "markdown",
            "id": "9d9d73c6",
            "metadata": {},
            "source": [
                "### History"
            ]
        },
        {
            "cell_type": "markdown",
            "id": "fac0a8e0",
            "metadata": {},
            "source": [
                "See Henkel et al (2008), tables 4.1 and 4.3: the decay rate of the mean order parameter (mean occupancy) $\\overline\\rho$ should decay with time as\n",
                "$$\n",
                "    \\rho(t) \\sim t^{-\\delta}\n",
                "$$\n",
                "where in $d=2$ we should find $\\delta \\approx 0.4505$. If that's what we're seeing, there's a long \"burn-in\" phase. It appears we're a bit off the ideal value: (1) because there is a finite lattice-size bias; and (2) because we need to extremely precise about choosing $p=p_c$. \n",
                "\n",
                "Here I have heuristically tuned $p$ to\n",
                "$$\n",
                "    p = p_c? = \\,\\,0.163145\n",
                "$$\n",
                "which is rather far off any traditional critical DP probability $p_c$, probably because of the 9-cell neighborhood live/die dependency."
            ]
        },
        {
            "cell_type": "code",
            "execution_count": 12,
            "id": "0981a0b0",
            "metadata": {},
            "outputs": [
                {
                    "name": "stdout",
                    "output_type": "stream",
                    "text": [
                        "ρmean_p0p163145_s1_nx1000_ny1000\n"
                    ]
                },
                {
                    "data": {
                        "image/png": "[encoded data, 96744 characters]",
                        "text/plain": [
                            "<Figure size 600x400 with 1 Axes>"
                        ]
                    },
                    "metadata": {
                        "image/png": {
                            "height": 400,
                            "width": 558
                        }
                    },
                    "output_type": "display_data"
                }
            ],
            "source": [
                "δ = 0.45051\n",
                "ρ_mean_ref = 0.238\n",
                "\n",
                "name = make_name(parameters, \"ρmean\", None, )\n",
                "print(name)\n",
                "viz.plot_ρmean(\n",
                "    name,\n",
                "    make_title(parameters, None),\n",
                "    tracking,\n",
                "    δ, \n",
                "    ρ_mean_ref,\n",
                "    fig_size=(6,4,),\n",
                "    i_offset=1,\n",
                ")\n",
                "plt.show()"
            ]
        },
        {
            "cell_type": "code",
            "execution_count": 13,
            "id": "a00db691",
            "metadata": {},
            "outputs": [
                {
                    "name": "stdout",
                    "output_type": "stream",
                    "text": [
                        "Estimated t-decay exponent:  δ = -0.586\n"
                    ]
                }
            ],
            "source": [
                "i_offset: int = parameters.n_iterations//3\n",
                "t: NDArray = tracking[0][i_offset:]\n",
                "ρ_mean: NDArray = tracking[1][i_offset:]\n",
                "(exponent, scale, r_value, p_value, std_err) \\\n",
                "    = linregress(np.log(t), np.log(ρ_mean))\n",
                "\n",
                "print(rf\"Estimated t-decay exponent:  δ = {exponent:0.3f}\")"
            ]
        },
        {
            "cell_type": "markdown",
            "id": "46b9e6c9",
            "metadata": {},
            "source": [
                "<hr>\n",
                "\n",
                "## Export"
            ]
        },
        {
            "cell_type": "code",
            "execution_count": 14,
            "id": "77f7dc1f",
            "metadata": {},
            "outputs": [
                {
                    "data": {
                        "text/plain": [
                            "'././plots/dp2d'"
                        ]
                    },
                    "execution_count": 14,
                    "metadata": {},
                    "output_type": "execute_result"
                }
            ],
            "source": [
                "plots_dir: str = create_directories((os.path.curdir,\"plots\",), \"dp2d\")\n",
                "export_plots(\n",
                "    viz.fdict,\n",
                "    plots_dir,\n",
                "    \"PNG\",\n",
                "    suffix=\"\",\n",
                "    dpi=150,\n",
                ")"
            ]
        }
    ],
    "metadata": {
        "kernelspec": {
            "display_name": "DPRS (3.14.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.14.3"
        }
    },
    "nbformat": 4,
    "nbformat_minor": 5
}
