Reading time: about 10 minutes (2030 words).

Contents


Setting up

Scala is a (non-pure) functional language that supports object oriented programming. It runs on the Java virtual machine (jvm), and because of this it is possible to import UACalc Java packages into Scala programs (just like we do in Jython.

  1. Install Java if you don't already have it.
    Ubuntu users can follow these instructions for installing (even multiple versions of) Java.

  2. Download and Install Scala following the instructions given on the download page.

    Ubuntu users can download the file scala-2.13.3.tgz, unpack it and put a link to the scala-2.13.3/bin directory in your PATH, OR put a symbolic link to the scala-2.13.3/bin/scala file your $HOME/bin directory (assuming the latter is in your path). For example, I did the following:

    cd ~/opt/SCALA             # (where I downloaded scala-2.13.3.tgz)
    tar xvzf scala-2.13.3.tgz
    ln $HOME/opt/SCALA/scala-2.13.3/bin/scala $HOME/bin/scala
    source ~/.bash_profile
    
  3. Download the uacalc.jar file from uacalc.org/lib/uacalc.jar


Scala REPL with UACalc objects

  1. Start the Scala REPL with UACalc dependencies.
    In a terminal window, change to the directory containing the jar files you downloaded above and do

    scala -classpath uacalc.jar
    

    You should see something like the following

    Welcome to Scala 2.13.3 (Java HotSpot(TM) 64-Bit Server VM, Java 15).
    Type in expressions for evaluation. Or try :help.
    
    scala> 
    
  2. Import UACalc classes and make some objects.

    scala> import org.uacalc.alg.conlat.BasicPartition
    
    scala> val a = new BasicPartition("|0,1|2,3|")
    a: org.uacalc.alg.conlat.BasicPartition = |0,1|2,3|
    
    scala> val b = new BasicPartition("|0|1,2|3|")
    b: org.uacalc.alg.conlat.BasicPartition = |0|1,2|3|
    
    scala> a.compose(b)
    blocks: [[0,1],[2,3]]
    val res1: org.uacalc.alg.conlat.BinaryRelation = 
      [[0, 0], [0, 1], [0, 2], [1, 0], [1, 1], [1, 2], 
      [2, 1], [2, 2], [2, 3], [3, 1], [3, 2], [3, 3]]
    

The LUAU Library

This section gives an example use of the Lazy Universe Algebra Utilities (aka Lūʻau) Scala library.

ScalaLūʻau is a Scala library providing some tools that facilitate computational algebra research and, in particular, importing and using Java packages from the Universal Algebra Calculator in Scala programs.

Constructing general algebras with Scala

We first describe how to construct an algebra that we can compute with.

The file UACalcAlgebraFactory.scala in the basic_algebra package of ScalaLūʻau makes it easy to build operation tables from Scala functions, lists, or arrays.

Some examples are given in the Scala worksheet called AlgebraFactory.sc, which is well documented with comments and self-explanatory. Nonetheless, we describe the first of those examples here.

EXAMPLE: a unary operation defined from a (Scala) function.

In this example, we define a typical function in Scala, then we use that function to construct some UACalc operations and, finally, we construct a UACalc BasicAlgebra with these operations.

The required imports for this example are

import org.uacalc.alg.BasicAlgebra
import basic_algebra.UACalcAlgebraFactory._
import scala.jdk.javaapi.CollectionConverters.asJava

First, we define a Scala function by specifying what the function does at each point.

val plusMod5: List[Int] => Int = l.sum % 5

It should be clear that this function takes a list of Ints and outputs their sum modulo 5. The type signature of this function, List[Int] => Int, is optional in this context since it can be inferred by the Scala type checker; nonetheless, we make the types explicit for clarity.

Next we use the plusMod5 function to construct a couple of UACalc operations using the method UACalcOpFromFun (defined in the basic_algebra package).

val op1: UACalcOperation = UACalcOpFromFun(plusMod5, "binaryMod5", 2, 5)
val op2: UACalcOperation = UACalcOpFromFun(plusMod5, "ternaryMod5", 3, 5)

In order to use these operations as input to UACalc's BasicAlgebra constructor, we need to put them into a Java list.

val my_ops = asJava(List(op1, op2))

Finally, we are ready to build our algebra using the BasicAlgebra constructor (imported above).

val myAlg: BasicAlgebra = new BasicAlgebra("My 1st Alg", 5, my_ops)

We can now apply any of the UACalc methods available for BasicAlgebra instances, e.g.,

myAlg.getName()
myAlg.universe()
myAlg.con().getUniverseList

These method calls produce the following output:

res10: String = My 1st Alg
res11: java.util.Set[_] = [0, 1, 2, 3, 4]
res12: java.util.List[org.uacalc.alg.conlat.Partition] = [|0|1|2|3|4|, |0,1,2,3,4|]

revealing that the algebra we constructed is called "My 1st Alg", has universe {0, 1, ..., 4}, and is simple.

Finally, we can write the algebra to a UACalc .ua (xml) file by invoking

AlgebraIO.writeAlgebraFile(myAlg, "~/UACalcAlgebraFromScalaFunctions.ua")

The resulting file could then be opened in the UACalc GUI.


Computing Congruences for Quantumn Physicists

Here are some highlights from the file TumaExample.scala, which we used to find congruences of cellular automata for quantum physics. This was for joint work with Jiri Tu̇ma.

First we created a class called Tuma and defined (in the file Tuma.scala) as follows.

package basic_algebra
import algebra_util._
class Tuma (n: Int, m: Int) {
  private val N = Math.pow(2, m).toInt
  def cardinality: Int = N

  /** Process state
    * @param inputstate
    * @param t a String representing an Int in base 2
    * @param iters an Int number of iterations
    */
  def processState(inputstate: String, t: String, iters: Int): String = {
    @scala.annotation.tailrec
    def proc_state_aux(state: String, indx: Int): String = {
      if (indx == 0) state else {
        def newstate(k: Int): Int =
          strAt(t,Integer.parseInt(strAt(state,k-1)+strAt(state, k)+strAt(state,k+1),2)).toInt
        val newstatestr: String = fun2str(newstate, 1 until (state.length-1))
        proc_state_aux(newstatestr, indx - 1)
      }
    }
    proc_state_aux(inputstate, iters)
  }

  def state_list(x: Int, y: Int, z: Int): String =
    int2bin((N * (N * x + y)) + z, 3 * m)

  val t : String = int2bin(n, 8).reverse

  def op: (Int, Int, Int) => Int = (x, y, z) =>
    Integer.parseInt(processState(state_list(x, y, z), t, m), 2)
}

Next we construct an instance of the Tuma class for parameters n = 110 and m = 7.

val n = 110; val m = 7
val tuma = new Tuma(n, m)
val N = tuma.cardinality
def t_def(args: List[Int]): Int = tuma.op(args(0), args(1), args(2))

Notice that in the last line above we converted the type of tuma.op from Int × Int × Int => Int to List[Int] => Int. This is so we can pass the resulting function, t_def, into UACalcOpFromFun and construct UACalc operation.

val t_op: UACalcOperation = UACalcOpFromFun(t_def, "t", 3, N)

// make a singleton Java list containing the operation
val t_uacalc_op = asJava(List(t_op))

// construct the basic algebra
val t_Alg: BasicAlgebra = new BasicAlgebra("Jiri's Alg" + "(" + n + "," + m + ")", N, t_uacalc_op)

Next we write the algebraic structure to a UACalc algebra file so we can inspect the algebra in the UACalc GUI if desired.

val A = AlgebraIO.writeAlgebraFile(t_Alg, algebra_dir + "/Tuma-" + n + "-" + m + ".ua")

Most of the congruences of the algebra will have the same block structure and will be "obvious" or uninteresting. These tend to have small blocks and, after some experimentation, we realized that they are generated by values in the following list.

val smblocks = List(10,18,20,21,34,36,37,40,41,42,43,74,82,84,85,106)

Finally, for each a and b not in the smblocks list, we compute and print out the congruence generated by (a, b) as follows.

for (a <- (0 until N-1).filterNot(smblocks.contains))
  for (b <- (a+1 until N).filterNot(smblocks.contains)) {

    val cab = t_Alg.con().Cg(a,b, null)

    if (cab.numberOfBlocks() > 17)
      println("Cg(" + a + "," + b + ") = " + cab + "  NUM BLOCKS = " + cab.numberOfBlocks())

(The if conditional limits the printed output to only interesting congruences.)


More Learning Resources

The Best Scala Books

(Please support this site by using the links above to order the books.)


The Best Scala Courses

Martin Odersky, creator of Scala, (along with some colleagues) offers an excellent sequence of online courses on the Coursera platform. They are listed below.

  1. Functional Programming Principles in Scala
  2. Functional Program Design in Scala
  3. Parallel Programming in Scala
  4. Big Data Analysis with Scala and Spark

Contributions Welcome

This page was created by volunteers. Help grow it into a more useful resource. If you have any comments, suggestions, or other contributions, please create a new gitlab issue or email williamdemeo at gmail.


-->