owned this note changed 3 years ago
Published Linked with GitHub
tags: Alucard

An Introduction to Alucard

Installation

The easiest way to install Alucard at the moment is through ros.

  1. Install ros
  2. Set sbcl as the default ros install sbcl-bin
  3. Update asdf ros install asdf
  4. run git clone https://github.com/anoma/juvix-circuits.git
  5. cd into the cloned directory cd juvix-circuits
  6. If you are using vscode, you should also run ros install "nobody-famous/alive-lsp"
  7. run make install
    • The executable should be in ~/.local/bin/
    • Alternatively make all will install it in ./build/ as well.

The last command will add it to ~/.local/bin/, so make sure that is on your path!

Now we can run alucard!

Terminal Commands

There are basically 2 ways to run alucard programs.

Interactive Mode

The interactive mode is via a REPL in Emacs/VSCode. To run the interactive mode,

$ cd ~/.local/bin
$ ./alu.image -y
;; Slynk started at port: 4005.
? 

If you are using a SLIME (VS-Code uses SLIME), replace the -y with -s.

Then use your client (e.g. Slime or Sly) to connect to localhost with port 4005 (or whatever port is shown in the terminal). You can set the port with the -p option.

If you are using vscode it might spit you out in the cl-user package. to check your prompt should say ALUSER> in vs-code or emacs. If that is the case then run the following

(in-package :aluser)

one can quit out of the repl by typing (quit).

Batch Mode

Batch mode lets you provide input from files to the alu.image executable with the -i flag and specify the output file with the -o flag.

$ ./alu.image -i "my_program.lisp" -o "output/my_program_output.txt"

If the -o flag is not given, you will go into interactive mode.

To specify the function that will act as the entry point to one's circuit, you can write

(entry-point function-name) in the file, otherwise no code will be generated.

Running Alucard from Emacs

If you are running sly, M-x sly-connect to localhost with the port number shown in your terminal earlier.

If one is using ccl instead of SBCL you might want to run:

(setf *print-pretty* t) (in-package :aluser)

Running Alucard as a library

You can also (ql:quickload :alu) if one has the asd file on the path or have loaded into one's environemnt, and have the same functionality!

Running Alucard from VSCode

Please check the readme

Writing programs and generating VAMP-IR

You know you are ready to get going when typing *package* in your REPL returns

#<PACKAGE "ALUSER">

or your REPL prompt says ALUSER>.

Let's define a simple circuit, which represents the equation, \(x+3 = 0\).

(defcircuit add-three ((public x int) (output bool))
  (= (+ x 3) 0))

In the above code, the keyword defcircuit is used to denote that we are defining a polynomial circuit, named add-three. The circuit takes as input the integer x, which is a public input, and the output is true if the equation is satisfied, and false if not.

To get the VAMP-IR code corresponding to any circuit defined below, call the function vampir on it.

For example, the VAMP-IR code for add-three is shown below.

ALUSER> (vampir add-three)
def add_three x1333 -> g1335 {
  g1334 = x1333 + 3
  g1334 = 0
  g1335 = g1334
}

Note that since the compiler pipeline is currently incomplete, it is not yet possible to run VAMP-IR and generate output.

Currently it's good to run vampir on various circuits created, as the type checker currently only runs when trying to extract.

Let's look at some more examples.

The follow circuit represents the equation \(x^3 + 3x^2 + 2x + 3y+ 4 =0\).

(defcircuit multivar ((public x int)
                      (public y int)
                      (output int))
  (= (+ (exp x 3)
        (* 3 (exp x 2))
        (* 2 x)
        (* 3 y)
        4)
     0))

We can also define a circuit to calculate the square root of an input in the following manner.

(defcircuit square-root ((private p int)
                         (output int))
  (def ((with-constraint (x)
          (= p (* x x))))
    x))

The def plays a role similar to let in Rust. The with-constraint macro can be used in any situation where we want a variable to be subject to certain constraints.

Alucard also allows us to define custom datatypes.

Let us define the type point which defines a point on the Cartesian plane.

(deftype point ()
  (x int)
  (y int))

Let's write a function to calculate the distance of this point from the origin. We'll make use of the square-root function we wrote earlier.

(defcircuit distance ((public pt point)
                      (output int))
  (square-root (+ (exp (x pt) 2) (exp (y pt) 2))))
  
(entry-point distance)  ; marking the function as the main! 

Let us now mark this function as the entry point to the file

(entry-point distance)

