181 lines
12 KiB
HTML
181 lines
12 KiB
HTML
<!DOCTYPE html PUBLIC ""
|
||
"">
|
||
<html><head><meta charset="UTF-8" /><title>Introduction to Sparse-array</title><link rel="stylesheet" type="text/css" href="css/default.css" /><link rel="stylesheet" type="text/css" href="css/highlight.css" /><script type="text/javascript" src="js/highlight.min.js"></script><script type="text/javascript" src="js/jquery.min.js"></script><script type="text/javascript" src="js/page_effects.js"></script><script>hljs.initHighlightingOnLoad();</script></head><body><div id="header"><h2>Generated by <a href="https://github.com/weavejester/codox">Codox</a></h2><h1><a href="index.html"><span class="project-title"><span class="project-name">Sparse-array</span> <span class="project-version">0.3.0</span></span></a></h1></div><div class="sidebar primary"><h3 class="no-link"><span class="inner">Project</span></h3><ul class="index-link"><li class="depth-1 "><a href="index.html"><div class="inner">Index</div></a></li></ul><h3 class="no-link"><span class="inner">Topics</span></h3><ul><li class="depth-1 current"><a href="intro.html"><div class="inner"><span>Introduction to Sparse-array</span></div></a></li></ul><h3 class="no-link"><span class="inner">Namespaces</span></h3><ul><li class="depth-1"><div class="no-link"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>sparse-array</span></div></div></li><li class="depth-2 branch"><a href="sparse-array.core.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>core</span></div></a></li><li class="depth-2"><a href="sparse-array.extract.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>extract</span></div></a></li></ul></div><div class="document" id="content"><div class="doc"><div class="markdown"><h1><a href="#introduction-to-sparse-array" name="introduction-to-sparse-array"></a>Introduction to Sparse-array</h1>
|
||
<p>A Clojure library designed to manipulate sparse <em>arrays</em> - multi-dimensional spaces accessed by indices, but containing arbitrary values rather than just numbers. For sparse spaces which contain numbers only, you’re better to use a <em>sparse matrix</em> library, <a href="https://mikera.github.io/core.matrix/">for example clojure.core.matrix</a>.</p>
|
||
<p>Arbitrary numbers of dimensions are supported, up to limits imposed by the JVM stack.</p>
|
||
<p>Available from Clojars: <a href="https://clojars.org/sparse-array"><img src="https://img.shields.io/clojars/v/sparse-array.svg" alt="Clojars Project" /></a> Tests currently <a href="https://circleci.com/gh/simon-brooke/sparse-array"><img src="https://circleci.com/gh/simon-brooke/sparse-array.svg?style=svg" alt="CircleCI" /></a></p>
|
||
<h2><a href="#conventions" name="conventions"></a>Conventions</h2>
|
||
<h3><a href="#sparse-arrays" name="sparse-arrays"></a>Sparse arrays</h3>
|
||
<p>For the purposes of this library, a sparse array shall be implemented as a map, such that all keys are non-negative members of the set of integers, except for the following keyword keys, all of which are expected to be present:</p>
|
||
<ol>
|
||
<li><code>:dimensions</code> The number of dimensions in this array, counting the present one (value expected to be a real number);</li>
|
||
<li><code>:coord</code> The coordinate of the dimension represented by the current map (value expected to be a keyword);</li>
|
||
<li><code>:content</code> What this map contains; if the value of <code>:dimensions</code> is one, then <code>:data</code>; otherwise, an ordered sequence of the coordinates of the dimensions below this one.</li>
|
||
</ol>
|
||
<p>Thus an array with a single value ‘hello’ at coordinates x = 3, y = 4, z = 5 would be encoded:</p>
|
||
<pre><code class="clojure">{:dimensions 3
|
||
:coord :x
|
||
:content [:y :z]
|
||
3 {:dimensions 2
|
||
:coord :y
|
||
:content [:z]
|
||
4 {:dimensions 1
|
||
:coord :z
|
||
:content :data
|
||
5 "hello"
|
||
}
|
||
}
|
||
}
|
||
</code></pre>
|
||
<h3><a href="#errors-and-error-reporting" name="errors-and-error-reporting"></a>Errors and error-reporting</h3>
|
||
<p>A dynamic variable, <code>*safe-sparse-operations*</code>, is provided to handle behaviour in error conditions. If this is <code>false</code>, bad data will generally not cause an exception to be thrown, and corrupt structures may be returned, thus:</p>
|
||
<pre><code class="clojure">(put (make-sparse-array :x :y :z) "hello" 3) ;; insufficient coordinates specified
|
||
|
||
=> {:dimensions 3, :coord :x, :content (:y :z), 3 {:dimensions 2, :coord :y, :content (:z), nil {:dimensions 1, :coord :z, :content :data, nil nil}}}
|
||
</code></pre>
|
||
<p>However, if <code>*safe-sparse-operations*</code> is bound to <code>true</code>, exceptions will be thrown instead:</p>
|
||
<pre><code class="clojure">(binding [*safe-sparse-operations* true]
|
||
(put (make-sparse-array :x :y :z) "hello" 3))
|
||
|
||
ExceptionInfo Expected 3 coordinates; found 1 clojure.core/ex-info (core.clj:4617)
|
||
</code></pre>
|
||
<p>Sanity checking data is potentially expensive; for this reason <code>*safe-sparse-operations*</code> defaults to <code>false</code>, but you may wish to bind it to <code>true</code> especially while debugging.</p>
|
||
<h3><a href="#dense-arrays" name="dense-arrays"></a>Dense arrays</h3>
|
||
<p>For the purposes of conversion, a <strong>dense array</strong> is assumed to be a vector; a two dimensional dense array a vector of vectors; a three dimensional dense array a vector of vectors of vectors, and so on. For any depth <code>N</code>, all vectors at depth <code>N</code> must have the same arity. If these conventions are not respected conversion may fail.</p>
|
||
<h2><a href="#usage" name="usage"></a>Usage</h2>
|
||
<h3><a href="#make-sparse-array" name="make-sparse-array"></a>make-sparse-array</h3>
|
||
<p><code>sparse-array.core/make-sparse-array ([& dimensions])</code></p>
|
||
<p>Make a sparse array with these <code>dimensions</code>. Every member of <code>dimensions</code> must be a keyword; otherwise, <code>nil</code> will be returned.</p>
|
||
<p>e.g.</p>
|
||
<pre><code class="clojure">(make-sparse-array :x :y :z)
|
||
|
||
=> {:dimensions 3, :coord :x, :content (:y :z)}
|
||
|
||
</code></pre>
|
||
<h3><a href="#sparse-array-" name="sparse-array-"></a>sparse-array?</h3>
|
||
<p><code>sparse-array.core/sparse-array? ([x])</code></p>
|
||
<p><code>true</code> if <code>x</code> is a sparse array conforming to the conventions established by this library, else <code>false</code>.</p>
|
||
<h3><a href="#put" name="put"></a>put</h3>
|
||
<p><code>sparse-array.core/put ([array value & coordinates])</code></p>
|
||
<p>Return a sparse array like this <code>array</code> but with this <code>value</code> at these <code>coordinates</code>. Returns <code>nil</code> if any coordinate is invalid.</p>
|
||
<p>e.g.</p>
|
||
<pre><code class="clojure">(put (put (make-sparse-array :x :y) "hello" 3 4) "goodbye" 4 3)
|
||
|
||
=> {:dimensions 2,
|
||
:coord :x,
|
||
:content (:y),
|
||
3 {:dimensions 1, :coord :y, :content :data, 4 "hello"},
|
||
4 {:dimensions 1, :coord :y, :content :data, 3 "goodbye"}}
|
||
</code></pre>
|
||
<h3><a href="#get" name="get"></a>get</h3>
|
||
<p><code>sparse-array.core/get ([array & coordinates])</code></p>
|
||
<p>Return the value in this sparse <code>array</code> at these <code>coordinates</code>.</p>
|
||
<h3><a href="#merge-sparse-arrays" name="merge-sparse-arrays"></a>merge-sparse-arrays</h3>
|
||
<p><code>sparse-array.core/merge-sparse-arrays ([a1 a2])</code></p>
|
||
<p>Return a sparse array taking values from sparse arrays <code>a1</code> and <code>a2</code>, but preferring values from <code>a2</code> where there is a conflict. <code>a1</code> and <code>a2</code> must have the <strong>same</strong> dimensions in the <strong>same</strong> order, or <code>nil</code> will be returned.</p>
|
||
<p>e.g.</p>
|
||
<pre><code class="clojure">(merge-sparse-arrays
|
||
(put (make-sparse-array :x) "hello" 3)
|
||
(put (make-sparse-array :x) "goodbye" 4)))
|
||
|
||
=> {:dimensions 1, :coord :x, :content :data, 3 "hello", 4 "goodbye"}
|
||
</code></pre>
|
||
<h3><a href="#dense-to-sparse" name="dense-to-sparse"></a>dense-to-sparse</h3>
|
||
<p><code>sparse-array.core/dense-to-sparse ([x] [x coordinates])</code></p>
|
||
<p>Return a sparse array representing the content of the dense array <code>x</code>, assuming these <code>coordinates</code> if specified. <em>NOTE THAT</em> if insufficient values of <code>coordinates</code> are specified, the resulting sparse array will be malformed.</p>
|
||
<p>e.g.</p>
|
||
<pre><code class="clojure">(dense-to-sparse [nil nil nil "hello" nil "goodbye"])
|
||
|
||
=> {:dimensions 1, :coord :i0, :content :data, 3 "hello", 5 "goodbye"}
|
||
</code></pre>
|
||
<h3><a href="#sparse-to-dense" name="sparse-to-dense"></a>sparse-to-dense</h3>
|
||
<p><code>sparse-array.core/sparse-to-dense ([x] [x arity])</code></p>
|
||
<p>Return a dense array representing the content of the sparse array <code>x</code>.</p>
|
||
<p><strong>NOTE THAT</strong> this has the potential to consume very large amounts of memory.</p>
|
||
<p>e.g.</p>
|
||
<pre><code class="clojure">(sparse-to-dense
|
||
(put
|
||
(put
|
||
(make-sparse-array :x :y)
|
||
"hello" 3 4)
|
||
"goodbye" 4 3))
|
||
|
||
=> [[nil nil nil nil nil]
|
||
[nil nil nil nil nil]
|
||
[nil nil nil nil nil]
|
||
[nil nil nil nil "hello"]
|
||
[nil nil nil "goodbye" nil]]
|
||
</code></pre>
|
||
<h3><a href="#extract" name="extract"></a>extract</h3>
|
||
<p>The whole point of working with sparse arrays is because we wish to work with interesting subsets of arrays the entirety of which would be too large to conveniently handle; thus perhaps the most important operation is to be able to extract a sparse subset of an array.</p>
|
||
<p><code>sparse-array.extract/extract ([array function])</code></p>
|
||
<p>Return a sparse subset of this <code>array</code> - which may be either sparse or dense - comprising all those cells for which this <code>function</code> returns a ‘truthy’ value.</p>
|
||
<p>e.g.</p>
|
||
<pre><code class="clojure">(extract [[[1 2 3][:one :two :three]["one" "two" "three"]]
|
||
[[1 :two "three"]["one" 2 :three][:one "two" 3]]
|
||
[[1.0 2.0 3.0][2/2 4/2 6/2]["I" "II" "III"]]]
|
||
#(if
|
||
(number? %)
|
||
(= % 3)
|
||
(= (name %) "three")))
|
||
|
||
=> {:dimensions 3,
|
||
:coord :i0,
|
||
:content (:i1 :i2),
|
||
0
|
||
{:dimensions 2,
|
||
:coord :i1,
|
||
:content (:i2),
|
||
0 {:dimensions 1, :coord :i2, :content :data, 2 3},
|
||
1 {:dimensions 1, :coord :i2, :content :data, 2 :three},
|
||
2 {:dimensions 1, :coord :i2, :content :data, 2 "three"}},
|
||
1
|
||
{:dimensions 2,
|
||
:coord :i1,
|
||
:content (:i2),
|
||
0 {:dimensions 1, :coord :i2, :content :data, 2 "three"},
|
||
1 {:dimensions 1, :coord :i2, :content :data, 2 :three},
|
||
2 {:dimensions 1, :coord :i2, :content :data, 2 3}},
|
||
2
|
||
{:dimensions 2,
|
||
:coord :i1,
|
||
:content (:i2),
|
||
1 {:dimensions 1, :coord :i2, :content :data, 2 3}}}
|
||
</code></pre>
|
||
<h3><a href="#extract-from-dense" name="extract-from-dense"></a>extract-from-dense</h3>
|
||
<p>Note that the above example returns the default axis sequence <code>{i0, i1, i2...}</code>; extracting from a sparse array will always retain the axes of the array extracted from. Dense arrays, obviously, do not have explicit axes.</p>
|
||
<p>You may wish to specify a sequence of axes when extracting from a dense array. A function is provided:</p>
|
||
<p><code>sparse-array.extract/extract-from-dense ([array function] [array function axes])</code></p>
|
||
<p>Return a subset of this dense <code>array</code> comprising all those cells for which this <code>function</code> returns a ‘truthy’ value. Use these <code>axes</code> if provided.</p>
|
||
<p>e.g.</p>
|
||
<pre><code class="clojure">(extract-from-dense
|
||
[[[1 2 3][:one :two :three]["one" "two" "three"]]
|
||
[[1 :two "three"]["one" 2 :three][:one "two" 3]]
|
||
[[1.0 2.0 3.0][2/2 4/2 6/2]["I" "II" "III"]]]
|
||
integer?
|
||
'(:p :q :r))
|
||
|
||
=> {:dimensions 3,
|
||
:coord :p,
|
||
:content (:q :r),
|
||
0
|
||
{:dimensions 2,
|
||
:coord :q,
|
||
:content (:r),
|
||
0 {:dimensions 1, :coord :r, :content :data, 0 1, 1 2, 2 3}},
|
||
1
|
||
{:dimensions 2,
|
||
:coord :q,
|
||
:content (:r),
|
||
0 {:dimensions 1, :coord :r, :content :data, 0 1},
|
||
1 {:dimensions 1, :coord :r, :content :data, 1 2},
|
||
2 {:dimensions 1, :coord :r, :content :data, 2 3}},
|
||
2
|
||
{:dimensions 2,
|
||
:coord :q,
|
||
:content (:r),
|
||
1 {:dimensions 1, :coord :r, :content :data, 0 1, 1 2, 2 3}}}
|
||
</code></pre>
|
||
<h2><a href="#license" name="license"></a>License</h2>
|
||
<p>Copyright © 2019 Simon Brooke</p>
|
||
<p>Distributed under the Eclipse Public License either version 1.0 or (at your option) any later version.</p></div></div></div></body></html> |