Sunday, January 30, 2022

Displaying Hiccup generated HTML with Swing

HTML is a very common data format, so we can make use of it with both ClojureScript as well as Clojure. In the later case, we can use HTML generated by Clojure in Java Swing applications. A number of Java Swing classes optionally use HTML in order to enrich the content of their interfaces. We can provide that HTML using Hiccup like so.
(ns swing-hiccup.basic
  (:require [hiccup.core :refer :all]
            [garden.core :refer [css]))
  (:import (javax.swing JFrame JLabel)))

(def introduction-to-category-theory
  [:html
   [:h1 "Introduction to category theory"]
   "The elements of a category are"
   [:ol
    [:li "Objects"]
    [:li "Morphisms"]]
   "associated to these elements of a category are functions"
   [:ol
    [:li "A source function f : Morphisms -> Objects"]
    [:li "A target function f : Morphisms -> Objects"]
    [:li "A composition function f : Morphisms2* -> Morphisms"]
    [:li "An identity function f : Objects -> Morphisms"]]
   "that satisfy axioms of associativity and identity."])

(defn view-html
  [arg]

  (let [^JFrame f (JFrame.)
        ^JLabel p (JLabel.
                    ^String
                    (html arg))]
    (.add f p)
    (.setTitle f "Abstract Algebra")
    (.setVisible f true)
    (.setSize f 450 300)))
This produces a Java Swing JFrame as depicted below, and so with Hicccup you can freely integrate HTML generated by Clojure into your Java swing applications. The Java standard library is much more sophisticated then just this, however. Java also comes with standard support for stylesheets provided by the java.swing.text.html package in the java.desktop module. In order to make things a little more interesting, here we not only use HTML and CSS in a Java Swing application, we programatically generate the HTML using Clojure.

This demonstrates how easy it is to generate HTML using Clojure because thanks to Hiccup it allows you to represent HTM as the same sort of S-expression the rest of the language deals with. It is for this resaon that I have always felt that Clojure is the best language for generating HTML (such as in Java Swing applications that make use of HTML or on the web).

In order to also programatically generate CSS as well we can simply make use of the Garden library. Garden is for CSS what Hiccup is for HTML, as it allows us to generated CSS from Clojure's EDN expressions. With this HTML, CSS, etc can all be represented as no different then any other data structure in Clojure. This gets to one of the advantages of the language which is that it is a common format for all kinds of data, as well as a common language for both the server side and the client side with Clojure Common.

In order to then display the generated HTML and CSS we just have to create a JEditorPane containing a HTMLEditorKit and then put it in a standard Java Swing JFrame. The CSS is added automatically by add rule on the Swing StyleSheet, and the HTML is provided as the text of the JEditorPane. Type hinting is used throughout to improve performance.
(ns swing-hiccup.styled
  (:require [hiccup.core :refer :all]
            [garden.core :refer [css]]
            [swing-hiccup.basic :refer :all])
  (:import (javax.swing JFrame JEditorPane)
           (javax.swing.text.html HTMLEditorKit StyleSheet)))

(defn join-table
  [n]

  (letfn [(cell-value [x y]
            (bit-or x y))]
    (map
      (fn [i]
        (map
          (fn [j]
            (cell-value i j))
          (range n)))
      (range n))))

(defn wrap-table
  [coll]

  (vec
    (conj
      (map
        (fn [row]
          (vec
            (conj
              (map
                (fn [i]
                  [:td (.toString i)])
                row)
              :tr)))
        coll)
      :table)))

(def css-data
  [[:body {:color "white"}]
   [:td {:border "1px solid black"
         :color "black"
         :font-size "16px"}]])

(def html-data
  [:html
   [:body
    (wrap-table (join-table 8))]])

(defn view-styled-html
  [html-data css-data]

  (let [^JEditorPane pane (JEditorPane.)
        ^HTMLEditorKit kit (HTMLEditorKit.)
        ^StyleSheet style-sheet (.getStyleSheet kit)
        ^JFrame frame (JFrame. )]
    (.setEditable pane false)
    (.setEditorKit pane kit)
    (.addRule style-sheet (css css-data))
    (.setDocument pane (.createDefaultDocument kit))
    (.setText pane (html html-data))
    (.add frame pane)
    (.setSize frame 350 350)
    (.setVisible frame true)
    (.setTitle frame "Multiplication Table")))
