Effective Quadratures ported to Virtual Reality

In the past few years, we have been witnessing the dawn of so-called immersive technologies. One of the most promising branches of such tech is Virtual Reality (VR), which transfers its users into the bespoke, 3D environment. VR can be used to effectively visualize and simultaneously aid the users in understanding complex and convoluted datasets by allowing novel, more empowering, and more intuitive interaction methods.

In this first introductory blog post, we demonstrate how relatively easy it is to port results produced by Effective Quadratures into Virtual Reality and combine these two technologies to obtain even more engaging data visualization. As an example, we will show how to visualize in VR histograms generated by Effective Quadratures and described in here.

To make it easier will we will show how to couple the Effective Quadratures with VR in these three simple steps:

  1. Run from within Unity the Effective Quadratures to generated the data for the histogram and save these data into an output text file.
  2. Read the generated data directly from the data text file.
  3. Plot the histogram in VR.

The Python script eq_plot_hist.py that we will be using to generate the data file looks as follows:

from equadratures import *
import numpy as np
import matplotlib.pyplot as plt

SAVE_PATH_WIN = "Assets/StreamingAssets/"

def save_hist_data(n, bins, file_name="his_data.txt"):
    with open(file_name, 'w') as out:
        out.write(str(len(n)) + '\n')
        for bin_len in n: out.write(str(bin_len) + '\n')
        out.write(str(len(bins)) + '\n')
        for edges in bins: out.write(str(edges) + '\n')

def main():
    VALUE = 15
    plt.rcParams.update({'font.size': VALUE})

    # Create some random data sets and add them together!
    param1 = np.random.rand(1000)
    param2 = np.random.randn(1200)
    param3 = np.random.randn(1300)*0.5 - 0.2
    param4 = np.random.randn(300)*0.1 + 3
    data = np.hstack([param1, param2, param3, param4])
    s = Parameter(distribution='custom', data=data, order=3)
    s_values, pdf = s.get_pdf()
    s_values, cdf = s.get_cdf()
    s_samples = s.get_samples(6000)

    fig = plt.figure()
    ax = fig.add_subplot(1,1,1)
    ax.set_axisbelow(True)
    plt.plot(s_values, pdf, '-', c='crimson', lw=4)
    plt.fill_between(s_values,  pdf*0.0, pdf, color="crimson" , interpolate=True, hatch="\\\\\\\\", edgecolor="grey",  linewidth=0.5,alpha=0.5)
    n, bins, patches = plt.hist(data, 100, normed=1, facecolor=None, alpha=0.7, edgecolor='k')
    plt.xlabel('$s$', fontsize=VALUE)
    plt.ylabel('PDF', fontsize=VALUE)
    plt.grid()
    plt.savefig(SAVE_PATH_WIN + 'tutorial_1_fig_e.png', dpi=200, bbox_inches='tight')
    save_hist_data(n, bins, file_name=SAVE_PATH_WIN + "hist_data_fig.txt")

if __name__ == "__main__":
	main()

The execution of this script should result in generating a dataset in the form of a text file and save a plot in the PNG form that will be looking similar to this one:

Our VR environment will be facilitated by the Unity game engine used as our main development platform. Here, we have two options, namely C# or JavaScript, for selecting the programming language to prepare scripts for the game engine side. We will not be going into the details of preparing all the elements of VR-based visualization as those can be learned through various online courses. However, we will show the main scripts related to reading the data generated by Effective Quadratures and visualizing the histogram in Unity/VR.

The C# script GenerateHistogram.cs that we will be using to generate the data file has five key components:

(1) Setting the attributes and initial values.

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Linq;
using System;
using UnityEngine.SceneManagement;

public class GenerateHistogram : MonoBehaviour {

    public GameObject cube;
    public RunPython pythonCallLib;

    public bool plotDistributionHist = false;
    public float scalingFactor = 100f;
    float objSize = 1f; // unit sized 1x1x1 cube
    
    float[] bins;

