Sunday, 7 April 2013

Makefiles for tikz sagemath and teaching notes written in markdown

So I've posted before on multiple occasions about PCUTL which is a higher education certification process I'm currently undergoing. One of the things I paid particular attention to in my latest portfolio (a copy of which can be found at the previous link) is accessibility of teaching notes.

Generally this implies making sure students have notes in a format that they can easily manipulate (for example change font size and color). This is often implied to mean that notes should be written in .doc format. As a mathematician this isn't really an option as all our teaching material really requires to be written in LaTeX.

I have spent some time being amazed by pandoc which is a really smart piece of software that allows you to take documents in various languages and output to a variety of other languages. Here's a video I made showing how I use like to use pandoc to get notes in html,pdf and .docx format (I wrote a blog post here about it):



My entire SAS/R programming course I'm currently teaching was written this way (you can find the notes etc here).

I've recently started writing a new course: a game theory course.

This course needs a bunch of diagrams and plots (as opposed to the previous that basically had a bunch of screenshots). I wanted to use the same overall system but planned on using tikz for the diagrams (tikz lets you draw nice diagrams using code) and perhaps thought about using sagetex (I use +Sage Mathematical Software System a lot but have never used sagetex which lets you have sage code inside of a LaTeX document) for the plots.
After searching around for a bit I didn't see how I was going to be able to use the awesome pandoc to create teaching notes in multiple formats using the above approach. There was no way for pandoc to take the tikz code and output a separate image file for the html (and whatever it needs for .docx).

This blog post is a summary of my approach.

Which basically uses a couple of things:
  • Some tikz code that creates a standalone pdf image;
  • A script that converts a pdf to a png (so that it can be used in html);
  • A makefile so that I can recompile only the tikz code that needs to be recompiled;
That is how I get my tikz diagrams.

I also have a similar system to create plots from sage and I'll also show the makefile I use to get pandoc to sort out the files I want.

Creating standalone pngs with tikz and makefiles


To create a standalone tikz image you need to use the standalone document class. Once you've done that you can just use normal tikz code:

\documentclass[tikz]{standalone}
\usepackage{tikz,amsmath}
\tikzstyle{end} = [circle, minimum width=3pt, fill, inner sep=0pt, right]
\begin{document}
\begin{tikzpicture}
    \draw (0,0) -- (4,0) node [below] {$u_1$};
    \draw (0,0) -- (0,4) node [left] {$u_2$};
    \draw (3,0) node[end] (A) {} node [above=.3cm,right] {$(3,0)$};
    \draw (0,3) node[end] (B) {} node [above=.3cm,right] {$(0,3)$};
    \draw (1,1) node[end] (C) {} node [below,left] {$(1,1)$};
    \draw (2,2) node[end] (D) {} node [above=.3cm,right] {$(2,2)$};
    \draw (A) -- (D);
    \draw (D) -- (B);
    \draw (B) -- (C);
    \draw (C) -- (A);
    \draw [dashed,thick] (C) -- ++(0,1.5);
    \draw [dashed,thick] (C) -- ++(1.5,0);
    \draw [->] (2,3.5) node[above] {\tiny{Feasible average payoffs}} -- (.75,2);
    \draw [->] (4,1.5) node[above] {\tiny{Individually rational payoffs}} -- (1.5,1.25);
\end{tikzpicture}
\end{document}

The above for example creates the following standalone image (note that if you pdflatex it you obviously get a pdf which would be great if you were just pdflatex'ing a bigger document but I'll get to creating a png next):



I've then written a short python script (I'm sure this could be done as easily in bash but I'm just more comfortable in python) that can act on any given tex file to convert it to png:

#!/usr/bin/env python
from sys import argv
from os import system

system("pdflatex %s" % (argv[1]))
system("convert -density 300 %s.pdf %s.png" % (argv[1][:-4], argv[1][:-4]))