This Clojure based Java Swing application then produces an output JFrame that looks as displayed in the image below. The table displayed is a join semilattice of a boolean algebra, which can simply be described by bitwise operations. Hopefully this demonstrates the utility of HTML and CSS both in Clojure and in ClojureScript because even when are building our graphical interfaces with Java and Swing there are a number of cases in which we can make use of HTML and CSS. Its typically easier to use Clojure to generate HTML then Java, but it is easy enough to switch between the two of them.

Saturday, January 29, 2022

Drawing Young diagrams with HTML5 Canvas

The previous Java Swing program to visualize members of Young's lattice $\mathbb{Y}$ can fairly easily be ported to ClojureScript to make an HTML based application.
; this is a clojure common method
(defn determine-coordinates
  "Get the coordinates of a young diagram."
  [coll]

  (let [sorted-seq (sort > (seq coll))]
    (apply
     concat
     (map-indexed
      (fn [x v]
        (map
         (fn [y]
           [x y])
         (range 0 v)))
      sorted-seq))))
      
; this is specific to clojurescript
(defn display-young-diagram
  "Display a young diagram onto an html canvas"
  [ctx data start end block-width block-height]
  
  (let [coords (determine-coordinates data)] 
    (set! (.-strokeStyle ctx) "black")
    (doseq [[x y] coords] 
      (.strokeRect 
        ctx 
        (+ start (* x block-width)) 
        (+ end (* y block-height)) 
        block-width 
        block-height))))

