aifig 0.1.7
A machine learning figure generation library

# AI Figures

## Purpose

AIFIG is a python library for generating figures of machine learning models.

The libary allows you to generate figures such as the following, which may be useful for use in presentations, papers etc.

<img src="https://raw.githubusercontent.com/evgiz/aifig/master/img/fig_gan.png">

**AIFIG** is a refactored version of some of my personal code. Functionality will naturally be limited and not suited for every use. I encourage anyone who is interested to contribute with additional features.

If you use AIFIG in a paper, you can cite the library like this (bibtex):

```latex
@misc{aifig,
  author = {Sigve Rokenes},
  title = {AI-FIG},
  year = {2019},
  publisher = {GitHub},
  journal = {GitHub repository},
  howpublished = {\url{https://github.com/evgiz/aifig}}
}
```

## Install

AI-FIG library with svg export:
```bash
pip install aifig
```

If you need to export as png or pdf:
```bash
pip install svglib
```

## Usage

#### Simple example

```python
# Import library
import aifig

# Create new figure, title and author is optional
my_figure = aifig.figure("Figure 1", "Sigve Rokenes")

# Figures consist of graphs (eg. each network in a model)
my_graph = aifig.graph("gen")

# Graphs contain elements (inputs, outputs, layers)
my_graph.add(aifig.dense("input", 16))
my_graph.add(aifig.dense("hidden_1", 64))
my_graph.add(aifig.dense("hidden_2", 128))
my_graph.add(aifig.dense("hidden_3", 64))
my_graph.add(aifig.dense("output", 1))
my_graph.add(aifig.arrow("prediction"))

# Add the graph to the figure at position (0,0)
my_figure.add(graph, 0, 0)

# Save the figure 
my_figure.save_png("my_figure.png", scale=1)
my_figure.save_svg("my_figure.svg")
my_figure.save_pdf("my_figure.pdf")
```

*The above code generates this figure:*

<img src="https://raw.githubusercontent.com/evgiz/aifig/master/img/fig_simple.png">

#### Multi-graph example (GAN model)

```python
import aifig
figure = aifig.figure()

# Define generator network
generator_elements = [
  aifig.dense("noise_vector", 128, comment="norm_dist", simple=True),
  aifig.conv("tconv_1", 48, comment="5x5"),
  aifig.conv("tconv_2", 32, comment="5x5"),
  aifig.conv("tconv_3", 8, comment="5x5"),
  aifig.conv("tconv_4", 3, comment="5x5"),
  aifig.image("gen_result", comment="(fake image)")
]

# Define discriminator network
discriminator_elements = [
  aifig.image("image_input", comment="real/fake"),
  aifig.conv("conv_1", 16, comment="5x5"),
  aifig.pool("max_pool")
  aifig.conv("conv_2", 32, comment="5x5"),
  aifig.pool("max_pool"),
  aifig.conv("conv_3", 48, comment="5x5"),
  aifig.dense("dense_1", 64),
  aifig.dense("output", 1),
  aifig.arrow("prediction", comment="log prob")
]

# Create graphs with elements
gen_graph = aifig.graph("gen", generator_elements)
dsc_graph = aifig.graph("dsc", discriminator_elements)
dat_graph = aifig.graph("dat", [aifig.image("real_image", comment="(dataset)")])

# Add graphs to figure
figure.add(gen_graph, 0, 0)
figure.add(dat_graph, 1, 0)
figure.add(dsc_graph, 0, 1)

# Connect inputs to discriminator network
figure.connect("gen", "dsc")
figure.connect("dat", "dsc")

# Save figure as png
figure.save_png("gan.png")
```

*This code generates the following figure:*

<img src="https://raw.githubusercontent.com/evgiz/aifig/master/img/fig_gan.png">

## API

A figure consists of one or more graphs. These graphs are placed in a grid using `figure.add(graph, x, y)`. You can add elements to graphs using `mygraph.add(element)`, and you can connect graphs with arrows using `figure.connect("graph_name1", "graph_name2")`.  Finally, to save a figure, use `my_figure.save_svg("fig.svg")` or variants for different formats.

```python
# ===================== #
#        Figure         #
# ===================== #
#  title			figure title
#  author			figure author
my_figure = aifig.figure()

# figure.add
#  graph			graph to add
#  x				x position in grid
#  y				y position in grid
my_figure.add(graph, 0, 0)

# figure.connect
#  from				name of first graph
#  to				name of second graph
#  position			grid position of arrow, use this if 
#				different arrows overlap
#  offset			arrow offset in units, useful to
#				distinguish different arrows at same position
my_figure.connect("graph1", "graph2")

# figure.save (path)
#  path				file path to save to
#  scale			upscale (png only)
#  debug			enable debug draw mode
my_figure.save_png("my_figure.png", scale=1)
my_figure.save_svg("my_figure.svg")
my_figure.save_pdf("my_figure.pdf")

# ===================== #
#         Graph         #
# ===================== #
# 	name			(required)
# 	elements		[list of elements]
# 	spacing			(between elements, default 32)

my_graph = aifig.graph("graph_name")
my_graph.add(element)

# ===================== #
#    Layer elements     #
# ===================== #
# 	label			text label, use None to hide
# 	size			size of layer (nodes, filters)
#	comment			additional comment text
#	size_label		set to False to hide size label
#	simple			(dense only) set True to render as simple rectangle

dense = aifig.dense()		# Dense (fully connected)
conv  = aifig.conv()		# Convolutional layer

# ===================== #
#    Simple elements    #
# ===================== #
#	label			text label, use None to hide
#	comment			additional comment text

pool  = aifig.pool()		# Pooling layer
image = aifig.image()		# Image (usually input)
arrow = aifig.arrow()		# Arrow

# ===================== #
#    Special elements   #
# ===================== #
# 	width			width of padding (use negative to reduce)

padding = aifig.padding(10) 
```



### Dependencies

- svgwrite 
- svglib (only to save as pdf/png)
- reportlab (only to save as pdf/png)