blob: f3fdff5ca43b17230882a652c97f843034368f61 [file] [log] [blame]
<!DOCTYPE html>
<!--[if lt IE 7]> <html class="no-js lt-ie9 lt-ie8 lt-ie7"> <![endif]-->
<!--[if IE 7]> <html class="no-js lt-ie9 lt-ie8"> <![endif]-->
<!--[if IE 8]> <html class="no-js lt-ie9"> <![endif]-->
<!--[if gt IE 8]><!--> <html class="no-js"> <!--<![endif]--><head>
<meta charset='utf-8'/><meta http-equiv='X-UA-Compatible' content='IE=edge'/><meta name='viewport' content='width=device-width, initial-scale=1'/><meta name='keywords' content='data science, groovy, matrices, ojalgo, commons math, nd4j, ejml, vector api, eclipse deeplearning4j'/><meta name='description' content='This post looks at using Groovy to write a number of applications involving matrices. It uses a number of open source matrix libraries.'/><title>The Apache Groovy programming language - Blogs - Matrix calculations with Groovy, Apache Commons Math, ojAlgo, Nd4j and EJML</title><link href='../img/favicon.ico' type='image/x-ico' rel='icon'/><link rel='stylesheet' type='text/css' href='../css/bootstrap.css'/><link rel='stylesheet' type='text/css' href='../css/font-awesome.min.css'/><link rel='stylesheet' type='text/css' href='../css/style.css'/><link rel='stylesheet' type='text/css' href='https://cdnjs.cloudflare.com/ajax/libs/prettify/r298/prettify.min.css'/>
</head><body>
<div id='fork-me'>
<a href='https://github.com/apache/groovy'>
<img style='position: fixed; top: 20px; right: -58px; border: 0; z-index: 100; transform: rotate(45deg);' src='/img/horizontal-github-ribbon.png'/>
</a>
</div><div id='st-container' class='st-container st-effect-9'>
<nav class='st-menu st-effect-9' id='menu-12'>
<h2 class='icon icon-lab'>Socialize</h2><ul>
<li>
<a href='https://groovy-lang.org/mailing-lists.html' class='icon'><span class='fa fa-envelope'></span> Discuss on the mailing-list</a>
</li><li>
<a href='https://twitter.com/ApacheGroovy' class='icon'><span class='fa fa-twitter'></span> Groovy on Twitter</a>
</li><li>
<a href='https://groovy-lang.org/events.html' class='icon'><span class='fa fa-calendar'></span> Events and conferences</a>
</li><li>
<a href='https://github.com/apache/groovy' class='icon'><span class='fa fa-github'></span> Source code on GitHub</a>
</li><li>
<a href='https://groovy-lang.org/reporting-issues.html' class='icon'><span class='fa fa-bug'></span> Report issues in Jira</a>
</li><li>
<a href='http://stackoverflow.com/questions/tagged/groovy' class='icon'><span class='fa fa-stack-overflow'></span> Stack Overflow questions</a>
</li><li>
<a href='http://groovycommunity.com/' class='icon'><span class='fa fa-slack'></span> Slack Community</a>
</li>
</ul>
</nav><div class='st-pusher'>
<div class='st-content'>
<div class='st-content-inner'>
<!--[if lt IE 7]>
<p class="browsehappy">You are using an <strong>outdated</strong> browser. Please <a href="http://browsehappy.com/">upgrade your browser</a> to improve your experience.</p>
<![endif]--><div><div class='navbar navbar-default navbar-static-top' role='navigation'>
<div class='container'>
<div class='navbar-header'>
<button type='button' class='navbar-toggle' data-toggle='collapse' data-target='.navbar-collapse'>
<span class='sr-only'></span><span class='icon-bar'></span><span class='icon-bar'></span><span class='icon-bar'></span>
</button><a class='navbar-brand' href='../index.html'>
<i class='fa fa-star'></i> Apache Groovy
</a>
</div><div class='navbar-collapse collapse'>
<ul class='nav navbar-nav navbar-right'>
<li class=''><a href='https://groovy-lang.org/learn.html'>Learn</a></li><li class=''><a href='https://groovy-lang.org/documentation.html'>Documentation</a></li><li class=''><a href='/download.html'>Download</a></li><li class=''><a href='https://groovy-lang.org/support.html'>Support</a></li><li class=''><a href='/'>Contribute</a></li><li class=''><a href='https://groovy-lang.org/ecosystem.html'>Ecosystem</a></li><li class=''><a href='/blog'>Blog posts</a></li><li class=''><a href='https://groovy.apache.org/events.html'></a></li><li>
<a data-effect='st-effect-9' class='st-trigger' href='#'>Socialize</a>
</li><li class=''>
<a href='../search.html'>
<i class='fa fa-search'></i>
</a>
</li>
</ul>
</div>
</div>
</div><div id='content' class='page-1'><div class='row'><div class='row-fluid'><div class='col-lg-3'><ul class='nav-sidebar'><li><a href='./'>Blog index</a></li><li class='active'><a href='#doc'>Matrix calculations with Groovy, Apache Commons Math, ojAlgo, Nd4j and EJML</a></li><li><a href='#_fibonacci' class='anchor-link'>Fibonacci</a></li><li><a href='#_leslie_matrices' class='anchor-link'>Leslie Matrices</a></li><li><a href='#_encryption_with_matrices' class='anchor-link'>Encryption with matrices</a></li><li><a href='#_shape_manipulation' class='anchor-link'>Shape manipulation</a></li><li><a href='#_language_and_tool_extensibility' class='anchor-link'>Language and tool extensibility</a></li><li><a href='#_further_information' class='anchor-link'>Further information</a></li><li><a href='#_conclusion' class='anchor-link'>Conclusion</a></li></ul><br/><ul class='nav-sidebar'><li style='padding: 0.35em 0.625em; background-color: #eee'><span>Related posts</span></li><li><a href='./classifying-iris-flowers-with-deep'>Classifying Iris Flowers with Deep Learning, Groovy and GraalVM</a></li><li><a href='./deep-learning-and-eclipse-collections'>Deep Learning and Eclipse Collections</a></li><li><a href='./fun-with-obfuscated-groovy'>Fun with obfuscated Groovy</a></li><li><a href='./whiskey-clustering-with-groovy-and'>Whiskey Clustering with Groovy and Apache Ignite</a></li><li><a href='./reading-and-writing-csv-files'>Reading and Writing CSV files with Groovy</a></li><li><a href='./using-groovy-with-apache-wayang'>Using Groovy with Apache Wayang and Apache Spark</a></li><li><a href='./detecting-objects-with-groovy-the'>Detecting objects with Groovy, the Deep Java Library (DJL), and Apache MXNet</a></li><li><a href='./fruity-eclipse-collections'>Fruity Eclipse Collections</a></li></ul></div><div class='col-lg-8 col-lg-pull-0'><a name='doc'></a><h1>Matrix calculations with Groovy, Apache Commons Math, ojAlgo, Nd4j and EJML</h1><p><span>Author: <i>Paul King</i></span><br/><span>Published: 2022-08-18 01:41PM</span></p><hr/><div id="preamble">
<div class="sectionbody">
<div class="paragraph">
<p>This blogs looks at performing matrix calculations with Groovy
using various libraries:
<a href="https://commons.apache.org/">Apache Commons</a>
<a href="https://commons.apache.org/proper/commons-math/">Math</a>,
<a href="https://www.ojalgo.org/">ojAlgo</a>,
<a href="https://ejml.org/">EJML</a>, and
<a href="https://deeplearning4j.konduit.ai/nd4j/tutorials/quickstart">Nd4j</a>
(part of Eclipse <a href="https://deeplearning4j.konduit.ai/">Deeplearning4j</a>).
We&#8217;ll also take a quick
look at using the incubating Vector API for matrix calculations
(JEPs <a href="https://openjdk.org/jeps/338">338</a>,
<a href="https://openjdk.org/jeps/414">414</a>,
<a href="https://openjdk.org/jeps/417">417</a>,
and <a href="https://openjdk.org/jeps/426">426</a>).</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_fibonacci">Fibonacci</h2>
<div class="sectionbody">
<div class="paragraph">
<p>The Fibonacci sequence has origins in India centuries earlier but
is named after the Italian author of the publication,
<a href="https://en.wikipedia.org/wiki/Fibonacci_number">The Book of Calculation</a>, published in 1202. In that publication, Fibonacci
proposed the sequence as a means to calculate the growth of
idealized (biologically unrealistic) rabbit populations.
He proposed that a newly born breeding pair of rabbits are
put in a field; each breeding pair mates at the age of one month,
and at the end of their second month they always produce another
pair of rabbits; and rabbits never die, but continue breeding
forever. Fibonacci posed the puzzle: how many pairs will there
be in one year? The sequence goes like this:</p>
</div>
<div class="listingblock">
<div class="content">
<pre>1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233</pre>
</div>
</div>
<div class="paragraph">
<p>We can solve this problem using matrices. If we multiply the matrix <span class="image"><img src="img/FibMatrix.png" alt="fibonacci matrix" width="43"></span> by itself n times we get <span class="image"><img src="img/FibMatrixN.png" alt="fib n matrix" width="82"></span>.
This is an operation known as matrix exponentiation.
Let&#8217;s explore this problem using four of the most popular
and maintained matrix libraries.</p>
</div>
<div class="sect2">
<h3 id="_apache_commons_math">Apache Commons Math</h3>
<div class="paragraph">
<p>Let&#8217;s explore solving this problem using Apache Commons Math.
Apache Commons Math is a library of lightweight self-contained
mathematics and statics components. Matrices are part of the
linear algebra part of this library and for that context,
matrices of double values are relevant. So, we&#8217;ll represent
our Fibonacci numbers as double values.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code data-lang="groovy">double[][] data = [[1d, 1d], [1d, 0d]]
def m = MatrixUtils.createRealMatrix(data)
println m * m * m
println m**6</code></pre>
</div>
</div>
<div class="paragraph">
<p>Commons math has a factory method for creating matrixes from
double arrays. The names of the methods for multiplication and
exponentiation happen to align with Groovy&#8217;s methods available
for operator overloading, namely <code>multiply</code> and <code>power</code>,
so we can use Groovy&#8217;s convenient shorthands.</p>
</div>
<div class="paragraph">
<p>When we run the script, the output looks like this:</p>
</div>
<div class="listingblock">
<div class="content">
<pre>Array2DRowRealMatrix{{3.0,2.0},{2.0,1.0}}
Array2DRowRealMatrix{{13.0,8.0},{8.0,5.0}}</pre>
</div>
</div>
<div class="paragraph">
<p>We could go a little further and print the values from the
matrix, but the result is clear enough. We see the values
in the Fibonacci sequence appearing in the output.</p>
</div>
</div>
<div class="sect2">
<h3 id="_ejml">EJML</h3>
<div class="paragraph">
<p>EJML (Efficient Java Matrix Library) is a linear algebra library
for manipulating real, complex, dense, and sparse matrices.
It is also a 100% Java solution. It has some novel features
including import from Matlab and support for semirings
(<a href="https://en.wikipedia.org/wiki/GraphBLAS">GraphBLAS</a>) which
can be used for graph algorithms where a sparse matrix may
be used to represent a graph as an adjacency matrix or
incidence matrix.</p>
</div>
<div class="paragraph">
<p>We can do the same calculation using EJML:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code data-lang="groovy">def m = new SimpleMatrix([[1d, 1d], [1d, 0d]] as double[][])
def ans = m.mult(m).mult(m)
println ans
6.times { ans = ans.mult(m) }
println ans</code></pre>
</div>
</div>
<div class="paragraph">
<p>The name of the multiply method differs from the one where we
can use automatic operator overloading shorthands, so we just
call the method that EJML provides. See a little later under
language extensibility on how we could in fact add support to
use the same shorthands as we saw for Commons Math.</p>
</div>
<div class="paragraph">
<p>EJML doesn&#8217;t have an exponentiation method but we just call
multiply the requisite number of times to achieve the same effect.
Note that we bumped the number of iterations called for the second
matrix, to reveal the next bunch of elements from the Fibonacci
sequence.</p>
</div>
<div class="paragraph">
<p>Running this script has this output:</p>
</div>
<div class="listingblock">
<div class="content">
<pre>Type = DDRM , rows = 2 , cols = 2
3.0000E+00 2.0000E+00
2.0000E+00 1.0000E+00
Type = DDRM , rows = 2 , cols = 2
5.5000E+01 3.4000E+01
3.4000E+01 2.1000E+01</pre>
</div>
</div>
<div class="paragraph">
<p>The first matrix has the same number as previously, and the second
reveals the next numbers in the Fibonacci sequence (21, 34, and 55).</p>
</div>
</div>
<div class="sect2">
<h3 id="_nd4j">Nd4j</h3>
<div class="paragraph">
<p>Nd4j provides functionality available on Python to Java users.
It contains a mix of numpy operations and tensorflow/pytorch
operations. Nd4l makes use of native backends to allow it to
work on different platforms and provide efficient operation when
scaling up.</p>
</div>
<div class="paragraph">
<p>The code for our Fibonacci solution is very similar to EJML:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code data-lang="groovy">def m = Nd4j.create([[1, 1], [1, 0]] as int[][])
def ans = m.mmul(m).mmul(m)
println ans
9.times { ans = ans.mmul(m) }
println ans</code></pre>
</div>
</div>
<div class="paragraph">
<p>One feature that is different to the previous two libraries is
that Nd4j supports <em>integer</em> matrices (as well as doubles and
numerous other numerical and related types).</p>
</div>
<div class="paragraph">
<p>Running the script gives the following output:</p>
</div>
<div class="listingblock">
<div class="content">
<pre><span class="maroon">...
[main] INFO org.nd4j.linalg.factory.Nd4jBackend - Loaded [CpuBackend] backend
...
[main] INFO org.nd4j.linalg.cpu.nativecpu.CpuNDArrayFactory - Binary level Generic x86 optimization level AVX/AVX2
[main] INFO org.nd4j.linalg.api.ops.executioner.DefaultOpExecutioner - Blas vendor: [OPENBLAS]
...</span>
[[ 3, 2],
[ 2, 1]]
[[ 233, 144],
[ 144, 89]]</pre>
</div>
</div>
<div class="paragraph">
<p>Again, the first matrix is the same as we have seen previously,
the second has been bumped along the Fibonacci sequence by
three more elements.</p>
</div>
</div>
<div class="sect2">
<h3 id="_ojalgo">ojAlgo</h3>
<div class="paragraph">
<p>The next library we&#8217;ll look at is ojAlgo (oj! Algorithms).
It is an open source all-Java offering for mathematics, linear
algebra and optimisation, supporting data science, machine
learning and scientific computing. It claims to be the fastest
pure-Java linear algebra library available and the project
website provide links to numerous benchmarks backing that claim.</p>
</div>
<div class="paragraph">
<p>Here is the code for our Fibonacci example:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code data-lang="groovy">def factory = Primitive64Matrix.FACTORY
def m = factory.rows([[1d, 1d], [1d, 0d]] as double[][])
println m * m * m
println m**15</code></pre>
</div>
</div>
<div class="paragraph">
<p>We can see it supports the same operator overloading features we
saw for Commons Math.</p>
</div>
<div class="paragraph">
<p>When we run the script, it has the following output:</p>
</div>
<div class="listingblock">
<div class="content">
<pre>org.ojalgo.matrix.Primitive64Matrix &lt; 2 x 2 &gt;
{ { 3.0, 2.0 },
{ 2.0, 1.0 } }
org.ojalgo.matrix.Primitive64Matrix &lt; 2 x 2 &gt;
{ { 987.0, 610.0 },
{ 610.0, 377.0 } }</pre>
</div>
</div>
<div class="paragraph">
<p>As expected, the first matrix is as we&#8217;ve seen before, while the
second reveals the next three numbers in the sequence.</p>
</div>
</div>
<div class="sect2">
<h3 id="_exploring_the_vector_api_and_ejml">Exploring the Vector API and EJML</h3>
<div class="paragraph">
<p>From JDK16, various versions (JEPs
<a href="https://openjdk.org/jeps/338">338</a>,
<a href="https://openjdk.org/jeps/414">414</a>,
<a href="https://openjdk.org/jeps/417">417</a>,
<a href="https://openjdk.org/jeps/426">426</a>) of the Vector API
have been available as an incubating preview feature.
The HotSpot compiler has previously already had some minimal
optimisations that can leverage vector hardware instructions
but the Vector API expands the scope of possible optimisations
considerably. We could look at writing our own code that might
make use of the Vector API and perhaps perform our matrix
multiplications ourselves. But, one of the libraries has already
done just that, so we&#8217;ll explore that path.</p>
</div>
<div class="paragraph">
<p>The main contributor to the EJML library has published a
<a href="https://github.com/lessthanoptimal/VectorPerformance">repo</a>
for the purposes of very early prototyping and benchmarking.
We&#8217;ll use the methods from one of its classes to explore use
of the vector API for our Fibonacci example. The
<code>MatrixMultiplication</code> class has three methods:
<code>mult_ikj_simple</code> is coded in the way any of us might write a
multiplication method as a first pass from its definition without
any attempts at optimisation, <code>mult_ikj</code> is coded in a
highly-optimised fashion and corresponds to the code EJML would
normally use, and <code>mult_ikj_vector</code> uses the Vector API. Note, you can think of these methods as "one layer down" from the <code>mult</code>
method we called in the previous example, i.e.&nbsp;the <code>mult</code> method
we used previously would be calling one of these under the covers.
That&#8217;s why we pass the internal "matrix" representation instead
of our <code>SimpleMatrix</code> instance.</p>
</div>
<div class="paragraph">
<p>For our little calculations, the optimisations offered by the
Vector API would not be expected to be huge. However, we&#8217;ll do
our calculation for generating the matrix we did as a first step
for all of the libraries and we&#8217;ll do it in a loop with 1000
iterations for each of the three methods (<code>mult_ikj_simple</code>,
<code>mult_ikj</code>, and <code>mult_ikj_vector</code>). The code looks like this:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code data-lang="groovy">def m = new SimpleMatrix([[1, 1], [1, 0]] as double[][])
double[] expected = [3, 2, 2, 1]
def step1, result
long t0 = System.nanoTime()
1000.times {
step1 = new SimpleMatrix(2, 2)
result = new SimpleMatrix(2, 2)
MatrixMultiplication.mult_ikj_simple(m.matrix, m.matrix, step1.matrix)
MatrixMultiplication.mult_ikj_simple(step1.matrix, m.matrix, result.matrix)
assert result.matrix.data == expected
}
long t1 = System.nanoTime()
1000.times {
step1 = new SimpleMatrix(2, 2)
result = new SimpleMatrix(2, 2)
MatrixMultiplication.mult_ikj(m.matrix, m.matrix, step1.matrix)
MatrixMultiplication.mult_ikj(step1.matrix, m.matrix, result.matrix)
assert result.matrix.data == expected
}
long t2 = System.nanoTime()
1000.times {
step1 = new SimpleMatrix(2, 2)
result = new SimpleMatrix(2, 2)
MatrixMultiplication.mult_ikj_vector(m.matrix, m.matrix, step1.matrix)
MatrixMultiplication.mult_ikj_vector(step1.matrix, m.matrix, result.matrix)
assert result.matrix.data == expected
}
long t3 = System.nanoTime()
printf "Simple: %6.2f ms\n", (t1 - t0)/1000_000
printf "Optimized: %6.2f ms\n", (t2 - t1)/1000_000
printf "Vector: %6.2f ms\n", (t3 - t2)/1000_000</code></pre>
</div>
</div>
<div class="paragraph">
<p>This example was run on JDK16 with the following VM options:
<code>–enable-preview –add-modules jdk.incubator.vector</code>.</p>
</div>
<div class="paragraph">
<p>The output looks like this:</p>
</div>
<div class="listingblock">
<div class="content">
<pre><span class="maroon">WARNING: Using incubator modules: jdk.incubator.vector</span>
Simple: 116.34 ms
Optimized: 34.91 ms
Vector: 21.94 ms</pre>
</div>
</div>
<div class="paragraph">
<p>We can see here that we have some improvement even for our trivial
little calculation. Certainly, for biggest problems, the benefit
of using the Vector API could be quite substantial.</p>
</div>
<div class="paragraph">
<p>We should give a big disclaimer here. This little microbenchmark
using a loop of 1000 will give highly variable results and was
just done to give a very simple performance comparison.
For a more predictable comparison, consider running the
<a href="https://github.com/openjdk/jmh">jmh</a> benchmark in the aforementioned
<a href="https://github.com/lessthanoptimal/VectorPerformance">repo</a>.
And you may wish to wait until the Vector API is out of preview
before relying on it for any production code - but by all means,
consider trying it out now.</p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_leslie_matrices">Leslie Matrices</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Earlier, we described the Fibonacci sequence as being for
<em>unrealistic</em> rabbit populations, where rabbits never died and
continued breeding forever. It turns out that Fibonacci matrices
are a special case of a more generalised model which can model
realistic rabbit populations (among other things). These are
<a href="https://en.wikipedia.org/wiki/Leslie_matrix">Leslie matrices</a>.
For Leslie matrices, populations are divided into classes,
and we keep track of birth rates and survival rates over a
particular period for each class. We store this information
in a matrix in a special form. The populations for each class
for the next period can be calculated from those for the current
period through multiplication by the Leslie matrix.</p>
</div>
<div class="paragraph">
<p>This technique can be used for animal populations or human
population calculations. A Leslie matrix can help you find out
if there will be enough GenX, Millenials, and GenZ tax payers
to support an aging and soon retiring baby boomer population.
Sophisticated animal models might track populations for an animal
and for its predators or its prey. The survival and birth rates
might be adjusted based on such information. Given that only
females give birth, Leslie models will often be done only in
terms of the female population, with the total population
extrapolated from that.</p>
</div>
<div class="paragraph">
<p>We&#8217;ll show an example for kangaroo population based on this
<a href="https://www.youtube.com/watch?v=I5WM2wdjr1M">video tutorial</a>
for Leslie matrices. It can help us find out if the kangaroo
population is under threat (perhaps drought, fires or floods
have impacted their feeding habitat) or if favorable conditions
are leading to overpopulation.</p>
</div>
<div class="paragraph">
<p>Following that example, we divide kangaroos into 3 population
classes: ages 0 to 3, 3 to 6, and 6 to 9. We are going to look
at the population every three years. The 0-3 year olds birthrate
(B1) is 0 since they are pre-fertile. The most fertile 3-6 year
olds birthrate (B2) is 2.3. The old roos (6-9) have a birthrate
(B3) of 0.4. We assume no kangaroos survive past 9 years old.
60% (S1) of the young kangaroos survive to move into the next age
group. 30% (S2) of the middle-aged kangaroos survive into old age.
Initially, we have 400 kangaroos in each age group.</p>
</div>
<div class="paragraph">
<p>Here is what the code looks like for this model:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code data-lang="groovy">double[] init = [400, // 0..&lt;3
400, // 3..&lt;6
400] // 6..9
def p0 = MatrixUtils.createRealVector(init)
println "Initial populations: $p0"
double[][] data = [
[0 , 2.3, 0.4], // B1 B2 B3
[0.6, 0, 0 ], // S1 0 0
[0 , 0.3, 0 ] // 0 S2 0
]
def L = MatrixUtils.createRealMatrix(data)
def p1 = L.operate(p0)
println "Population after 1 round: $p1"
def p2 = L.operate(p1)
println "Population after 2 rounds: $p2"
def L10 = L ** 10
println "Population after 10 rounds: ${L10.operate(p0).toArray()*.round()}"</code></pre>
</div>
</div>
<div class="paragraph">
<p>This code produces the following output:</p>
</div>
<div class="listingblock">
<div class="content">
<pre>Initial populations: {400; 400; 400}
Population after 1 round: {1,080; 240; 120}
Population after 2 rounds: {600; 648; 72}
Population after 10 rounds: [3019, 2558, 365]</pre>
</div>
</div>
<div class="paragraph">
<p>After the first round, we see many young roos but a worrying drop
off in the older age groups. After the second round, only the
oldest age group looks worryingly small. However, with the healthy
numbers in the young generation, we can see that after 10
generations that indeed, the overall population is not at risk.
In fact, overpopulation might become a problem.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_encryption_with_matrices">Encryption with matrices</h2>
<div class="sectionbody">
<div class="paragraph">
<p>An early technique to encrypt a message was the
<a href="https://en.wikipedia.org/wiki/Caesar_cipher">Caesar cipher</a>.
It substitutes letters in the alphabet by the letter shifted a
certain amount along in the alphabet, e.g.&nbsp;"IBM" becomes "HAL"
if shifting to the previous letter and "VMS" becomes "WNT" if
shifting one letter forward in the alphabet. This kind of cipher
can be broken by looking at frequency analysis of letters or
pattern words.</p>
</div>
<div class="paragraph">
<p>The <a href="https://en.wikipedia.org/wiki/Hill_cipher">Hill cipher</a>
improves upon the Caesar cipher by factoring multiple letters
into each letter of the encrypted text. Using matrices made it
practical to look at three or more symbols at once. In general,
an N-by-N matrix (the key) is multiplied by an encoding of the
message in matrix form. The result is a matrix representation
(encoded form) of the encrypted text. We use the inverse matrix
of our key to decrypt or message.</p>
</div>
<div class="paragraph">
<p>We need to have a way to convert our text message to and from
a matrix. A common scheme is to encode A as 1, B as 2, and so on.
We&#8217;ll just use the ascii value for each character. We define
<code>encode</code> and <code>decode</code> methods to do this:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code data-lang="groovy">double[][] encode(String s) { s.bytes*.intValue().collate(3) as double[][] }
String decode(double[] data) { data*.round() as char[] }</code></pre>
</div>
</div>
<div class="paragraph">
<p>We&#8217;ll define a 2-by-2 matrix as our key and use it to encrypt.
We&#8217;ll find the inverse of our key and use that to decrypt.
If we wanted to, we could use a 3-by-3 key for improved security
at the cost of more processing time.</p>
</div>
<div class="paragraph">
<p>Our code looks like this:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code data-lang="groovy">def message = 'GROOVY'
def m = new SimpleMatrix(encode(message))
println "Original: $message"
def enKey = new SimpleMatrix([[1, 3], [-1, 2]] as double[][])
def encrypted = enKey.mult(m)
println "Encrypted: ${decode(encrypted.matrix.data)}"
def deKey = enKey.invert()
def decrypted = deKey.mult(encrypted)
println "Decrypted: ${decode(decrypted.matrix.data)}"</code></pre>
</div>
</div>
<div class="paragraph">
<p>When run, it has the following output:</p>
</div>
<div class="listingblock">
<div class="content">
<pre>Original: GROOVY
Encrypted: ĴŔŚWZc
Decrypted: GROOVY</pre>
</div>
</div>
<div class="paragraph">
<p>This offers far more security than the Caesar cipher, however,
given today&#8217;s computing availability, Hill ciphers can still
eventually be broken with sufficient brute force. For this reason,
Hill ciphers are seldom used on their own for encryption, but they
<em>are</em> often used in combination with other techniques to add
diffusion - strengthening the security offered by the other
techniques.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_shape_manipulation">Shape manipulation</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Our final example looks at geometrically transforming shapes.
To do this, we represent the points of the shape as vectors and
multiply them using transforms represented as matrices. We need
only worry about the corners, since we&#8217;ll use Swing to draw our
shape, and it has a method for drawing a polygon by giving its
corners.</p>
</div>
<div class="paragraph">
<p>First we&#8217;ll use Groovy&#8217;s <code>SwingBuilder</code> to set up our frame:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code data-lang="groovy">new SwingBuilder().edt {
def frame = frame(title: 'Shapes', size: [420, 440], show: true, defaultCloseOperation: DISPOSE_ON_CLOSE) {
//contentPane.background = Color.WHITE
widget(new CustomPaintComponent())
}
frame.contentPane.background = Color.WHITE
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>We aren&#8217;t really use much of SwingBuilder&#8217;s functionality here
but if we wanted to add more functionality, SwingBuilder would
make that task easier.</p>
</div>
<div class="paragraph">
<p>We will actually draw our shapes within a custom component.
We&#8217;ll define a few color constants, a <code>drawPolygon</code> method which
given a matrix of points will draw those points as a polygon.
We&#8217;ll also define a <code>vectors</code> method to convert a list of points
(the corners) into vectors, and a <code>transform</code> method which is a
factory method for creating a transform matrix.</p>
</div>
<div class="paragraph">
<p>Here is the code:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code data-lang="groovy">class CustomPaintComponent extends Component {
static final Color violet = new Color(0x67, 0x27, 0x7A, 127)
static final Color seaGreen = new Color(0x69, 0xCC, 0x67, 127)
static final Color crystalBlue = new Color(0x06, 0x4B, 0x93, 127)
static drawPolygon(Graphics g, List pts, boolean fill) {
def poly = new Polygon().tap {
pts.each {
addPoint(*it.toRawCopy1D()*.round()*.intValue().collect { it + 200 })
}
}
fill ? g.fillPolygon(poly) : g.drawPolygon(poly)
}
static List&lt;Primitive64Matrix&gt; vectors(List&lt;Integer&gt;... pts) {
pts.collect{ factory.column(*it) }
}
static transform(List&lt;Number&gt;... lists) {
factory.rows(lists as double[][])
}
void paint(Graphics g) {
g.drawLine(0, 200, 400, 200)
g.drawLine(200, 0, 200, 400)
g.stroke = new BasicStroke(2)
def triangle = vectors([-85, -150], [-145, -30], [-25, -30])
g.color = seaGreen
drawPolygon(g, triangle, true)
// transform triangle
...
def rectangle = vectors([0, -110], [0, -45], [95, -45], [95, -110])
g.color = crystalBlue
drawPolygon(g, rectangle, true)
// transform rectangle
...
def trapezoid = vectors([50, 50], [70, 100], [100, 100], [120, 50])
g.color = violet
drawPolygon(g, trapezoid, true)
// transform trapezoid
...
}
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>When we run this code we see our three shapes:</p>
</div>
<div class="paragraph">
<p><span class="image"><img src="img/MatrixShapes.png" alt="Shapes"></span></p>
</div>
<div class="paragraph">
<p>We can now add our transforms. We&#8217;ll have one which rotate by 90
degrees anti-clockwise. Another which enlarges a shape by 25% and
one that shrinks a shape by 25%. We can combine transforms simply
by multiplying them together. We&#8217;ll make two transformations of
our triangle. We&#8217;ll rotate in both cases but we&#8217;ll shrink one and
enlarge the other. We apply the transform simply by multiplying
each point by the transform matrix. Then we&#8217;ll draw both
transformed shapes as an outline rather than filled (to make it
easier to distinguish the original and transformed versions).
Here is the code:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code data-lang="groovy">def rot90 = transform([0, 1], [-1, 0])
def bigger = transform([1.25, 0], [0, 1.25])
def smaller = transform([0.75, 0], [0, 0.75])
def triangle_ = triangle.collect{ rot90 * bigger * it }
drawPolygon(g, triangle_, false)
triangle_ = triangle.collect{ rot90 * smaller * it }
drawPolygon(g, triangle_, false)</code></pre>
</div>
</div>
<div class="paragraph">
<p>For our rectangle, we&#8217;ll have one simple transform which flips
the shape in the vertical axis. A second transform combines
multiple changes in one transform. We could have split this into
smaller transforms and the multiplied them together - but here
they are all in one. We flip in the horizontal access and then
apply a shear. We then draw the transformed shapes as outlines:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code data-lang="groovy">def flipV = transform([1, 0], [0, -1])
def rectangle_ = rectangle.collect{ flipV * it }
drawPolygon(g, rectangle_, false)
def flipH_shear = transform([-1, 0.5], [0, 1])
rectangle_ = rectangle.collect{ flipH_shear * it }
drawPolygon(g, rectangle_, false)</code></pre>
</div>
</div>
<div class="paragraph">
<p>For our trapezoid, we create a transform which rotates 45 degrees
anti-clockwise (recall sin 45° = cos 45° = 0.707). Then we create
6 transforms rotating at 45, 90, 135 and so forth. We draw each
transformed shape as an outline:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code data-lang="groovy">def rot45 = transform([0.707, 0.707], [-0.707, 0.707])
def trapezoid_
(1..6).each { z -&gt;
trapezoid_ = trapezoid.collect{ rot45 ** z * it }
drawPolygon(g, trapezoid_, false)
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>When we run the entire example, here is what the output looks
like:</p>
</div>
<div class="paragraph">
<p><span class="image"><img src="img/MatrixShapesTransformed.png" alt="Shapes transformed"></span></p>
</div>
<div class="paragraph">
<p>We can see here that matrix transforms give us powerful ways to
manipulate images. We have used 2D images here, but the same
techniques would apply to 3D shapes.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_language_and_tool_extensibility">Language and tool extensibility</h2>
<div class="sectionbody">
<div class="paragraph">
<p>We saw earlier that some of the examples could make use of Groovy
operator shorthand syntax, while others couldn&#8217;t. Here is a
summary of some common methods in the libraries:</p>
</div>
<table class="tableblock frame-all grid-all stretch">
<colgroup>
<col style="width: 20%;">
<col style="width: 20%;">
<col style="width: 20%;">
<col style="width: 20%;">
<col style="width: 20%;">
</colgroup>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><strong class="blue"><em>Groovy operator&nbsp;</em></strong></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><strong class="blue"><em>+</em></strong></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><strong class="blue"><em>-</em></strong></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><strong class="blue"><em>*</em></strong></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><strong class="blue"><em>**</em></strong></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><strong class="blue"><em>Groovy method</em></strong></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><strong class="blue"><em>plus</em></strong></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><strong class="blue"><em>minus</em></strong></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><strong class="blue"><em>multiply</em></strong></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><strong class="blue"><em>power</em></strong></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><strong class="aqua"><em>Commons math</em></strong></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>add</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>subtract</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>multiply</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>power</code></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><strong class="aqua"><em>EJML</em></strong></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>plus</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>minus</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>mult</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">-</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><strong class="aqua"><em>Nd4j</em></strong></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>add</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>sub</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>mmul</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">-</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><strong class="aqua"><em>ojAlgo</em></strong></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>add</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>subtract</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>multiply</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>power</code></p></td>
</tr>
</tbody>
</table>
<div class="paragraph">
<p>Where the library used the same name as Groovy&#8217;s method,
the shorthand could be used.</p>
</div>
<div class="paragraph">
<p>Groovy offers numerous extensibility features. We won&#8217;t go into
all of the details but instead refer readers to the
<a href="https://dl.acm.org/doi/abs/10.1145/3386326">History of Groovy</a>
paper which gives more details.</p>
</div>
<div class="paragraph">
<p>In summary, that paper defined the following extensions for
Commons Math matrices:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code data-lang="groovy">RealMatrix.metaClass {
plus &lt;&lt; { RealMatrix ma -&gt; delegate.add(ma) }
plus &lt;&lt; { double d -&gt; delegate.scalarAdd(d) }
multiply { double d -&gt; delegate.scalarMultiply(d) }
bitwiseNegate { -&gt; new LUDecomposition(delegate).solver.inverse }
constructor = { List l -&gt; MatrixUtils.createRealMatrix(l as double[][]) }
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>This fixes some of the method name discrepancies in the above
table. We can now use the operator shorthand for matrix and
scalar addition as well as scalar multiply. We can also use
the ~ (<code>bitwiseNegate</code>) operator when finding the inverse.
The mention of <code>double[][]</code> during matrix construction is
now also gone.</p>
</div>
<div class="paragraph">
<p>The paper goes on to discuss how to automatically add the
necessary imports for a matrix library and provide aliases if
needed. The imports aren&#8217;t shown for the code listings in this
blog but are in the listings in the sample code repo.
In any case, the imports can be "configured away" as the paper
discusses. The end result is our code in its entirety can look
like this:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code data-lang="groovy">var m = [[1, 1], [1, 0]] as Matrix
m**6</code></pre>
</div>
</div>
<div class="paragraph">
<p>The paper also discusses tooling extensibility, in particular the
visualisation aspects of the GroovyConsole. It shows how to define
an output transform which renders any matrix result using a
jlatexmath library. So instead of seeing
<code>Array2DRowRealMatrix{{13.0,8.0},{8.0,5.0}}</code>, they will see
a graphical rendition of the matrix. So, the final end-user
experience when using the GroovyConsole looks like this:</p>
</div>
<div class="paragraph">
<p><span class="image"><img src="img/GroovyConsoleOutputTransformsMatrix.png" alt="matrix output transforms in groovy console"></span></p>
</div>
<div class="paragraph">
<p>When using in Jupyter style environments, other pretty output
styles may be supported.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_further_information">Further information</h2>
<div class="sectionbody">
<div class="ulist">
<ul>
<li>
<p>Apache Commons Math <a href="https://commons.apache.org/proper/commons-math/userguide/linear.html">User Guide</a> and <a href="https://github.com/apache/commons-math">GitHub mirror</a></p>
</li>
<li>
<p>ojAlgo <a href="https://www.ojalgo.org/">website</a> and <a href="https://github.com/optimatika/ojAlgo/">GitHub repo</a></p>
</li>
<li>
<p>EJML <a href="http://ejml.org/">website</a> and <a href="https://github.com/lessthanoptimal/ejml">GitHub repo</a></p>
</li>
<li>
<p>Nd4j <a href="https://deeplearning4j.konduit.ai/nd4j/tutorials/quickstart">documentation</a> and <a href="https://github.com/eclipse/deeplearning4j/tree/master/nd4j">GitHub repo</a></p>
</li>
<li>
<p><a href="https://github.com/paulk-asert/MatrixGroovy">Example code repo</a></p>
</li>
<li>
<p><a href="https://www.youtube.com/watch?v=I5WM2wdjr1M">Leslie Matrices for population age distribution modelling</a> video</p>
</li>
<li>
<p><a href="https://dl.acm.org/doi/abs/10.1145/3386326">A history of the Groovy programming language</a> paper</p>
</li>
<li>
<p><a href="https://medium.com/@Styp/java-18-vector-api-do-we-get-free-speed-up-c4510eda50d2">Java 18: Vector API — Do we get free speed-up?</a> blog post</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_conclusion">Conclusion</h2>
<div class="sectionbody">
<div class="paragraph">
<p>We have examined a number of simple applications of matrices using
the Groovy programming language and the Apache Commons Math,
ojAlgo, Nd4j, and EJML libraries. You should be convinced that
using matrices on the JVM isn&#8217;t hard, and you have numerous
options. We also saw a brief glimpse at the Vector API which
looks like an exciting addition to the JVM (hopefully arriving
soon in non-preview mode).</p>
</div>
</div>
</div></div></div></div></div><footer id='footer'>
<div class='row'>
<div class='colset-3-footer'>
<div class='col-1'>
<h1>Groovy</h1><ul>
<li><a href='https://groovy-lang.org/learn.html'>Learn</a></li><li><a href='https://groovy-lang.org/documentation.html'>Documentation</a></li><li><a href='/download.html'>Download</a></li><li><a href='https://groovy-lang.org/support.html'>Support</a></li><li><a href='/'>Contribute</a></li><li><a href='https://groovy-lang.org/ecosystem.html'>Ecosystem</a></li><li><a href='/blog'>Blog posts</a></li><li><a href='https://groovy.apache.org/events.html'></a></li>
</ul>
</div><div class='col-2'>
<h1>About</h1><ul>
<li><a href='https://github.com/apache/groovy'>Source code</a></li><li><a href='https://groovy-lang.org/security.html'>Security</a></li><li><a href='https://groovy-lang.org/learn.html#books'>Books</a></li><li><a href='https://groovy-lang.org/thanks.html'>Thanks</a></li><li><a href='http://www.apache.org/foundation/sponsorship.html'>Sponsorship</a></li><li><a href='https://groovy-lang.org/faq.html'>FAQ</a></li><li><a href='https://groovy-lang.org/search.html'>Search</a></li>
</ul>
</div><div class='col-3'>
<h1>Socialize</h1><ul>
<li><a href='https://groovy-lang.org/mailing-lists.html'>Discuss on the mailing-list</a></li><li><a href='https://twitter.com/ApacheGroovy'>Groovy on Twitter</a></li><li><a href='https://groovy-lang.org/events.html'>Events and conferences</a></li><li><a href='https://github.com/apache/groovy'>Source code on GitHub</a></li><li><a href='https://groovy-lang.org/reporting-issues.html'>Report issues in Jira</a></li><li><a href='http://stackoverflow.com/questions/tagged/groovy'>Stack Overflow questions</a></li><li><a href='http://groovycommunity.com/'>Slack Community</a></li>
</ul>
</div><div class='col-right'>
<p>
The Groovy programming language is supported by the <a href='http://www.apache.org'>Apache Software Foundation</a> and the Groovy community.
</p><div text-align='right'>
<img src='../img/asf_logo.png' title='The Apache Software Foundation' alt='The Apache Software Foundation' style='width:60%'/>
</div><p>Apache&reg; and the Apache feather logo are either registered trademarks or trademarks of The Apache Software Foundation.</p>
</div>
</div><div class='clearfix'>&copy; 2003-2023 the Apache Groovy project &mdash; Groovy is Open Source: <a href='http://www.apache.org/licenses/LICENSE-2.0.html' alt='Apache 2 License'>license</a>, <a href='https://privacy.apache.org/policies/privacy-policy-public.html'>privacy policy</a>.</div>
</div>
</footer></div>
</div>
</div>
</div>
</div><script src='../js/vendor/jquery-1.10.2.min.js' defer></script><script src='../js/vendor/classie.js' defer></script><script src='../js/vendor/bootstrap.js' defer></script><script src='../js/vendor/sidebarEffects.js' defer></script><script src='../js/vendor/modernizr-2.6.2.min.js' defer></script><script src='../js/plugins.js' defer></script><script src='https://cdnjs.cloudflare.com/ajax/libs/prettify/r298/prettify.min.js'></script><script>document.addEventListener('DOMContentLoaded',prettyPrint)</script><script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-257558-10', 'auto');
ga('send', 'pageview');
</script>
</body></html>