; clojurescript is the best way to develop web applications
(defn init []
  (let [ctx (.getContext (.getElementById js/document "canvas") "2d")]
    (display-young-diagram ctx data '(1 2 3 4) 50 30 30)))
The real hero of this port is Clojure Common, which allows us to share code between the backend and the frontend, so that we can more easily port JVM based applications to the web. The determine coordinates function as you can see can be shifted over to ClojureScript without a single change to our code base.

Its not a perfect solution, as there are obviously things you can do on the JVM (like make use of Java libraries such as apache commons and the standard library) that cannot readily be ported to the browser setting but it is better then nothing which is quite often is what we were left with doing web development in the old days.

So write as much code as possible in Clojure Commons so that porting JVM based applications to the web will be easier. Generally, these will be pure functions that express business logic, and since they don't need side effects they won't have to depend so much on the platform they are running on.

Sunday, January 23, 2022

Mutable multisets

The implementation of mutable multisets in Clojure deosn't contradict the currently existing multiset implementations in the Java platform, in particular the one provided by apache commons, because Java collections tend to be mutable. So in general we have different JVM datatypes for mutable and immutable collections. To use the mutable one just call up the apache commons collection library and import the multiset data type.
import java.util.ArrayList;
import java.util.Arrays;
import org.apache.commons.collections4.multiset.HashMultiSet;
In order to create a multiset, it is good to first have a java.util.Collection created first. So to create one I simply using I simply use the asList method of the java.util.Arrays class.
// create a mutable multiset
var ms = new HashMultiSet < Integer > (Arrays.asList(1,2,2,2,3));

// print its curent contents
System.out.println(ms);

// add elements to the multiset to change it
ms.add(4);
ms.add(5);

// now the multiset is actually different
System.out.println(ms);

// remove the elements we added to change it back
ms.remove(4);
ms.remove(5);

// the multiset is back to its original state
System.out.println(ms);
There are slight differences in the API from the more familiar multiset implementation, like we use uniqueSet for what is commonly called the support in multiset theory and we use getCount to get the multiplicity of them element. But aside from a few terminology differences the apache commons provides everything you need to deal with multisets. It also provides a ton of other nice things like the best math library programmed in Java.

See also:
Immutable mlutisets in Clojure

Friday, January 14, 2022

Sieve of Eratosthenes in Jasmin

I have hand written a number of programs in pure Java bytecode. So for example, the sieve which I implemented before in Java is something I have written out in bytecode before by hand. In particular, what you will notice about this code is the high amount of documentation and structure for JVM bytecode.
; Compute the sieve of an integer
; @param(0) the integer argument
; @local(1) the boolean array
; @local(2) the current index
; @local(3) the limit
; @local(4) the other index
.method public static sieve(I)[Z
    .limit stack 8
    .limit locals 8

    iload_0
    iconst_1
    iadd
    newarray boolean    
    astore_1

    iload_0
    i2d
    invokestatic java/lang/Math.sqrt(D)D
    d2i
    iconst_1
    iadd
    istore_3
        
    iconst_2    
    istore 4

    aload_1
    iconst_0    
    iconst_1    
    bastore

    aload_1
    iconst_1
    iconst_1
    bastore
    
    aload_1
    iconst_2
    iconst_0
    bastore

    bipush 4
    istore_2
initialization_loop:
    iload_2 
    iload_0
    if_icmpgt initialization_loop_breakpoint

    aload_1
    iload_2
    iconst_0
    bastore

    iinc 2 1
    goto initialization_loop
initialization_loop_breakpoint:
    iconst_2
    istore_2

loop:
    iload_2
    iload_3
    if_icmpge loop_breakpoint
    
    aload_1
    iload_2
    baload
    ifne inner_loop_breakpoint

    iload_2
    iload_2
    imul
    istore 4

inner_loop:
    iload 4
    iload_0
    if_icmpgt inner_loop_breakpoint

    aload_1
    iload 4
    iconst_1
    bastore    

    iload 4
    iload_2
    iadd
    istore 4
    goto inner_loop
inner_loop_breakpoint:

    iinc 2 1
    goto loop
loop_breakpoint:
    
    aload_1
    areturn
.end method

; @locals
; @param(0) the array argument
; @local(1) the count
; @local(2) the current index
; @local(3) the array argument length
.method public static falseCount([Z)I
    .limit stack 4
    .limit locals 4  

    iconst_0
    istore_1
    
    iconst_0
    istore_2
    
    aload_0
    arraylength
    istore_3
loop:
    iload_2
    iload_3
    if_icmpge loop_breakpoint

    aload_0
    iload_2
    baload
    ifeq is_false
    goto is_false_breakpoint

is_false:
    iinc 1 1
is_false_breakpoint:

    iinc 2 1
    goto loop
loop_breakpoint:
    iload_1
    ireturn
.end method

; Get the true indices of a boolean array
; @param(0) the argument array
; @local(1) the return array
; @local(2) the current index
; @local(3) the argument array length
; @local(4) the return array index
.method public static falseIndices([Z)[I
    .limit stack 4
    .limit locals 5

    aload_0
    invokestatic Sieve/falseCount([Z)I
    newarray int
    astore_1
    
    iconst_0
    istore_2
    
    aload_0
    arraylength
    istore_3
    
    iconst_0
    istore 4
loop:
    iload_2
    iload_3
    if_icmpge loop_breakpoint

    aload_0
    iload_2
    baload
    ifeq is_false
    goto is_false_breakpoint

is_false:
    aload_1
    iload 4
    iload_2
    iastore
    iinc 4 1
is_false_breakpoint:    

    iinc 2 1
    goto loop
loop_breakpoint:
    aload_1
    areturn
.end method

Adjacency matrix swing view

A simple Java application that displays adjacency matrices using Swing will be presented.
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class AdjacencyMatrixView {
    JButton[][] buttons;
    JPanel panel;

    // a constructor method for adjacency matrix views
    public AdjacencyMatrixView(JButton[][] buttons, JPanel panel) {
        this.buttons = buttons;
        this.panel = panel;
    }

    public static Color getButtonBackgroundColor(boolean val) {
        return val ? Color.PINK : Color.WHITE;
    }

    public void inPlaceUpdate(AdjacencyMatrix mat) {
        var l = mat.order();
        for(int x = 0; x < l; x++) {
            for(int y = 0; y < l; y++) {
                var currentButton = buttons[x][y];
                currentButton.setBackground(getButtonBackgroundColor(mat.containsEdge(x,y)));
            }
        }
    }

    public void update(AdjacencyMatrix mat, JFrame f) {
        var view = this;
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                f.getContentPane().remove(view.panel);
                f.invalidate();
                f.validate();
                f.repaint();

                var newView = createView(mat, f);
                view.buttons = newView.buttons;
                view.panel = newView.panel;
                f.add(newView.panel);
                f.revalidate();
                f.repaint();
            }
        });
    }

    // a static factory method
    public static AdjacencyMatrixView createView(AdjacencyMatrix mat, JFrame f) {
        var l = mat.order();
        var currentButtons = new JButton[l][l];
        var p = new JPanel();

        p.setLayout(new GridLayout(l,l));
        for(int x = 0; x < l; x++) {
            for(int y = 0; y < l; y++) {
                var currentButton = new JButton(" ");
                currentButton.putClientProperty("id", new int[]{x,y});
                currentButtons[x][y] = currentButton;
                currentButton.setBackground(getButtonBackgroundColor(mat.containsEdge(x,y)));
                currentButton.addActionListener(new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent actionEvent) {
                        int[] coord = (int[]) currentButton.getClientProperty("id");
                        mat.swapEdge(coord[0],coord[1]);
                        currentButton.setBackground(getButtonBackgroundColor(mat.containsEdge(coord[0],coord[1])));
                    }
                });
                p.add(currentButton);
            }
        }

        return new AdjacencyMatrixView(currentButtons, p);
    }

}
Lets create a main method that runs the GUI that we have built. This should create a JMenuBar that contains all the functionality related to mutable adjacency matrices described before.
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.nio.file.Files;
import java.util.Arrays;
import java.util.regex.Pattern;

public class Application {

    public static void main(String[] args) {
        var mat = new java.util.concurrent.atomic.AtomicReference(new AdjacencyMatrix(5));

        var f = new JFrame();
        var view = AdjacencyMatrixView.createView(mat.get(), f);
        f.add(view.panel);

        var menu = new JMenuBar();
        var fileMenu = new JMenu("File");
        menu.add(fileMenu);

        var newMenu = new JMenuItem("New");
        newMenu.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent actionEvent) {
//                view.update(new AdjacencyMatrix(10), f);
                var str = JOptionPane.showInputDialog("Enter a number:");
                if(str != null) {
                    var p = Pattern.compile("[0-9]+");
                    var m = p.matcher(str);
                    if (m.matches()) {
                        var newMatrix = new AdjacencyMatrix(Integer.parseInt(str));
                        view.update(newMatrix, f);
                        mat.set(newMatrix);
                    }
                }
            }
        });
        fileMenu.add(newMenu);

        var openMenu = new JMenuItem("Open");
        openMenu.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent actionEvent) {
                var jfc = new JFileChooser();
                jfc.showSaveDialog(f);
                var currentFile = jfc.getSelectedFile();
                if(currentFile != null) {
                    try {
                        var currentBytes = Files.readAllBytes(currentFile.toPath());
                        var newMatrix = AdjacencyMatrix.fromBytes(currentBytes);
                        view.update(newMatrix, f);
                        mat.set(newMatrix);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
        fileMenu.add(openMenu);

        var saveMenu = new JMenuItem("Save");
        saveMenu.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent actionEvent) {
                var jfc = new JFileChooser();
                jfc.showSaveDialog(f);
                var currentFile = jfc.getSelectedFile();
                if(currentFile != null) {
                    var currentBytes = mat.get().getBytes();

                    try {
                        Files.write(currentFile.toPath(), currentBytes);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
        fileMenu.add(saveMenu);

        var editMenu = new JMenu("Edit");
        var complementMenu = new JMenuItem("Complement");
        complementMenu.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent actionEvent) {
                mat.get().complement();
                view.inPlaceUpdate(mat.get());
            }

        });
        editMenu.add(complementMenu);

        var transposeMenu = new JMenuItem("Transpose");
        transposeMenu.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent actionEvent) {
                mat.get().transpose();
                view.inPlaceUpdate(mat.get());
            }
        });
        editMenu.add(transposeMenu);

        var symmetricClosureMenu = new JMenuItem("Symmetric closure");
        symmetricClosureMenu.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent actionEvent) {
                mat.get().symmetricClosure();
                view.inPlaceUpdate(mat.get());
            }
        });
        editMenu.add(symmetricClosureMenu);

        var symmetricComponentMenu = new JMenuItem("Symmetric component");
        symmetricComponentMenu.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent actionEvent) {
                mat.get().symmetricComponent();
                view.inPlaceUpdate(mat.get());
            }
        });
        editMenu.add(symmetricComponentMenu);

        var scramble = new JMenuItem("Randomize");
        scramble.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent actionEvent) {
                mat.get().randomize();
                view.inPlaceUpdate(mat.get());
            }
        });
        editMenu.add(scramble);

        menu.add(editMenu);

        f.setJMenuBar(menu);
        f.setSize(500,500);
        f.setVisible(true);
    }

}
That is it. Now we have a rudimentary GUI for dealing with adjacency matrices, as grids of booleans. Clicking on any point in the grid will switch its value from true to false and back again. I didn't think I would post this part of the simple adjacency matrix program, but it doesn't hurt to add a little bit more to what I was doing.