4 taichi@Gensokyo:~/Documents/Work/Repo/alu/alu git:main:*? % alu.image -i example.lisp -o example.vampir 4 taichi@Gensokyo:~/Documents/Work/Repo/alu/alu git:main:*? % cat example.vampir def distance pt10733_x pt10733_y -> g10770 { g10766 = pt10733_x ^ 2 g10768 = pt10733_y ^ 2 g10769 = g10766 + g10768 g10770 = square_root g10769 } def square_root p10729 -> x10772 { g10784 = x10772 * x10772 p10729 = g10784 g10785 = p10729 }%

We can also invoke the same functionality from the repl as well

ALUSER> (pipeline:dump-entry-point) def distance pt16131_x pt16131_y -> g16712 { g16708 = pt16131_x ^ 2 g16710 = pt16131_y ^ 2 g16711 = g16708 + g16710 g16712 = square_root g16711 } def square_root p15972 -> x16714 { g16726 = x16714 * x16714 p15972 = g16726 g16727 = p15972 } #<SLYNK-GRAY::SLY-OUTPUT-STREAM #x302000D6CA7D> ALUSER> (pipeline:dump-entry-point-to-file "example.vampir") #<BASIC-FILE-CHARACTER-OUTPUT-STREAM ("example.vampir"/:closed #x30200297CAED>

Some of this formula is repetitive, namely the (exp (lookup pt) 2)

we can use the common lisp integration to remove such repetitive looking code.

Namely we can use flet, which defines a local function to be our square function.

(defcircuit distance ((public pt point) (output int)) (flet ((square (cord) (exp cord 2))) (square-root (+ (square (x pt)) (square (y pt))))))

If we find this function general enough we can move it to the top level with defun.

(defun square (cord) (exp cord 2)) (defcircuit distance ((public pt point) (output int)) (square-root (+ (square (x pt)) (square (y pt)))))

A lot more can be abstracted, we have the entire toolkit of the common lisp programming language to help abstract away alucard code!

A lot more information on this can be found in the reference manual

Debugging

Let us say we try to make a type error.

(deftype point () (x int) (y int)) (defun square (cord) (check (exp cord 2) (int 32))) (defcircuit distance ((public pt point) (output int)) (square-root (+ (square (x pt)) (square (y pt)))))

In this code, we are saying the expected output of square should be of (int 32), however the type should really just be int. We can move this check to any part of the expression in the square function and the error would be the same.

ALUSER> (vampir distance) ;;;;;;;;;;;;;;;;;;;;;; In Function DISTANCE ;;;;;;;;;;;;;;;;;;;;;; 2022-08-08 08:35:27 [ERROR] <UNIFIER><TYPE>: The types #<#<REFERENCE-TYPE INT> 32> and #<REFERENCE-TYPE INT> are not equivalent [STACK] ((:IN SQUARE (EXP CORD 2) (CHECK (EXP CORD 2) (INT 32))) (SQUARE (X PT)) (+ (SQUARE (X PT)) (SQUARE (Y PT))) (SQUARE-ROOT (+ (SQUARE (X PT)) (SQUARE (Y PT)))))

Here we get the type error message saying that (int 32) and int are not compatable, and further the stack trace to the point.

It is saying that the expression (EXP CORD 2) caused the error as we stated that the type must be of (int 32) right before!

The stack trace just give the expression dump until the current point where the error occurs.

Reporting Compiler Errors

If the system errors with a very baroque error message like

4 1 taichi@Gensokyo:~/Documents/Work/Repo/alu/alu git:main:*? % alu.image -i example.lisp -o example.vampir > Debug: Undefined function ALU.PASS.DEPENDENCIES::TRACK-FUNCTION-DEPS called with arguments ((#<LET G10547 = #<#<REFERENCE *> #1=#<REFERENCE X10535> #1#>> > #<LET G10548 = #<#<REFERENCE => #<REFERENCE P10517> #<REFERENCE G10547>>>)) . > While executing: SIGNAL, in process toplevel(4). > Type :GO to continue, :POP to abort, :R for a list of available restarts. > If continued: Retry applying ALU.PASS.DEPENDENCIES::TRACK-FUNCTION-DEPS to ((#<LET G10547 = #<#<REFERENCE *> #1=#<REFERENCE X10535> #1#>> #<LET G10548 = #<#<REFERENCE => #<REFERENCE P10517> #<REFERENCE G10547>>>)). > Type :? for other options. 1 >

And spits you into a repl, please file an issue or message mariari directly. These are internal errors and they might show up!

Select a repo