###### tags: `Alucard` # An Introduction to Alucard [toc] ## Installation The easiest way to install Alucard at the moment is through ros. 1. Install [ros](https://roswell.github.io/Installation.html) 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, ```shell $ 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 ```lisp= (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. ```shell $ ./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: ```lisp= (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$. ```lisp! (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$. ```lisp (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. ```lisp (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. ```lisp (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. ```lisp (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)` ```bash= 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 ```lisp= 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. ```lisp= (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`. ```lisp= (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](https://hackmd.io/emeUBiYoSqmJ95Ls2wsrMQ) ### Debugging Let us say we try to make a type error. ```lisp= (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. ```lisp= 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 ```lisp= 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!