Sunday, January 9, 2022

Adjacency matrices

In order to better demonstrate Clojure-Java interop I will create an adjacency matrix class for computations in graph theory, and then I will call its methods from Clojure. Java and Clojure can call each other because in the end their method calls both compile to the same JVM opcodes: invokevirtual, invokestatic, etc.

Graph data structures:

Basically, graph data structures typically come in three different forms, and the appropriate one can be chosen based upon performance requirements. Adjacency matrices are more efficient for dense graphs, and adjacency lists are preferably for sparse ones.
  • Adjacency matrices
  • Incidence matrices
  • Adjacency lists
Adjacency lists are the closest to the representation of graphs in classical set theory. On the other hand, in categorical set theory and topos theory, a graph might be represented as a set-valued functor on the double arrow category. I will demonstrate an adjacency matrix data structure.

An adjacency matrix class:

This creates a mutable adjacency matrix class in Java that uses a multidimensional boolean array internally. The boolean array is then manipulated by for loops, which compile to JVM bytecode in the obvious manner.
import java.util.Arrays;
import java.util.HashSet;
import java.util.Random;
import java.util.Set;

public class AdjacencyMatrix {
    boolean[][] edges;

    public AdjacencyMatrix(boolean[][] edges) {
        this.edges = edges;
    }

