Home

Published

- 2 min read

Pyodide


The page uses Pyodide to make it possible to run Python code in a browser via WebAssembly.

Demo

Explanation

The demo basically does numerical integration to generate characteristic function of a Normal Distribution. It does so using Python libraries numpy and scipy because doing these in javascript is somewhat very limited. The Python script is executed in WebAssembly context using Pyodide.

If you click the button above, it will execute the following Python block:

   import numpy as np
from scipy.integrate import quad
from scipy.stats import norm


def complex_quad(func, a, b, **kwargs):
    def real_func(x):
        return func(x).real

    def imag_func(x):
        return func(x).imag

    real_integral = quad(real_func, a, b, **kwargs)
    imag_integral = quad(imag_func, a, b, **kwargs)
    return real_integral[0] + 1j * imag_integral[1], real_integral[1:], imag_integral[1:]


def characteristic_function(pdf):
    def _c(t):
        integrand = lambda x: np.exp(1j * t * x) * pdf(x)
        return complex_quad(integrand, -np.inf, np.inf)[0]

    return _c


# Example usage with Standard Normal distribution
norm_pdf = norm().pdf
dist = norm(loc=0, scale=0.001)
neg_freq = norm(loc=-1, scale=0.001).pdf
pos_freq = norm(loc=1, scale=0.001).pdf


def binomial_pdf(x):
    return (neg_freq(x) + pos_freq(x)) / 2


t_values = np.arange(-100, 100) / 100  # array of t from 0 to 100
cf = np.vectorize(characteristic_function(norm_pdf))
original_distribution = (np.vectorize(norm_pdf))(t_values)
complex_results = cf(t_values)
# Return t-values and results as list of list
real_parts = list(map(np.real, complex_results))
imag_parts = list(map(np.imag, complex_results))

# Return t-values and parts as list of list
results = [t_values.tolist(), original_distribution.tolist(), real_parts, imag_parts]
results

After that the following javascript block will read the result. We then use Plotly to render the data in JavaScript, so that we can interact with the chart.

   function generatePlot() {
  const result = window.pythonResult
  const generatedID = window.pyodideElementID
  const plotID =`${generatedID}-plot`
  let plotDiv = document.getElementById(plotID)
  console.log(plotDiv)
  if(!plotDiv) {
    plotDiv = document.createElement('div')
    plotDiv.id = plotID
    const parentDiv = document.getElementById(generatedID)
    console.log(parentDiv)
    parentDiv.prepend(plotDiv)
  }
  const t = result[0].map((i) => i)
  const o = result[1].map((i) => i)
  const r = result[2].map((i) => i)
  const im = result[3].map((i) => i)
  Plotly.newPlot(
    plotDiv,
    [
      {
        x: t,
        y: o,
        name: 'Normal Distribution',
        type: 'scatter',
        mode: 'lines',
        marker: { color: 'green' }
      },
      {
        x: t,
        y: r,
        name: 'Characteristic function - Real part',
        type: 'scatter',
        mode: 'lines',
        marker: { color: 'blue' }
      },
      {
        x: t,
        y: im,
        name: 'Characteristic function - Imaginary part',
        type: 'scatter',
        mode: 'lines',
        marker: { color: 'red' }
      }
    ],
    {
      title: 'Characteristic Function',
      xaxis: { title: 't' },
      yaxis: { title: 'Value' }
    }
  )
  document.getElementById(`${generatedID}-spinner`).classList.add('hidden')
}

generatePlot()

Related Posts

There are no related posts yet. 😢