Updated: 2022-12-16 Fri 10:41

Attractors using Datashader

Datashader is a python visualization library that breaks down the visualization step into different parts with the idea of speeding up by running individual parts on particular hardware. This way it can leverage GPUs too potentially (need to read more). This note is just a proof of concept/toying with the library.

To install, use pip install datashader (or conda if in a conda env). It also supports numba so that should help speeding up the execution too.

1. Clifford Attractors

Clifford Attractors are strange attractors defined by two iterative equations.

\[ x_{n+1} = \sin (ay_n) + c \cos (ax_n)\\ y_{n+1} = \sin (bx_n) + d \cos (by_n) \]

import numpy as np
import pandas as pd
import datashader as ds
from datashader import transfer_functions as tf
from datashader.colors import inferno, viridis
from datashader.utils import export_image
from numba import jit
from functools import partial

Setup system for Clifford Attractor

@jit(nopython=True)
def clifford_attractor(x, y, a=-1.3, b=-1.3, c=-1.8, d=-1.9):
    return np.sin(a * y) + c * np.cos(a * x), \
        np.sin(b * x) + d * np.cos(b * y)

Now, we evaluate this for n iterations.

n = 100000000

@jit(nopython=True)
def trajectory_coords(fn, x0, y0, a, b=0, c=0, d=0, n=n):
    x, y = np.zeros(n), np.zeros(n)
    x[0], y[0] = x0, y0
    # partial_fn = partial(fn, a=a, b=b, c=c, d=d)
    for i in range(n-1):
        x[i+1], y[i+1] = fn(x[i], y[i], a=a, b=b, c=c, d=d)
    return x, y

def trajectory(fn, x0, y0, a, b=0, c=0, d=0, n=n):
    x, y = trajectory_coords(fn, x0, y0, a, b, c, d)
    return pd.DataFrame({"x": x, "y": y})
%%time
df = trajectory(clifford_attractor, 0, 0, -1.244, -1.251, -1.815, -1.908)
CPU times: user 3.74 s, sys: 246 ms, total: 3.99 s
Wall time: 3.99 s

Datashader allows aggregating values usinf different types of functions. Here we’ll just count the instances of (x, y) coordinate being reached by the attractor.

cvs = ds.Canvas(plot_width=700, plot_height=700)
agg = cvs.points(df, "x", "y")

We can plot this aggregated data using the tf.shade function.

tf.Image.border=0
img = tf.shade(agg, cmap=inferno)
# img
export_image(img, "clifford_attractor_4", export_path="../assets/", )
clifford_attractor_4.png
help(ds.colors)
Help on module datashader.colors in datashader:

NAME
    datashader.colors

FUNCTIONS
    colormap_select(base_colormap, start=0, end=1.0, reverse=False)
        Given a colormap in the form of a list, such as a Bokeh palette,
        return a version of the colormap reversed if requested, and selecting
        a subset (on a scale 0,1.0) of the elements in the colormap list.

        For instance:

        >>> cmap = ["#000000", "#969696", "#d9d9d9", "#ffffff"]
        >>> colormap_select(cmap,reverse=True)
        ['#ffffff', '#d9d9d9', '#969696', '#000000']
        >>> colormap_select(cmap,0.3,reverse=True)
        ['#d9d9d9', '#969696', '#000000']

    hex_to_rgb(x)
        Convert a color hexcode to an rgb tuple.

        Example
        -------
        >>> rgb('#FFFFFF')
        (255, 255, 255)

    rgb(x)
        Return a triple representing rgb color.

        Can convert colors by name or hexcode. Passing in a valid rgb tuple is
        idempotent.

        Example
        -------
        >>> rgb('plum')
        (221, 160, 221)
        >>> rgb('#FFFFFF')
        (255, 255, 255)
        >>> rgb((255, 255, 255))
        (255, 255, 255)

DATA
    Elevation = ['aqua', 'sandybrown', 'limegreen', 'green', 'green', 'dar...
    Greys9 = ['#000000', '#252525', '#525252', '#737373', '#969696', '#bdb...
    Hot = ['black', 'maroon', 'darkred', 'red', 'orangered', 'darkorange',...
    Set1 = ['#e41a1c', '#377eb8', '#4daf4a', '#984ea3', '#ff7f00', '#ffff3...
    Set2 = ['#66c2a5', '#fc8d62', '#8da0cb', '#e78ac3', '#a6d854', '#ffd92...
    Set3 = ['#8dd3c7', '#ffffb3', '#bebada', '#fb8072', '#80b1d3', '#fdb46...
    Sets1to3 = ['#e41a1c', '#377eb8', '#4daf4a', '#984ea3', '#ff7f00', '#f...
    annotations = _Feature((3, 7, 0, 'beta', 1), (3, 11, 0, 'alpha', 0), 1...
    color_lookup = {'aliceblue': '#F0F8FF', 'antiquewhite': '#FAEBD7', 'aq...
    inferno = [(0, 0, 3), (0, 0, 4), (0, 0, 6), (1, 0, 7), (1, 1, 9), (1, ...
    viridis = [(68, 1, 84), (68, 2, 85), (69, 3, 87), (69, 5, 88), (69, 6,...

FILE
    /home/chahak/.local/share/virtualenvs/chahak13.github.io-6_wF4i83/lib/python3.10/site-packages/datashader/colors.py