    public AdjacencyMatrix(int n) {
        this.edges = new boolean[n][n];
    }

    public void setEdge(int x, int y, boolean val) {
        edges[x][y] = val;
    }

    public void swapEdge(int x, int y) {
        edges[x][y] = !edges[x][y];
    }

    public void clear() {
        for(int x = 0; x < edges.length; x++) {
            for(int y = 0; y < edges.length; y++) {
                setEdge(x,y,false);
            }
        }
    }

    public void fill() {
        for(int x = 0; x < edges.length; x++) {
            for(int y = 0; y < edges.length; y++) {
                setEdge(x,y,true);
            }
        }
    }

    public void complement() {
        for(int x = 0; x < edges.length; x++) {
            for(int y = 0; y < edges.length; y++) {
                swapEdge(x, y);
            }
        }
    }

    public void symmetricClosure() {
        for(int x = 0; x < edges.length; x++) {
            for(int y = 0; y < edges.length; y++) {
                setEdge(x, y, edges[x][y] || edges[y][x]);
            }
        }
    }

    public void symmetricComponent() {
        for(int x = 0; x < edges.length; x++) {
            for(int y = 0; y < edges.length; y++) {
                setEdge(x, y, edges[x][y] && edges[y][x]);
            }
        }
    }

    public void randomize() {
        var r = new Random();
        for(int x = 0; x < edges.length; x++) {
            for(int y = 0; y < edges.length; y++) {
                setEdge(x, y, (r.nextInt(2) == 1));
            }
        }
    }

    public void transpose() {
        // we need to duplicate the old matrix so that we
        // don't get stopped out by side effects.
        var oldMatrix = new boolean[edges.length][edges.length];
        for(int i = 0; i < edges.length; i++) {
            oldMatrix[i] = edges[i].clone();
        }

        for(int x = 0; x < edges.length; x++) {
            for(int y = 0; y < edges.length; y++) {
                edges[x][y] = oldMatrix[y][x];
            }
        }
    }

    public int order() {
        return edges.length;
    }

    public boolean containsEdge(int x, int y) {
        return edges[x][y];
    }

    public int size() {
        int rval = 0;
        for(int x = 0; x < edges.length; x++) {
            for(int y = 0; y < edges.length; y++) {
                if(edges[x][y]) {
                    rval++;
                }
            }
        }
        return rval;
    }

    public String toString() {
        return Arrays.deepToString(edges);
    }

    public byte[] getBytes() {
        var l = edges.length;
        var rval = new byte[(int) Math.pow(l,2)];

        for(int x = 0; x < edges.length; x++) {
            for(int y = 0; y < edges.length; y++) {
                rval[x*l + y] = (byte) ((this.edges[x][y]) ? 1 : 0);
            }
        }

        return rval;
    }

    public static AdjacencyMatrix fromBytes(byte[] coll) {
        var l = (int) Math.sqrt(coll.length);
        var edges = new boolean[l][l];

        for(int x = 0; x < l; x++) {
            for(int y = 0; y < l; y++) {
                edges[x][y] = (coll[x*l + y] == 1);
            }
        }

        return new AdjacencyMatrix(edges);
    }

    public Set edgeSet() {
        Set rval = new HashSet();

        for(int x = 0; x < edges.length; x++) {
            for(int y = 0; y < edges.length; y++) {
                if(edges[x][y]) {
                    rval.add(new int[]{x,y});
                }
            }
        }

        return rval;
    }

}
This is notable because it is a mutable class which means it would be hard to recreate exactly in Clojure, which tries to use immutable and persistent data structures for everything. It might pay off to have both an adjacency matrix value and a separate mutable matrix class, but we will leave that aside for now. I will now demonstrate a little bit about how to use this class from Clojure.