    public string binsFile = "hist_data_fig_e.txt";
    public string pythonScript = "eq_plot_hist.py";

(2) The main controlo sequence: first we generated the data file with Effective Quadratures in GenerateHistData(), then, we read the bins numbers and heights from this file in ReadBins(). Next we prepare an array of GameObject each for one bin, and fianlly we assign the bins’ heights into the individual GameObjects in PlotHistogram().

    void Start()
    {
        GenerateHistData();
        float[] bins = ReadBins(binsFile);
        Debug.Log(bins.Length);
        GameObject[] binsObj = new GameObject[bins.Length];
        PlotHistogram(binsObj, bins);
    }

(3) The Python scripts (and other resources) have to be placed in a certain relative path into the Unity project directory.

    void GenerateHistData()
    {
        string script = "Assets\\StreamingAssets\\" + pythonScript;
        pythonCallLib.RunPythonScript(script);
    }

(4) Now we are reading and returning the bin widths generated with Effective Quadratures. The scalingFactor was selected to give a better overview of the bins.

    float[] ReadBins(string fileName)
    {
        String Path = Application.dataPath + "/StreamingAssets/" + fileName;
        Debug.Log(Path);
        try
        {
            using (StreamReader reader = new StreamReader(Path, Encoding.Default))
            {
                string line = reader.ReadLine();
                int numberOfBins = int.Parse(line);
                bins = new float[numberOfBins];

                for (int i = 0; i < numberOfBins; i++)
                {
                    bins[i] = float.Parse(reader.ReadLine()) * scalingFactor;
                }
                reader.Close();
            }
        }
        catch (Exception e)
        {
            Console.WriteLine("{0}\n", e.Message);
        }

        return bins;
    }

(5) Plotting the histograms means instantiating the GameObjects in Unity, in this case we are using a primitive game object cube whose width will be adjusted to reflect the bins width.

    void AssignProperties(Transform objTransform, float x, float y, float z, int objCount, Vector3 dimmensions)
    {
        objTransform.transform.parent = this.transform;
        objTransform.transform.name = "object" + objCount;
        objTransform.transform.localScale = dimmensions * objSize;
        objTransform.transform.localPosition = new Vector3(x, y, z);
    }

    void PlotHistogram(GameObject[] objArray, float[] bins)
    {
        int half = (int)(bins.Length * 0.5f);
        for (int i = 0, j = -half; i < bins.Length; ++i, ++j)
        {
            objArray[i] = Instantiate(cube);
            AssignProperties(objArray[i].transform, j, bins[i] * 0.5f, 0, i, new Vector3(1f, bins[i], 1f));
            MeshRenderer renderer = objArray[i].GetComponent<MeshRenderer>();
            renderer.material.color = UnityEngine.Random.ColorHSV();
        }
    }
}

If everything went without an issue, execution of the Unity application from within the editor would look similar to this:

and yield similar results to this:

2 Likes

This is terrific @stadeja! Many thanks! A few follow-up queries:

  • Would it be possible to show multiple probability density functions (perhaps lying parallel to each other on the x-y plane)?

  • Could the user alter these distributions real-time?

  • Effective Quadratures automatically outputs a text file that summarizes output statistics (example shown below). Would it be possible to “say” this in VR?

Your problem has been defined by 7 parameters.
Parameter 1 is a uniform distribution over the support 30.0 to 60.0.
Parameter 2 is a uniform distribution over the support 0.005 to 0.02.
Parameter 3 is a uniform distribution over the support 0.002 to 0.01.
Parameter 4 is a uniform distribution over the support 1000.0 to 5000.0.
Parameter 5 is a uniform distribution over the support 90000.0 to 110000.0.
Parameter 6 is a uniform distribution over the support 290.0 to 296.0.
Parameter 7 is a uniform distribution over the support 340.0 to 360.0.

A summary of computed output statistics is given below:
The mean is estimated to be 0.462 while the variance is 0.019.
For the data avaliable, the polynomial approximation had a r square value of 1.
Additionally, the most important parameter–based on the total Sobol indices–was found to be parameter 2.