This makes use of convert which is a program that can convert pdf to png (more info here but it's natively on Mac OS and *nix I believe).

I call that python file convert_tex_to_png.py and so I can convert any tex file by doing:

python convert_tex_to_png.py file.tex

I'm going to put all these image files (tex and png) in a folder called images and there's (now that I'm done) about 70 image files in there so every time I change or write a new tex file I don't want to compile all the files and I also don't want to have to waste time compiling the particular file I've written.

So I've used makefiles.

Now I've tried to use makefiles before but in all honesty they confuse the heck out of me. I spent a while going through a bunch of tutorials but most of them look at c code. For those who don't know, a makefile is basically a short program that gives instructions to compile certain files. When written well these are great as by simply typing make you can recompile any file that has been modified (and not necessarily all of them!).

So here's the makefile I've converged to using (after heuristically trying a bunch of stuff from stackoverflow):

tex  = $(wildcard *.tex)
pngs = $(tex:%.tex=%.png)

all: $(pngs)

%.png: %.tex
    ./convert_tex_to_png.py $<;

clean:
    rm *.aux
    rm *.log
    rm *.pdf

I save the above file calling it `makefile` so that it is run by default when calling `make`. This will take all the tex files (that have been modified) and output the corresponding png files as required. Note I can also type make clean to remove all auxiliary files (aux, log and pdf).

I find this really useful as I can modify a bunch of tex files and once I'm ready simply run make to get all the pngs as required.

I'll briefly show the (basically same) code I use to get plots from sage as standalone pictures.

Creating standalone pngs with sage and makefiles


I put all the plots created via sage in a plots directory. Here's an example graph created with sage:



The sage code to do that is here:

f(a,b)=a^3/3+3/4*b^2+(1-a-b)^2/2
p = contour_plot(f,(0,.5),(0,.5),colorbar=True,axes_labels=["$\\alpha$","$\\beta$"],contours=100, cmap='coolwarm',fill=True)
p.axes_labels(['$\\alpha$','$\\beta$'])
p.save("L18-plot01.png")

The makefile (which will keep all png files up to date based on the sage files in the directory):

sage  = $(wildcard *.sage)
pngs = $(sage:%.sage=%.png)

all: $(pngs)

%.png: %.sage
    sage $<;

clean:
    rm *.py

(I can similarly use make clean to remove the .py files that get created when running sage in cli).

The final thing I'll show is how to write a makefile to get pandoc to output the required file formats.

Using a makefile with pandoc


So I write all of my notes in markdown. For various reasons which include:
  • Pandoc likes markdown as input (it can also handle tex and html though);
  • I like markdown;
  • I can incorporate latex in markdown when needed.
  • If a student ever wanted to modify the markdown but didn't know LaTeX they'd have an honest chance of knowing what was going on (if you don't know markdown it really is worth taking a look at this short video)
Thus my makefile must be able to compile all modified markdown files. I use a similar approach to the approach I had for tikz diagrams which is to write a short python script:

#!/usr/bin/env python
from sys import argv
from os import system

e = argv[1][:-3]
print e

system("pandoc -s " + e + ".md -N -o " + e + ".html --mathjax")
system("pandoc " + e + ".md -o " + e + ".docx")
system("pandoc " + e + ".md -N -o " + e + ".pdf --latex-engine=xelatex")

(pandocipy_given_file.py is the name of the above python script).

There are basically three pandoc commands going on there. The first creates the html with the LaTeX math being rendered by mathjax, the second creates the .docx format and the last one creates the pdf (using the xelatex engine).

The final piece of the puzzle is to use the following makefile:

md = $(wildcard *.md)
htmls = $(md:%.md=%.html)

all: $(htmls)

%.html: %.md
    ./pandocipy_given_file.py $<;

The way that makefiles work (I think) is to indicate what files you want as an output (and what those files are dependent on). So for example my previous makefiles wanted to output pngs with tex and sage files as dependencies. Here I use the required output as html and the dependencies are the md files.

Using the above I can change any md file and simple type make to get all the pdf, html and docx files updated.

Summary and a couple of things I don't want to forget


I have 3 directories here. The first: Course_notes contains the md files and corresponding python script and makefile. Course_notes also contains 2 other directories: images and plots which both contain the images and plots as I've described above.

So in the md files I refer to an image as so:

![A pic for C1](images/C01-img03.png)


I'm using pdf as the prescribed format of the notes for the course (students are welcome to use html and docx if they wish but I'm recommending pdf) and in general xelatex will sometime move images around to better format the text.

To make sure things don't get confusing I want to be able to \ref and \label images. The easiest way I've found to do this without messing around with the html (who knows what happens with docx, life is too short to worry about everything) is to use the \text:

In the excellently drawn image shown\text{ in Figure \ref{C01-img03}} we see that...

![A pic for C1\label{C01-img03}](images/C01-img03.png)

Pandoc is clever enough to simply ignore what is in the \text when converting to html and docx so that you get documents in html and docx that won't have a reference to the figure (but in general they won't reorder the images) and the pdf will have the documents referenced as you can see in these screenshots (showing pdf and html):

pdf:

html:


I've mainly posted this to make sure I remember how to do it but it might be helpful for others. I'm sure it could be improved by getting rid of my python scripts and having everything in the makefile. I tried that but had a hard time getting wildcards to work as I did so I didn't try anything more complicated. Finally there are ways to get makefiles to run makefiles in subdirectories so it would be nice I guess to also do that so that a single `make` in the highest directory would sort out everything.

I'll be putting all the teaching notes up on github so will link to that repository when I've done it.

EDIT: Here's the github repo and here's a website with all the content (which will eventually become the class website).

ANOTHER EDIT: Here's a follow up post which uses sed to keep the links in the right format.