Managing adjacency matrices from Clojure

Creating an adjacency matrix
The key to create an adjacency matrix now is to use the make-array function to create a boolean array in order to call the constructor. A new AdjacencyMatrix is created using the new opcode, and then initialized by a call to invokespecial with the appropriate constructor method handle.
(def arr (make-array Boolean/TYPE 4 4))
We can then manipulate the boolean array using the Clojure Java interop methods aget and aset which correspond to the aload and astore instructions of the Java virtual machine.
(aset (aget arr 0) 0 true) 
(aset (aget arr 2) 2 true)
Now that we have sufficiently manipulated the data of the adjacency matrix we can pass it to the constructor, in order to initialize an instance of the AdjacencyMatrix class.
(def ^AdjacencyMatrix adjacency-matrix
    (AdjacencyMatrix. arr))
The Adjacency matrix constructor is actually overloaded, so assuming that you don't want to do any operations on the input multidimensional boolean array before passing it to the constructor, you can just call the version that uses an integer as its main parameter.
(def ^AdjacencyMatrix adjacency-matrix
    (AdjacencyMatrix. 4))
Type hinting the adjacency matrix after you created it ensures that the Clojure code will typically compile to an invokevirtual call with the appropriate method signature, rather than going through reflection.

Manipulating the adjacency matrix
I have now prepared a number of Java methods that can be called to manipulate an adjacency matrix instance from Clojure. For example, clear sets all entries to false.
(.clear adjacency-matrix)
On the other hand, fill does the opposite and it sets all the entries in the adjacency matrix to true. In each case, we can call a Java method using the same sort of dot method call syntax as Java.
(.fill adjacency-matrix)
The fill operation is equivalent to using clear combined with complement which switches each boolean entry in the adjacency matrix to its opposite value.
(doto adjacency-matrix
    (.clear)
    (.complement))
A final method I created that you can call is transpose. It was actually the hardest to implement because of the nature of its side effects.
(.transpose adjacency-matrix)
Of course, many more methods could be added and are not included here but this demonstates the basic principles of Clojure Java interop.

Storing a matrix to a file:
One interesting thing I added in the Java class is the ability to take the adjacency matrix and convert it to a byte array. The whole point of this is so that I can use it to store an adjacency matrix to a file. Its not an elaborate serialisation method or anything, but it works.
(import java.io.File)
(import java.nio.Files)

(def current-file 
    (File. (str (System/getProperty "user.home") "/output.bin")))

(Files/write (.toPath current-file) (.getBytes adjacency-matrix))
In order to then get back the data of the adjacency matrix later, all we need to do is call java.nio.Files/readAllBytes and then use the fromBytes method provided in the Java class provided earlier.
(def adjacency-matrix
    (AdjacencyMatrix/fromBytes (Files/readAllBytes current-file)))
That should be sufficient for now to demonstrate this basic adjacency matrix. I envision creating thousands of Java classes for various objects of mathematics - like Young diagrams, transformationts, permutations, etc for use in Clojure.

Friday, January 7, 2022

Clojure java interop

The Java virtual machine is a general building material for programs, not necessarily tied to any language or programming paradigm. All that matters is that you can produce JVM bytecode. Since it is all the same in the end, it is worth asking what advantages the Java language has for generating JVM bytecode.
  • Support for variable names and distinguished argument lists.
  • Type deduction in opcode generation. In particular, you don't have to distinguish between iadd, ladd, fadd, and dadd when adding two numbers. Likewise, for the other arithmetic operations, loading and storing values in arrays, returning values, casting, etc. By the same token, you don't have to fully specify method signatures.
  • The import statement saves you from always having to fully specify java class names. There is no corresponding import opcode for the Java virtual machine, so this is purely a Java language feature.
  • The Java language doesn't force you to have to distinguish between different opcodes when loading constants. The Java virtual machine supports the different opcodes to make more compact bytecode. Any high level language (including the ASM bytecode library) should handle this automatically.
  • Java provides control flow constructs in the place of goto and conditional jumps. Part of this is the uniform condition system, which prevents you from having to determine which conditional jump opcode to use.
  • A uniform call syntax so you don't have to distinguish between invokestatic, invokevirtual, invokeinterface, and invokespecial.
  • Built in support for l-values and generalized place forms, so you can use a general assignment form on local variables, array indices, and static and instance fields. You can even set values in multidimensional arrays and the Java compiler will produce the correct combination of opcodes for you for that.
  • Last but not least, the Java language automatically handles arguments on the stack for you. This can be useful for example when dealing with mathematical expressions.
