Some Outstanding Projects
Stephen A. Edwards
Columbia University
Spring 2021
Mathematical Languages: Mx, CAL
Graphics Languages: CAL, CLAM, curve
C- and Java-Like Languages: Cpi, Dice
Hardware Description Languages: EHDL
Music Languages: Note-Hashtag
Mx: A Programming Language for Scientific
Computation
Tiantian Zhou, Hanhua Feng, Yong Man Ra, Chang Woo Lee 2003
Matlab-like language
Ï Matrix literals, slicing (e.g., a[0,:])
Ï User-defined functions; functions as first-class objects
Ï Expression-only and imperative-style function
declarations
Compiled into Java with an extensive matrix library∗
∗
This is no longer allowed; you must compile into LLVM
Example
Plotting the Lorenz equations
d y0
= α(y 1 − y 0 )
dt
d y1
= y 0 (r − y 2 ) − y 1
dt
d y2
= y0 y1 − b y2
dt
a = 10; /* Parameters for the Lorenz Equations */
b = 8/3.0;
r = 28;
func Lorenz ( y, t ) = [ a*(y[1]-y[0]); /* Matrix literal */
-y[0]*y[2] + r*y[0] - y[1];
y[0]*y[1] - b*y[2] ];
func RungeKutta( f, y, t, h ) { /* Differential Equation Solver */
k1 = h * f( y, t ); /* Invoke function f */
k2 = h * f( y+0.5*k1, t+0.5*h );
k3 = h * f( y+0.5*k2, t+0.5*h );
k4 = h * f( y+k3, t+h );
return y + (k1+k4)/6.0 + (k2+k3)/3.0;
}
N = 20000;
p = zeros(N+1,3); /* matrix of zeros */
t = 0.0;
h = 0.001;
x = [ 10; 0; 10 ]; /* matrix literal */
p[0,:] = x’; /* matrix transpose */
for ( i = 1:N ) {
x = RungeKutta( Lorenz, x, t, h ); /* Perform a step */
p[i,:] = x’;
t += h;
}
colormap(3);
plot(p); /* Plot points in the matrix */
return 0; /* Terminate */
YAPPL: Yet Another Probabilistic Programming
Language
David Hu, Jonathan Huggins, Hans Hyttinen, Harley McGrew, 2011
For programming statistical models: Church-inspired
language
Ï OCaml-like functional syntax with explicit types
Ï fun keyword for defining functions
Ï Imperative code, too
Compiled to OCaml∗
∗
This is no longer allowed; you must compile into LLVM
###
An implementation of the Dirichlet Process (DP) using memoization
###
fun float:beta float:a float:b = ~rand in
# get a stick, breaking more if necessary
fun int:pickastick (fun float int):sticks int:j =
if ~rand < ~sticks j then j else ~pickastick sticks j+1
in
# generic Dirichlet process code
fun (fun int):DP float:alpha (fun int):proc =
fun float:sticks int:x := ~beta 1.0 alpha in
fun int:atoms int:x := ~proc in
fun int:f = ~atoms ~pickastick sticks 1 in
f # return f
in
fun (fun (fun int) float):DPmem float:alpha (fun int float):proc =
fun (fun int):dps float:arg :=
fun int:apply = ~proc arg in
~DP alpha apply
in
fun (fun int):dp float:arg = ~dps arg in
dp
in
# this function will create Dirichlet process draws with geometric base distribution
let (fun (fun int) float):geom_dp = ~DPmem 1.0 geom in
# this is a DP draw with geometric base distribution with q = .2
let (fun int):mydraw = ~geom_dp .2 in
# use a tail-recursive loop to generate some samples from the Dirichlet Process
fun bool:loop int:i =
~print ~mydraw;
if i > 0 then ~loop i - 1 else true
in
~seed;
~loop 30; ~print_line ~mydraw
CAL: Concise Animation Language
Tianliang Sun, Xinan Xu, Jingyi Guo, 2013
Ï C-like syntax
Ï User-defined functions
Ï Structs
Ï OpenGL calls
C-like language compiles into LLVM IR linked to OpenGL
int i = 0, j = 0, size = 10;
struct point_or_shape {
point pt;
shp.size=0.2;
shape shp;
shp.x=0.2*j+0.1-1.0;
};
shp.y=-0.2*i-0.1+1.0;
shp.vy=shp.x/2.0+shp.y;
int add_point_or_shape(int x, int y,
shp.vx=shp.y/2.0-shp.x;
struct point_or_shape pos){
shp.r=shp.x/2.0+0.5;
if(x == y || x == size - y - 1)
shp.g=shp.y/2.0+0.5;
add_shape(pos.shp);
shp.b=1.0;
else
shp.omega=1.0;
add_point(pos.pt);
pos.pt = pt;
return 0;
pos.shp = shp;
}
wait(0.05);
add_point_or_shape(j, i, pos);
int main(){
}
struct point_or_shape pos;
}
point pt;
for(i=0;i<size*size;i=i++){
shape shp;
wait(0.05);
pop_shape();
for(i = 0; i < size; i=i++){
pop_point();
for(j = 0; j < size; j=j++){
}
pt.x=0.2*j+0.1-1.0;
pt.y=-0.2*i-0.1+1.0;
return 0;
pt.vx=pt.y+pt.x;
}
pt.vy=pt.x-pt.y;
pt.r=pt.x/2.0+0.5;
pt.g=pt.y/2.0+0.5;
pt.b=0.0;
CLAM: Concise Linear Algebra Manipulation
Language
Jeremy Andrus, Robert Martin, Kevin Sun, Yongxu Zhang, 2011
Image-processing language
Ï Images with multiple channels (arrays, e.g., Red, Green)
Ï Calculations: either literal C code or matrices
Ï Kernel: sequence of calculations assembled with |
Ï Convolution operator **
Compiles into C++ with extensive use of templates∗
∗
This is no longer allowed; you must compile into LLVM
Image srcimg = imgread(1);
/* Calc: functions on images */
/* # is "escape to C" */
Calc Lum := #[(3*Red + 6*Green + 1*Blue)/10]#;
Calc sobelG<Uint8>:=
#[sqrt((float)sobelGx*sobelGx + (float)sobelGy*sobelGy)]#;
Calc sobelTheta<Angle>:= #[atan((float)sobelGy/(float)sobelGx)]#;
srcimg |= Lum; /* Calculate luminance of source image */
Calc sobelGx<Uint8> := [1 / 1]{ -1 0 +1 , /* Convolution kernel */
-2 0 +2 ,
-1 0 +1 };
Calc sobelGy<Uint8> := [1 / 1]{ +1 +2 +1 ,
0 0 0 ,
-1 -2 -1 };
Kernel sobel = | @sobelGx | @sobelGy | sobelG; /* Build up kernel */
sobel |= sobelTheta; /* Add another kernel */
Image edges = srcimg:Lum ** sobel; /* Convolve with sobel */
Image output;
output:Red = edges:sobelG; /* Output B&W */
output:Green = edges:sobelG;
output:Blue = edges:sobelG;
imgwrite( output, "png", 2);
curve: Vector Graphics Animation
Kun An, John Chan, David Mauskop, Wisdom Omuya, Zitong
Wang, 2012
C-like language for animating vector graphics
Ï int, Point, Curve, and Layer types
Ï Wrote their own standard library with functions like
rectangleXY
Compiles into bytecode and interpreted
int drawTree(int x, int y, int n) {
Curve left;
Curve right;
if (n == 0) return 1;
drawTree(x - exp(2, n), y - 50, n - 1);
drawTree(x + exp(2, n), y - 50, n - 1);
left = lineP((x, y), (x - exp(2, n), y - 50));
right = lineP((x, y), (x + exp(2, n), y - 50));
draw([left, right]);
pause(100);
return 1;
}
Cpi: A C dialect for the Raspberry Pi
Edward Garcia, Niket Kandya, Naveen Revanna, Sean Yeh, 2013
Stripped-down C
Ï Integers, characters, pointers, arrays, structs
Ï User-defined functions
Ï for, if, case, while statements
Compiles into ARM V6 assembly
int checkrow(char board[], int row){
int x1; int checkboard(char board[]){
int x2; int result;
x1 = row + 1; int j;
x2 = row + 2; result = 0;
if (board[row] == board[x1]){
if (board[x1] == board[x2]){ for (j = 0; j < 3; j = j + 1){
if (board[row] != ’ ’){ result = result +
printf("Row win!\n"); checkrow(board, 3*j) +
return 1; checkcol(board, j);
} }
}
} // Check diags
return 0; if (board[0] != ’ ’){
} if (board[0] == board[4]){
if (board[4] == board[8]){
int checkcol(char board[], int col){ result = 1;
int x1; }
int x2; }
x1 = col + 3; }
x2 = col + 6; if (board[2] != ’ ’){
if (board[col] == board[x1]){ if (board[2] == board[4]){
if (board[x1] == board[x2]){ if (board[4] == board[6]){
if (board[col] != ’ ’){ result = 1;
printf("Column win!\n"); }
return 1; }
} }
}
} return result;
return 0; }
}
board[0] = ’ ’; board[1] = ’ ’;
int printboard(char board[]){ board[2] = ’ ’; board[3] = ’ ’;
printf("|%c|%c|%c|\n", board[0], board[4] = ’ ’; board[5] = ’ ’;
board[1],board[2]); board[6] = ’ ’; board[7] = ’ ’;
printf("-------\n"); board[8] = ’ ’; board[9] = ’ ’;
printf("|%c|%c|%c|\n", board[3],
board[4],board[5]); printf("Player 1: ’O’\nPlayer 2: ’X’\n\n");
printf("-------\n"); printf("Valid inputs are 0-9\n\n");
printf("|%c|%c|%c|\n", board[6],
board[7],board[8]); count = 0; winner = 0; player = 1;
return 0;
} while (winner == 0){
printboard(board);
char getchar(int p){
if (p == 1){ valid = 0;
return ’O’; while(valid == 0){
} printf("Player %d, enter your move: ",
return ’X’; player);
} printf("\n");
int main() scanf("%d", &choice);
{
int player; valid = 1;
int winner; if (choice < 0){ valid = 0; }
int choice; if (choice > 9){ valid = 0; }
int valid; if (valid == 1){
int i; if (board[choice] != ’ ’){
int count; valid = 0;
char board[9]; }
char tempc; }
}
tempc = getchar(player);
board[choice] = tempc;
if (checkboard(board) > 0){
printboard(board);
printf("Winner is Player %d!\n", player);
winner = player;
}
if (player == 1) {
player = 2;
} else {
player = 1;
}
count = count + 1;
if (count >= 9){
if (winner == 0){
printf("No one wins!\n");
winner = -1;
}
}
}
return 0;
}
Dice: “Java, but worse”
David Watkins, Emily Chen, Philip Schiffrin, Khaled Atef, 2015
Simplified Java language
Ï Classes, inheritance
Ï Methods, virtual function dispatch
Ï Arrays
Ï Strings
Ï File I/O
Compiles to LLVM
include("stdlib");
class Player {
public class LocationObj placeTile(bool retry) {
return new LocationObj();
}
public void setResult(class LocationObj move) {
}
}
class HumanPlayer extends Player {
private class Board board;
public int myPieceType;
constructor() {
this.board = new Board();
this.myPieceType = 2;
class Board b = this.board;
b.initializeBoard();
}
public class LocationObj placeTile(bool retry) {
if (this.myPieceType == 2)
this.myPieceType = 1;
if (retry){
print("Last move was invalid. Retry.\n"); }
print("It’s your turn\n");
class Board b = this.board;
b.printBoard();
print("Please enter your move\n");
class LocationObj move = this.getLocationObjChoice();
int temp = this.myPieceType;
b.setPlayerMove(move, temp);
return move;
}
public void setResult(class LocationObj move) {
int temp = this.myPieceType;
if (temp == 1) {
bool one = (move.getHorizontal() == 3);
bool two = (move.getHorizontal() == 4);
bool three = (move.getVertical() == 3);
bool four = (move.getVertical() == 4);
bool five = ((one or two ) and (three or four));
if(not five){
this.myPieceType = 0;
}
}
int opponentPieceType;
int temp2 = this.myPieceType;
if (temp2 == 0){
opponentPieceType = 1; }
else {
opponentPieceType = 0;}
class Board b = this.board;
b.setPlayerMove(move, opponentPieceType);
}
private class LocationObj getLocationObjChoice(){
char[] userInput;
class String uInput;
class Board b = new Board();
class LocationObj move = null;
int temp = this.myPieceType;
while (not (b.isValid(move, temp))) {
print("You are " , this.myPieceType , ". What is the x location of your next move?");
userInput = input();
uInput = new String(userInput);
int x = uInput.toInteger();
print("You are " , this.myPieceType , ". What is the y location of your next move?");
userInput = input();
uInput = new String(userInput);
int y = uInput.toInteger();
move = new LocationObj(x - 1, y - 1);
bool one = b.isValid(move,temp);
if (not one){
print("invalid move, try again.\n"); }
}
return move;
EHDL: Hardware Description Language
Paolo Mantovani, Mashooq Muhaimen, Neil Deshpande, Kaushik
Kaul, 2011
Ï Bit vectors/binary numbers of a specific width
Ï User-defined functions
Ï If-then-else, switch-case
Ï POS denotes clock boundaries in imperative code
Ï while loops have an implicit clock
Ï Arrays for little memories
Compiles into VHDL∗
∗
This is one of the few possible exceptions to the LLVM
backend rule. You need to convince me that a
non-LLVM-backend is the best choice for your project.
(int(1) sum, int(1) carry) fulladder(int(1) a, int(1) b, int(1) carryin){
sum = a ^ b ^ carryin;
carry = (a && b) ^ (carryin && (a ^ b));
}
(int(4) s, int(1) overflow) main(int(4) a, int(4) b, int(1) carryin) {
int(1) sum[4];
int(1) carry[4];
(sum[0], carry[0]) = fulladder(a(0),b(0),carryin);
(sum[1], carry[1]) = fulladder(a(1),b(1),carry[0]);
POS(1);
(sum[2], carry[2]) = fulladder(a(2),b(2),carry[1]);
(sum[3], carry[3]) = fulladder(a(3),b(3),carry[2]);
POS(1);
s(3) = sum[3]; s(2) = sum[2];
s(1) = sum[1]; s(0) = sum[0];
if ((a>0) && (b>0) && (sum[3]<0) )overflow = 1;
else if ((a<0) && (b<0) && (sum[3]>0) )overflow = 1;
else overflow = 0;
}
/* Sieve of Eratosthenes */
/* emits all the prime numbers less than m. m must be less than 200
as there is a bounded buffer of size 200 that is being used */
(int(32) primes=2) main (int(32) m) {
int(1) a[200];
int(1) sig;
int(32) n = 2;
int(32) k = 2;
while (n <= m) {
if ((a[n] == 0) && (k <= m)) {
if (k == n) {
primes = n; /* generate output */
} else {
a[k] = 1;
}
k = k + n;
}else {
n = n + 1;
k = n + 1;
}
} /* Implicit clock cycle here */
}
Note-Hashtag: Music Synthesis Language
Kevin Chen, Brian Kim, Edward Li, 2015
Ï Vectors of notes with durations
Ï Functional-like transformations (e.g., scale up two
pitches)
Ï Rhythm can be projected on a melody
Ï Melody can be projected onto a key signature
Ï User-defined composite types
Generates C++ code that produces a .WAV file∗
∗
Now, would have to compile into LLVM that, when run,
produces a .WAV file
// Twinkle, Twinkle Little Star
// main parts
intro = quarter:[ 1 1 5 5 6 6 ] . half:5
chorus = Rhythms intro : [ 4 4 3 3 2 2 1 ]
bridge = Relative 1 chorus
// the tune
twinkle_melody = intro . chorus . bridge . bridge . intro . chorus
twinkle_harmony = Relative 2 twinkle_melody
// supporting line
base = eighth:[ 1 5 3 5 ]
rise = eighth:[ 1 6 4 6 ]
fall = eighth:[ 7@(-1) 5 2 5 ]
bottom = eighth:[ 6@(-1) 5 1 5 ]
intro_accomp = base . base . rise . base
chorus_accomp = fall . base . bottom . base
bridge_accomp = base . fall . base . fall
// the accompaniment
accomp = intro_accomp . chorus_accomp . bridge_accomp . \
bridge_accomp . intro_accomp . chorus_accomp
twinkle_bass = Octave (-1) accomp
// the song
twinkle = Parallel { twinkle_melody twinkle_harmony twinkle_bass }
twinkle$volumes = { 1.0 0.5 0.5 }
Render twinkle "twinkle.wav"
tempo = 74
// stairway to heaven - led zeppelin
intro = eighth : [ 6@(-1) 1 3 6 7,5# 3 1 7 ] .\
e : [ 1@1,5 3 1 1@1 4#,4#@(-1) 2 6@(-1) 4 ] .\
e : [ 3,4@(-1) 1 6@(-1) ] . q:1 . e : [ 3 1 6@(-1) ]
fin_chord = 5@(-1),7@(-1)
fin = e:fin_chord,7@(-2) . Relative 1 ([ e (q+e) ]:fin_chord,5@(-2))
intro = intro . fin . Octave (-1) (e:[ 6@(-1) 4 3 ])
// note that the next phrase is the same except for the first and last notes
intro_next = EndWith ([ e e h ]:Chords fin . q:~) (StartWith (e:6@(-2)) intro)
stairway = intro . intro_next
all_the_way_to_heaven = Parallel { stairway }
Render all_the_way_to_heaven "stairway_to_heaven.wav"