Basics or algorithm visualization


  1. Intro
  2. Installation
  3. Connecting and creating a container
  4. Generating and changing data
  5. Markers
  6. Trees
  7. Adding markers to trees
  8. Graphs

Intro

Using AlVi is easy. You write some Python code and AlVi automagically visualizes the results. Oh, and yes, there is an interactive version of this tutorial, so you can alter the code and see what happens - try the interactive version.

If you want to reveal the magic and see what's happening under the hood read about section.

After going through that tutorial you will be able to dynamically visualize arrays, trees and graphs and achieve results like these ones:

Array
Tree
Graph

Installation

AlVi is designed to run inside Jupyter notebooks. The easiest way to start is to use the interactive tutorial where AlVi is already pre-installed. Alternatively you can install it inside any existing Jupyter notebook, either on your machine or hosted in the cloud, like this one try.jupyter.org. Just create a new notebook and run the following code:

import pip
pip.main(['install', 'alvi'])

Connecting and creating a container

Once you have AlVi installed you have to import it and connect to a server. If the server is not running it will be kicked off and work in the background. Connecting is omnipotent, which is a complicated way of saying that you can do as many times as you want and nothing wrong will happen. You don't have to analyze all the details and investigate what's happening once you start the server and well, if you want to, you most likely already read about section, right? So, making the long story short, here is the relevant code:

import alvi
client = alvi.connect()

Once you are are connected you can to create so called container. That can be an array, a tree or a graph. Let's start with an array:

array = client.create_array()

AlVi visualises arrays using cartesian system. Each element of an array is shown as a dot, place on x-axis visualizes index, while place on y-asis visualizes value. Let's generate some data to see how it looks.

Generating and changing data

array.generate_data("random", 20)

The command above generates N (20 in this particular case) elements with random values. Values are in range between 0 and N-1. It's useful for filling arrays with sample data, before you start implementing the main part.

Once you generate the data, you can modify it by simply assigning new values to the elements of the array. The dot that visualizes particular element will simply flow to the position that corresponds to the new value. The code below shows how to set descending values:

for i in range(array.size()):
    array[i] = array.size()-i
    array.sync()

Method "sync()" is available in every container and it synchronizes visualization (the displayed part) with data from your code. In the above code we are synchronizing the status (and then moving modified dots) per every assignment. If call to "sync()" would be moved outside of the loop then visualization would be done in one step by moving all dots to the new places. It's up to you to decide how often you wants to sync the data.

Markers

Sometimes you don't want to change the value, but rather mark a particular element. It can be useful when designing searching algorithms. The code below shows how to do it:

marker = array.create_marker("sample", array.size()-2)
array.sync()

As before, "sync()" is used to actually show the marker. Once marker is created you can move it to any element by calling the following code: marker.move(i), where "i" is an index of the targeted element. Don't forget to call "sync()" after you move the marker, otherwise you won't see any change.

List visualization summary

Here it the full code that connects to the server (spawning it it it wasn't running so far), creates an array fills it with some data, changes the data and marks one of the nodes:

import alvi
client = alvi.connect()
array = client.create_array()
array.generate_data("random", 20)
for i in range(array.size()):
    array[i] = array.size()-i
    array.sync()
marker = array.create_marker("sample", array.size()-2)
array.sync()

That's less than 10 lines of code, easy, isn't it?

Visualizing trees

Visualizing tree algorithms is not any harder than in case the ones that work on arrays. To some extends it's even easies as it more natural to draw a tree when you are working on the related data structure, than it's the case for array.

The code below creates a tree with N nodes. Each of the new nodes is added as a child to one of previously added nodes. Values are random (between 0 and N).

import alvi
import random

client = alvi.connect()
tree = client.create_tree()  # a new tree is always empty (has no nodes)
nodes = []  # we need a list of nodes to be able to randomly chose parents
node = tree.create_root(0)
nodes.append(node)
tree.sync()
N = 6
for i in range(N-1):
    value = random.randint(0, N)
    x = random.randint(0, i)
    parent = nodes[x]  # randomly choosing one of the previous nodes as the parent
    node = parent.children.create(value)
    nodes.append(node)
    tree.sync()

Adding markers to trees

You can mark nodes of the tree using markers or multi markers. The following code creates a tree, adds some nodes and then marks some of them. You can either use single node markers (like "marker1" in the following example), or create multi markers ("marker2") to higlight several nodes with the same color.

import alvi
client = alvi.connect()
tree = client.create_tree()
root = tree.create_root(0)
node1 = root.children.create(1)
node2 = root.children.create(2)
node3 = node1.children.create(3)
node4 = node1.children.create(4)
tree.create_marker("marker1", node1)
marker2 = tree.create_multi_marker("marker2")
marker2.append(node2)
marker2.append(node3)
marker2.append(node4)
tree.sync()
marker2.remove(node4)
tree.sync()

Graphs

Last, but not least you can visualize graphs. The following code creates a graph with N nodes. Every node is a child of one of previous nodes (randomly chosen). On top of that every "edge_factor" node is connected to another, randomly chosen node.

import alvi
import random
client = alvi.connect()

edge_factor = 3
N = 10
nodes = []
value = random.randint(0, N)
graph = client.create_graph()
node = graph.create_node(value)
graph.sync()
nodes.append(node)
for i in range(N):
    value = random.randint(0, N)
    x = random.randint(0, i)
    parent = nodes[x]
    node = parent.children.create(value)
    graph.sync()
    nodes.append(node)
    if i % edge_factor == 0:
        x = random.randint(0, i)
        node1 = nodes[x]
        node.create_edge(node1)
        graph.sync()