The Java language does a lot when you put it like that, but this by no means that Java virtual machine bytecode is hard to use. The Java virtual machine is still a high level architecture which is easier to use than any C-machine. Hopefully this explains why you might use the Java language to generate JVM bytecode, but it is all the same if you want to use something else or even write you or own compiler as I have.

Setting up a mixed project:
So in order to set up a mixed Java and Clojure project I suggest using Intellij IDEA. Intellij is the only Java focused IDE that also has good support for Clojure. Then you just need to create separate Java and Clojure folders and configure Leiningen to specify your Clojure folder in :source-paths and your Java folder in :java-source-paths.

Creating a Java class:
In order to create a first example of Java and Clojure interop, I have chosen the special case of defining a prime number sieve. Obviously, you could easily do this in Clojure, but perhaps some mathematical functionality should be written in Java so that they are more performant.
import java.util.BitSet;

public class NumberUtilities {

    public static int[] sieve(int n) {

        BitSet primes = new BitSet(n+1);
        primes.flip(2, n+1);

        for(int p = 2; p*p <= n; p++) {
            if(primes.get(p)) {
                for(int i = p*p; i <= n; i += p) {
                    primes.set(i, false);
                }
            }
        }

        return primes.stream().toArray();
    }

}
I mentioned that the Java language is just a tool for generating Java virtual machine bytecodes. Its kind of like an M-expression syntax for the JVM, and Lisp Flavoured Java is an S-expression syntax. Clojure is a different beast entirely from either of them. For the purposes of this demonstration, lets examine the output bytecode as it appears with Jasmin.
.class public NumberUtilities

.method public static sieve(I)[I
    .limit stack 4
    .limit locals 4

; initialize the bit set
    new java/util/BitSet
    dup
    iload_0
    iconst_1
    iadd
    invokespecial java/util/BitSet.(I)V

; flip the possible primes to true
    astore_1
    aload_1
    iconst_2
    iload_0
    iconst_1
    iadd
    invokevirtual java/util/BitSet.flip(II)V

; initial the first prime to two
    iconst_2
    istore_2

; start a loop in order to do the sieve on the main bit set
loop:
    iload_2
    iload_2
    imul
    iload_0
    if_icmpgt loop_breakpoint

; ensure that this number is a prime before starting the inner loop
    aload_1
    iload_2
    invokevirtual java/util/Bitset.get(I)Z
    ifeq inner_loop_breakpoint

; initialize the current multiple to the first non flipped index
    iload_2
    iload_2
    imul
    istore_3

; flip all multiples of the current prime to false
inner_loop:
    iload_3
    iload_0
    if_icmpgt inner_loop_breakpoint

; set the current index to false
    aload_0
    iload_3
    iconst_0
    invokevirtual java/util/BitSet.set(IZ)V

    iload_3
    iload_2
    iadd
    istore_3
    goto inner_loop

inner_loop_breakpoint:

    iinc 2,1
    goto loop
loop_breakpoint:

    ; convert the bitset into an int array containing all true indices and return
    aload_1
    invokevirtual java/util/BitSet.stream()Ljava/util/stream/IntStream;
    invokeinterface java/util/stream/IntStream.toArray()[I
    areturn
.end method
The advantages of the Java language can clearly be seen by comparing the Java language code to the Java virtual machine bytecode. Whenever someone says that Java is verbose, I just remember how much typing it saves from having to write JVM bytecode in hand, which I have a done a lot. Too much.

A notable aspect of this is how the compiler structures the output of for loops. There is quite a lot to unpack when using a for loop, and its logic appears all over the place in the compiled output. That is why some parts of the for loop appear before the loop starts, at the start of the loop, and at the end. Once you unpack all of that it is fairly easy to see how Java code corresponds to bytecode. In that sense, Java is one of the easier languages to understand in terms of its compiler output.

Calling Java functions from Clojure
All the countless hours spent reading the documentation of the Java virtual machine, the Java language, and the thousands of classes in the Java standard library are finally rewarded by using Clojure, which has seamless Java interop.
(prn (seq (NumberUtilities/sieve 1000)))
The execution of the sieve function written in Java produces the first prime numbers up to a thousand, which confirms our memory of the smallest primes.
(2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 
71 73 79 83 89 97 101 103 107 109 113 127 131 137 139 
149 151 157 163 167 173 179 181 191 193 197 199 211 223 
227 229 233 239 241 251 257 263 269 271 277 281 283 293 
307 311 313 317 331 337 347 349 353 359 367 373 379 383 
389 397 401 409 419 421 431 433 439 443 449 457 461 463 
467 479 487 491 499 503 509 521 523 541 547 557 563 569 
571 577 587 593 599 601 607 613 617 619 631 641 643 647 
653 659 661 673 677 683 691 701 709 719 727 733 739 743 
751 757 761 769 773 787 797 809 811 821 823 827 829 839 
853 857 859 863 877 881 883 887 907 911 919 929 937 941 
947 953 967 971 977 983 991 997)
Clojure is able to call the Java sieve function because they both speak the same basic language: Java virtual machine bytecode. We saw the JVM output of the Java class. The corresponding Clojure class produces the same sort of opcodes, starting with a call with invokestatic to call the sieve function. The only difference is that Clojure might have to use reflection if not enough type information is provided to it.

In this case that isn't necessary because NumberUtilities doesn't use method overloading, but in general the most important performance benefit you can add to your Clojure code is to use type hints so you don't have to use reflection to determine method signatures at runtime. Finally, after calling sieve we use seq in order to print it, because sequences produce better output when converted to Strings. A more Java like solution would be to use java.util.Arrays/toString.

The Java language and Clojure perfectly complement each other, because Clojure isn't just another copy of Java. Java is static, imperative, heteroiconic, etc while Clojure is dynamic, functional, and homoiconic. The fact that two languages that are so different from one another can come together is the ultimate testament to the power of the Java virtual machine.

Saturday, January 1, 2022

Decade in review and changes

I can now reflect on the past decade in order to determine what has been done right or wrong, and whats next. In particular, I will explain my programming philosophy and how it has developed over the years. Finally, I will explain the new blog description.

Fully embracing Clojure
I've had several years to really pick up on Common Lisp and Scheme, and I never have. Two important points in Clojure's favor are its richer and more modern syntax and the fact that it is hosted on a virtual machine. Most importantly, it is what I have invested most of my time in. By focusing on Clojure alone, I will have more time to embrace more of the Clojure and ClojureScript ecosystem.

Switching to Intellij:
I am not going to be touching any Lisp dialects other then Clojure, and that includes Emacs Lisp. I am proud to announce that I have switched to Intellij IDEA for everything including Clojure development. I think it was wrong of me to use Emacs so much, and Intellij IDEA is a godsend. It will free me up to mix Java, Clojure, and other JVM languages together in the same project as I see fit.

Virtual machines are more important then languages:
There is an old quote from Alan Kay which is a favourite of mine, which is that Lisp isn't a language it is a building material. Lisp doesn't constrain you to any type of programming, instead it opens up the language for you to develop as you see fit. Macros liberate you from the constraints of the compiler.

"Lisp isn't a language, it's a building material."

There is something equally and perhaps even more true which is that the Java virtual machine (JVM) is also a building material. What opened up my eyes was doing a compiler project for a JVM language. The JVM is a building tool by which can you can build anything you want by generating JVM bytecode.

"The Java virtual machine isn't a language, it's a building material."

The same is true of the Common Language Runtime (CLR), which shares most of the advantages of the JVM. There are a few differences, like the CLR has overloaded arithmetic, reified generics, value types (to be added in the JVM with Valhala), etc. But on a fundamental architectural level, they both share all the most important advantages of each other and they are excellent pieces of technology.

Programming today should surely be done on one of these virtual machines. That is the most important thing, as they can be used as the building material of today for programmers and language developers. I am more comfortable and at home at this point using Java and making use of the JVM then I am with using SBCL and no virtual machine even though the later is a Lisp dialect. In this sense, virtual machines are more important then languages.

This raises the issue: why not use both Lisp and the Java virtual machine at the same time? They are both building materials that you can meld into any form you like: so why not use them together. And of course, that is what we do when programming Clojure, but by far the most important thing is that we have a virtual machine underneath to fall back on.

The Java virtual machine gives us garbage collection, a higher level architecture, a highly optimized just in time compiler, a whole wealth of libraries, a compiler target, and a programming ecosystem with support for multiple languages. In this sense, it is the most important tool in the programmer's arsenal and not the individual language, which is just a nice way of expressing its features.

Knowledge engineering
The title of this blog is Lisp AI, which pays homage to the history of Lisp in artificial intelligence. The phrase artificial intelligence, first coined by John McCarthy, has now evolved to take on many meanings from many different subjects. I am primarily concerned with knowledge engineering and creating ontologies, and so that has been added to the description.