Compare commits

...

20 Commits

Author SHA1 Message Date
Chris Watson 36f3aef41e fix specs 2023-03-19 18:09:49 -06:00
Chris Watson 4df520caa1
chg: removed all instances of Vector.method
Converted `Vector.method` calls to `self.class.method` to make extending Vector easier.
2022-01-30 19:30:44 -07:00
Chris Watson d799f8b844 Fix issue with Vector 2021-10-18 13:33:14 -06:00
Chris Watson 707812559d
Merge pull request #5 from wontruefree/updates
use crystal 1.0 and add ameba
2021-08-08 16:05:00 -06:00
Jack Thorne 4ec81a8f01 use crystal 1.0; add ameba 2021-08-05 12:21:34 -05:00
Chris Watson 9ed28220eb
Update shard.yml 2020-05-03 23:28:05 -06:00
Chris Watson 420573592a
Merge pull request #4 from rmarronnier/feature/Add-map-with-index
Adds #map_with_index to Matrix
2019-09-20 20:29:02 -07:00
Rémy Marronnier e4ddcba118 Adds #map_with_index to Matrix 2019-09-21 04:26:30 +02:00
Chris Watson 6247c8d45a Updated workflow name 2019-09-10 19:18:18 -07:00
Chris Watson dacd9b797b
Update README.md 2019-09-10 19:15:45 -07:00
Chris Watson f6593419cb
Update README.md 2019-09-10 19:12:04 -07:00
Chris Watson 0e2fb0cb62
Update crystal.yml 2019-09-10 19:09:16 -07:00
Chris Watson 1e0d809535 Format code 2019-09-10 19:08:28 -07:00
Chris Watson b11731a824 Simplified structure 2019-09-10 19:06:36 -07:00
Chris Watson 9f599230da Update Matrix.zero documentation 2019-09-02 14:20:55 -07:00
Chris Watson c19b317dcd Should fix #2 2019-09-02 14:17:40 -07:00
Chris Watson 34ea337069 Add size method to Matrix 2019-08-23 18:47:47 -07:00
Chris Watson 5088c9ddfa Fixed Matrix#build 2019-08-23 15:05:07 -07:00
Chris Watson 57cdb9f6cc Updated specs. Now working with 0.31.0. 2019-08-23 13:45:07 -07:00
Chris c7bc6f4f5e
Allow Vector.zero to use T as type 2019-07-09 20:02:07 -07:00
32 changed files with 1415 additions and 1352 deletions

20
.github/workflows/crystal.yml vendored Normal file
View File

@ -0,0 +1,20 @@
name: Specs
on: [push]
jobs:
build:
runs-on: ubuntu-latest
container:
image: crystallang/crystal
steps:
- uses: actions/checkout@v1
- name: Install dependencies
run: shards install
- name: Check formatting
run: crystal tool format --check
- name: Run tests
run: crystal spec

1
.tool-versions Normal file
View File

@ -0,0 +1 @@
crystal 1.7.2

View File

@ -1,6 +1,8 @@
# Apatite # Apatite
Apatite is meant to be a collecion of mathmatical and scientific computing algorithms for the Crystal programming language. I don't expect it to ever reach the level of completeness as numpy, but hopefully it can save some people the trouble of implementing these methods on their own. [<img src="https://github.com/watzon/apatite/workflows/Specs/badge.svg">](https://github.com/watzon/apatite/actions)
Apatite is a collection of mathematical and scientific algorithms. Currently it implements the API from Ruby's `Matrix` class for both `Matrix` and `Vector`. This API will be added to as needs arise. The goal is for this project to eventually contain everything you could get from SciPy, but in pure Crystal.
## Installation ## Installation

View File

@ -3,7 +3,7 @@
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="generator" content="Crystal Docs 0.29.0"> <meta name="generator" content="Crystal Docs 0.30.1">
<link href="css/style.css" rel="stylesheet" type="text/css"> <link href="css/style.css" rel="stylesheet" type="text/css">
@ -171,28 +171,28 @@ of Crystal.</p>
<h2>Defined in:</h2> <h2>Defined in:</h2>
<a href="https://github.com/watzon/apatite/blob/fd6da99f9d1de3a90a6315a813fcb7206a053d9a/src/apatite/linear_algebra/error.cr#L1" target="_blank"> <a href="https://github.com/watzon/apatite/blob/c19b317dcdeac142e8bda3c957a21679d1e62a01/src/apatite/linear_algebra/error.cr#L1" target="_blank">
apatite/linear_algebra/error.cr apatite/linear_algebra/error.cr
</a> </a>
<br/> <br/>
<a href="https://github.com/watzon/apatite/blob/fd6da99f9d1de3a90a6315a813fcb7206a053d9a/src/apatite/linear_algebra.cr#L6" target="_blank"> <a href="https://github.com/watzon/apatite/blob/c19b317dcdeac142e8bda3c957a21679d1e62a01/src/apatite/linear_algebra.cr#L6" target="_blank">
apatite/linear_algebra.cr apatite/linear_algebra.cr
</a> </a>
<br/> <br/>
<a href="https://github.com/watzon/apatite/blob/fd6da99f9d1de3a90a6315a813fcb7206a053d9a/src/apatite.cr#L10" target="_blank"> <a href="https://github.com/watzon/apatite/blob/c19b317dcdeac142e8bda3c957a21679d1e62a01/src/apatite.cr#L10" target="_blank">
apatite.cr apatite.cr
</a> </a>
<br/> <br/>
<a href="https://github.com/watzon/apatite/blob/fd6da99f9d1de3a90a6315a813fcb7206a053d9a/src/apatite/version.cr#L1" target="_blank"> <a href="https://github.com/watzon/apatite/blob/c19b317dcdeac142e8bda3c957a21679d1e62a01/src/apatite/version.cr#L1" target="_blank">
apatite/version.cr apatite/version.cr
</a> </a>
@ -230,12 +230,12 @@ of Crystal.</p>
<h3>Instance methods inherited from module <code><a href="Apatite/LinearAlgebra.html">Apatite::LinearAlgebra</a></code></h3> <h3>Instance methods inherited from module <code><a href="Apatite/LinearAlgebra.html">Apatite::LinearAlgebra</a></code></h3>
<a href="Apatite/LinearAlgebra.html#sigmoid%28input%3ANumber%29-instance-method" class="tooltip"> <a href="Apatite/LinearAlgebra.html#sigmoid(input:Number)-instance-method" class="tooltip">
<span>sigmoid(input : Number)</span> <span>sigmoid(input : Number)</span>
sigmoid</a>, sigmoid</a>,
<a href="Apatite/LinearAlgebra.html#sigmoid_d%28input%3ANumber%29-instance-method" class="tooltip"> <a href="Apatite/LinearAlgebra.html#sigmoid_d(input:Number)-instance-method" class="tooltip">
<span>sigmoid_d(input : Number)</span> <span>sigmoid_d(input : Number)</span>
sigmoid_d</a> sigmoid_d</a>

View File

@ -3,7 +3,7 @@
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="generator" content="Crystal Docs 0.29.0"> <meta name="generator" content="Crystal Docs 0.30.1">
<link href="../css/style.css" rel="stylesheet" type="text/css"> <link href="../css/style.css" rel="stylesheet" type="text/css">
@ -163,49 +163,49 @@
<h2>Defined in:</h2> <h2>Defined in:</h2>
<a href="https://github.com/watzon/apatite/blob/fd6da99f9d1de3a90a6315a813fcb7206a053d9a/src/apatite/linear_algebra/error.cr#L2" target="_blank"> <a href="https://github.com/watzon/apatite/blob/c19b317dcdeac142e8bda3c957a21679d1e62a01/src/apatite/linear_algebra/error.cr#L2" target="_blank">
apatite/linear_algebra/error.cr apatite/linear_algebra/error.cr
</a> </a>
<br/> <br/>
<a href="https://github.com/watzon/apatite/blob/fd6da99f9d1de3a90a6315a813fcb7206a053d9a/src/apatite/linear_algebra/vector.cr#L4" target="_blank"> <a href="https://github.com/watzon/apatite/blob/c19b317dcdeac142e8bda3c957a21679d1e62a01/src/apatite/linear_algebra/vector.cr#L5" target="_blank">
apatite/linear_algebra/vector.cr apatite/linear_algebra/vector.cr
</a> </a>
<br/> <br/>
<a href="https://github.com/watzon/apatite/blob/fd6da99f9d1de3a90a6315a813fcb7206a053d9a/src/apatite/linear_algebra/matrix/eigenvalue_decomposition.cr#L1" target="_blank"> <a href="https://github.com/watzon/apatite/blob/c19b317dcdeac142e8bda3c957a21679d1e62a01/src/apatite/linear_algebra/matrix/eigenvalue_decomposition.cr#L1" target="_blank">
apatite/linear_algebra/matrix/eigenvalue_decomposition.cr apatite/linear_algebra/matrix/eigenvalue_decomposition.cr
</a> </a>
<br/> <br/>
<a href="https://github.com/watzon/apatite/blob/fd6da99f9d1de3a90a6315a813fcb7206a053d9a/src/apatite/linear_algebra/matrix/lup_decomposition.cr#L1" target="_blank"> <a href="https://github.com/watzon/apatite/blob/c19b317dcdeac142e8bda3c957a21679d1e62a01/src/apatite/linear_algebra/matrix/lup_decomposition.cr#L1" target="_blank">
apatite/linear_algebra/matrix/lup_decomposition.cr apatite/linear_algebra/matrix/lup_decomposition.cr
</a> </a>
<br/> <br/>
<a href="https://github.com/watzon/apatite/blob/fd6da99f9d1de3a90a6315a813fcb7206a053d9a/src/apatite/linear_algebra/matrix.cr#L5" target="_blank"> <a href="https://github.com/watzon/apatite/blob/c19b317dcdeac142e8bda3c957a21679d1e62a01/src/apatite/linear_algebra/matrix.cr#L5" target="_blank">
apatite/linear_algebra/matrix.cr apatite/linear_algebra/matrix.cr
</a> </a>
<br/> <br/>
<a href="https://github.com/watzon/apatite/blob/fd6da99f9d1de3a90a6315a813fcb7206a053d9a/src/apatite/linear_algebra.cr#L7" target="_blank"> <a href="https://github.com/watzon/apatite/blob/c19b317dcdeac142e8bda3c957a21679d1e62a01/src/apatite/linear_algebra.cr#L7" target="_blank">
apatite/linear_algebra.cr apatite/linear_algebra.cr
</a> </a>
<br/> <br/>
<a href="https://github.com/watzon/apatite/blob/fd6da99f9d1de3a90a6315a813fcb7206a053d9a/src/apatite/linear_algebra/ndarray.cr#L1" target="_blank"> <a href="https://github.com/watzon/apatite/blob/c19b317dcdeac142e8bda3c957a21679d1e62a01/src/apatite/linear_algebra/ndarray.cr#L1" target="_blank">
apatite/linear_algebra/ndarray.cr apatite/linear_algebra/ndarray.cr
</a> </a>
@ -224,14 +224,14 @@
<ul class="list-summary"> <ul class="list-summary">
<li class="entry-summary"> <li class="entry-summary">
<a href="#sigmoid%28input%3ANumber%29-instance-method" class="signature"><strong>#sigmoid</strong>(input : Number)</a> <a href="#sigmoid(input:Number)-instance-method" class="signature"><strong>#sigmoid</strong>(input : Number)</a>
<div class="summary"><p>Calculates the sigmoid curve for a numeric input.</p></div> <div class="summary"><p>Calculates the sigmoid curve for a numeric input.</p></div>
</li> </li>
<li class="entry-summary"> <li class="entry-summary">
<a href="#sigmoid_d%28input%3ANumber%29-instance-method" class="signature"><strong>#sigmoid_d</strong>(input : Number)</a> <a href="#sigmoid_d(input:Number)-instance-method" class="signature"><strong>#sigmoid_d</strong>(input : Number)</a>
<div class="summary"><p>Calculates the derivative sigmoid curve for a numeric input.</p></div> <div class="summary"><p>Calculates the derivative sigmoid curve for a numeric input.</p></div>
@ -259,7 +259,7 @@
def <strong>sigmoid</strong>(input : Number) def <strong>sigmoid</strong>(input : Number)
<a class="method-permalink" href="#sigmoid%28input%3ANumber%29-instance-method">#</a> <a class="method-permalink" href="#sigmoid(input:Number)-instance-method">#</a>
</div> </div>
<div class="doc"><p>Calculates the sigmoid curve for a numeric input.</p> <div class="doc"><p>Calculates the sigmoid curve for a numeric input.</p>
@ -271,7 +271,7 @@
<br/> <br/>
<div> <div>
[<a href="https://github.com/watzon/apatite/blob/fd6da99f9d1de3a90a6315a813fcb7206a053d9a/src/apatite/linear_algebra.cr#L15" target="_blank">View source</a>] [<a href="https://github.com/watzon/apatite/blob/c19b317dcdeac142e8bda3c957a21679d1e62a01/src/apatite/linear_algebra.cr#L15" target="_blank">View source</a>]
</div> </div>
</div> </div>
@ -281,7 +281,7 @@
def <strong>sigmoid_d</strong>(input : Number) def <strong>sigmoid_d</strong>(input : Number)
<a class="method-permalink" href="#sigmoid_d%28input%3ANumber%29-instance-method">#</a> <a class="method-permalink" href="#sigmoid_d(input:Number)-instance-method">#</a>
</div> </div>
<div class="doc"><p>Calculates the derivative sigmoid curve for a numeric input.</p> <div class="doc"><p>Calculates the derivative sigmoid curve for a numeric input.</p>
@ -291,7 +291,7 @@
<br/> <br/>
<div> <div>
[<a href="https://github.com/watzon/apatite/blob/fd6da99f9d1de3a90a6315a813fcb7206a053d9a/src/apatite/linear_algebra.cr#L23" target="_blank">View source</a>] [<a href="https://github.com/watzon/apatite/blob/c19b317dcdeac142e8bda3c957a21679d1e62a01/src/apatite/linear_algebra.cr#L23" target="_blank">View source</a>]
</div> </div>
</div> </div>

View File

@ -3,7 +3,7 @@
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="generator" content="Crystal Docs 0.29.0"> <meta name="generator" content="Crystal Docs 0.30.1">
<link href="../../css/style.css" rel="stylesheet" type="text/css"> <link href="../../css/style.css" rel="stylesheet" type="text/css">
@ -151,7 +151,7 @@
<h2>Defined in:</h2> <h2>Defined in:</h2>
<a href="https://github.com/watzon/apatite/blob/fd6da99f9d1de3a90a6315a813fcb7206a053d9a/src/apatite/linear_algebra/error.cr#L4" target="_blank"> <a href="https://github.com/watzon/apatite/blob/c19b317dcdeac142e8bda3c957a21679d1e62a01/src/apatite/linear_algebra/error.cr#L4" target="_blank">
apatite/linear_algebra/error.cr apatite/linear_algebra/error.cr
</a> </a>

View File

@ -3,7 +3,7 @@
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="generator" content="Crystal Docs 0.29.0"> <meta name="generator" content="Crystal Docs 0.30.1">
<link href="../../css/style.css" rel="stylesheet" type="text/css"> <link href="../../css/style.css" rel="stylesheet" type="text/css">
@ -151,7 +151,7 @@
<h2>Defined in:</h2> <h2>Defined in:</h2>
<a href="https://github.com/watzon/apatite/blob/fd6da99f9d1de3a90a6315a813fcb7206a053d9a/src/apatite/linear_algebra/error.cr#L6" target="_blank"> <a href="https://github.com/watzon/apatite/blob/c19b317dcdeac142e8bda3c957a21679d1e62a01/src/apatite/linear_algebra/error.cr#L6" target="_blank">
apatite/linear_algebra/error.cr apatite/linear_algebra/error.cr
</a> </a>

View File

@ -3,7 +3,7 @@
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="generator" content="Crystal Docs 0.29.0"> <meta name="generator" content="Crystal Docs 0.30.1">
<link href="../../css/style.css" rel="stylesheet" type="text/css"> <link href="../../css/style.css" rel="stylesheet" type="text/css">
@ -151,7 +151,7 @@
<h2>Defined in:</h2> <h2>Defined in:</h2>
<a href="https://github.com/watzon/apatite/blob/fd6da99f9d1de3a90a6315a813fcb7206a053d9a/src/apatite/linear_algebra/error.cr#L8" target="_blank"> <a href="https://github.com/watzon/apatite/blob/c19b317dcdeac142e8bda3c957a21679d1e62a01/src/apatite/linear_algebra/error.cr#L7" target="_blank">
apatite/linear_algebra/error.cr apatite/linear_algebra/error.cr
</a> </a>
@ -162,16 +162,6 @@
<h2>Constructors</h2>
<ul class="list-summary">
<li class="entry-summary">
<a href="#new%28method%2Cthis%2Cother%29-class-method" class="signature"><strong>.new</strong>(method, this, other)</a>
</li>
</ul>
@ -225,25 +215,6 @@
</div> </div>
<h2>Constructor Detail</h2>
<div class="entry-detail" id="new(method,this,other)-class-method">
<div class="signature">
def self.<strong>new</strong>(method, this, other)
<a class="method-permalink" href="#new%28method%2Cthis%2Cother%29-class-method">#</a>
</div>
<br/>
<div>
[<a href="https://github.com/watzon/apatite/blob/fd6da99f9d1de3a90a6315a813fcb7206a053d9a/src/apatite/linear_algebra/error.cr#L9" target="_blank">View source</a>]
</div>
</div>

View File

@ -3,7 +3,7 @@
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="generator" content="Crystal Docs 0.29.0"> <meta name="generator" content="Crystal Docs 0.30.1">
<link href="../../css/style.css" rel="stylesheet" type="text/css"> <link href="../../css/style.css" rel="stylesheet" type="text/css">
@ -164,7 +164,7 @@
<h2>Defined in:</h2> <h2>Defined in:</h2>
<a href="https://github.com/watzon/apatite/blob/fd6da99f9d1de3a90a6315a813fcb7206a053d9a/src/apatite/linear_algebra/error.cr#L3" target="_blank"> <a href="https://github.com/watzon/apatite/blob/c19b317dcdeac142e8bda3c957a21679d1e62a01/src/apatite/linear_algebra/error.cr#L3" target="_blank">
apatite/linear_algebra/error.cr apatite/linear_algebra/error.cr
</a> </a>

File diff suppressed because it is too large Load Diff

View File

@ -3,7 +3,7 @@
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="generator" content="Crystal Docs 0.29.0"> <meta name="generator" content="Crystal Docs 0.30.1">
<link href="../../../css/style.css" rel="stylesheet" type="text/css"> <link href="../../../css/style.css" rel="stylesheet" type="text/css">
@ -163,7 +163,7 @@ equal to the eigenvalues and V is formed by the eigenvectors.</p>
<h2>Defined in:</h2> <h2>Defined in:</h2>
<a href="https://github.com/watzon/apatite/blob/fd6da99f9d1de3a90a6315a813fcb7206a053d9a/src/apatite/linear_algebra/matrix/eigenvalue_decomposition.cr#L12" target="_blank"> <a href="https://github.com/watzon/apatite/blob/c19b317dcdeac142e8bda3c957a21679d1e62a01/src/apatite/linear_algebra/matrix/eigenvalue_decomposition.cr#L12" target="_blank">
apatite/linear_algebra/matrix/eigenvalue_decomposition.cr apatite/linear_algebra/matrix/eigenvalue_decomposition.cr
</a> </a>
@ -178,7 +178,7 @@ equal to the eigenvalues and V is formed by the eigenvectors.</p>
<ul class="list-summary"> <ul class="list-summary">
<li class="entry-summary"> <li class="entry-summary">
<a href="#new%28matrix%29-class-method" class="signature"><strong>.new</strong>(matrix)</a> <a href="#new(matrix)-class-method" class="signature"><strong>.new</strong>(matrix)</a>
</li> </li>
@ -224,13 +224,13 @@ equal to the eigenvalues and V is formed by the eigenvectors.</p>
def self.<strong>new</strong>(matrix) def self.<strong>new</strong>(matrix)
<a class="method-permalink" href="#new%28matrix%29-class-method">#</a> <a class="method-permalink" href="#new(matrix)-class-method">#</a>
</div> </div>
<br/> <br/>
<div> <div>
[<a href="https://github.com/watzon/apatite/blob/fd6da99f9d1de3a90a6315a813fcb7206a053d9a/src/apatite/linear_algebra/matrix/eigenvalue_decomposition.cr#L13" target="_blank">View source</a>] [<a href="https://github.com/watzon/apatite/blob/c19b317dcdeac142e8bda3c957a21679d1e62a01/src/apatite/linear_algebra/matrix/eigenvalue_decomposition.cr#L13" target="_blank">View source</a>]
</div> </div>
</div> </div>

View File

@ -3,7 +3,7 @@
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="generator" content="Crystal Docs 0.29.0"> <meta name="generator" content="Crystal Docs 0.30.1">
<link href="../../../css/style.css" rel="stylesheet" type="text/css"> <link href="../../../css/style.css" rel="stylesheet" type="text/css">
@ -163,7 +163,7 @@ linear equations. This will fail if singular? returns true.</p>
<h2>Defined in:</h2> <h2>Defined in:</h2>
<a href="https://github.com/watzon/apatite/blob/fd6da99f9d1de3a90a6315a813fcb7206a053d9a/src/apatite/linear_algebra/matrix/lup_decomposition.cr#L12" target="_blank"> <a href="https://github.com/watzon/apatite/blob/c19b317dcdeac142e8bda3c957a21679d1e62a01/src/apatite/linear_algebra/matrix/lup_decomposition.cr#L12" target="_blank">
apatite/linear_algebra/matrix/lup_decomposition.cr apatite/linear_algebra/matrix/lup_decomposition.cr
</a> </a>
@ -178,7 +178,7 @@ linear equations. This will fail if singular? returns true.</p>
<ul class="list-summary"> <ul class="list-summary">
<li class="entry-summary"> <li class="entry-summary">
<a href="#new%28matrix%29-class-method" class="signature"><strong>.new</strong>(matrix)</a> <a href="#new(matrix)-class-method" class="signature"><strong>.new</strong>(matrix)</a>
</li> </li>
@ -224,13 +224,13 @@ linear equations. This will fail if singular? returns true.</p>
def self.<strong>new</strong>(matrix) def self.<strong>new</strong>(matrix)
<a class="method-permalink" href="#new%28matrix%29-class-method">#</a> <a class="method-permalink" href="#new(matrix)-class-method">#</a>
</div> </div>
<br/> <br/>
<div> <div>
[<a href="https://github.com/watzon/apatite/blob/fd6da99f9d1de3a90a6315a813fcb7206a053d9a/src/apatite/linear_algebra/matrix/lup_decomposition.cr#L13" target="_blank">View source</a>] [<a href="https://github.com/watzon/apatite/blob/c19b317dcdeac142e8bda3c957a21679d1e62a01/src/apatite/linear_algebra/matrix/lup_decomposition.cr#L13" target="_blank">View source</a>]
</div> </div>
</div> </div>

View File

@ -3,7 +3,7 @@
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="generator" content="Crystal Docs 0.29.0"> <meta name="generator" content="Crystal Docs 0.30.1">
<link href="../../css/style.css" rel="stylesheet" type="text/css"> <link href="../../css/style.css" rel="stylesheet" type="text/css">
@ -139,17 +139,6 @@
<h2>Included Modules</h2>
<ul class="other-types-list">
<li class="other-type">Comparable(<a href="../../Apatite/LinearAlgebra/NDArray.html">Apatite::LinearAlgebra::NDArray</a>)</li>
<li class="other-type">Enumerable(Float64)</li>
<li class="other-type">Indexable(Float64)</li>
</ul>
@ -162,7 +151,7 @@
<h2>Defined in:</h2> <h2>Defined in:</h2>
<a href="https://github.com/watzon/apatite/blob/fd6da99f9d1de3a90a6315a813fcb7206a053d9a/src/apatite/linear_algebra/ndarray.cr#L2" target="_blank"> <a href="https://github.com/watzon/apatite/blob/c19b317dcdeac142e8bda3c957a21679d1e62a01/src/apatite/linear_algebra/ndarray.cr#L2" target="_blank">
apatite/linear_algebra/ndarray.cr apatite/linear_algebra/ndarray.cr
</a> </a>
@ -173,105 +162,11 @@
<h2>Constructors</h2>
<ul class="list-summary">
<li class="entry-summary">
<a href="#new%28data%3AArray%28Number%29%2Cshape%3AArray%28Int32%29%3F%3Dnil%29-class-method" class="signature"><strong>.new</strong>(data : Array(Number), shape : Array(Int32)? = <span class="n">nil</span>)</a>
</li>
</ul>
<h2>Instance Method Summary</h2>
<ul class="list-summary">
<li class="entry-summary">
<a href="#%5B%5D%28%2Aargs%2C%2A%2Aoptions%29-instance-method" class="signature"><strong>#[]</strong>(*args, **options)</a>
</li>
<li class="entry-summary">
<a href="#%5B%5D%28%2Aargs%2C%2A%2Aoptions%2C%26block%29-instance-method" class="signature"><strong>#[]</strong>(*args, **options, &block)</a>
</li>
<li class="entry-summary">
<a href="#%5B%5D%3D%28%2Aargs%2C%2A%2Aoptions%29-instance-method" class="signature"><strong>#[]=</strong>(*args, **options)</a>
</li>
<li class="entry-summary">
<a href="#%5B%5D%3F%28%2Aargs%2C%2A%2Aoptions%29-instance-method" class="signature"><strong>#[]?</strong>(*args, **options)</a>
</li>
<li class="entry-summary">
<a href="#%5B%5D%3F%28%2Aargs%2C%2A%2Aoptions%2C%26block%29-instance-method" class="signature"><strong>#[]?</strong>(*args, **options, &block)</a>
</li>
<li class="entry-summary">
<a href="#abs-instance-method" class="signature"><strong>#abs</strong></a>
<div class="summary"><p>Returns the absolute value of every item in the array</p></div>
</li>
<li class="entry-summary">
<a href="#acos-instance-method" class="signature"><strong>#acos</strong></a>
<div class="summary"><p>Returns the arccosine of each element in the current array.</p></div>
</li>
<li class="entry-summary">
<a href="#data%3AArray%28Float64%29-instance-method" class="signature"><strong>#data</strong> : Array(Float64)</a>
</li>
<li class="entry-summary">
<a href="#shape%3AArray%28Int32%29-instance-method" class="signature"><strong>#shape</strong> : Array(Int32)</a>
</li>
<li class="entry-summary">
<a href="#size%28%2Aargs%2C%2A%2Aoptions%29-instance-method" class="signature"><strong>#size</strong>(*args, **options)</a>
</li>
<li class="entry-summary">
<a href="#size%28%2Aargs%2C%2A%2Aoptions%2C%26block%29-instance-method" class="signature"><strong>#size</strong>(*args, **options, &block)</a>
</li>
<li class="entry-summary">
<a href="#to_unsafe%28%2Aargs%2C%2A%2Aoptions%29-instance-method" class="signature"><strong>#to_unsafe</strong>(*args, **options)</a>
</li>
<li class="entry-summary">
<a href="#to_unsafe%28%2Aargs%2C%2A%2Aoptions%2C%26block%29-instance-method" class="signature"><strong>#to_unsafe</strong>(*args, **options, &block)</a>
</li>
<li class="entry-summary">
<a href="#unsafe_fetch%28%2Aargs%2C%2A%2Aoptions%29-instance-method" class="signature"><strong>#unsafe_fetch</strong>(*args, **options)</a>
</li>
<li class="entry-summary">
<a href="#unsafe_fetch%28%2Aargs%2C%2A%2Aoptions%2C%26block%29-instance-method" class="signature"><strong>#unsafe_fetch</strong>(*args, **options, &block)</a>
</li>
</ul>
@ -296,328 +191,12 @@
</div> </div>
<h2>Constructor Detail</h2>
<div class="entry-detail" id="new(data:Array(Number),shape:Array(Int32)?=nil)-class-method">
<div class="signature">
def self.<strong>new</strong>(data : <a href="../../Array.html">Array</a>(Number), shape : <a href="../../Array.html">Array</a>(Int32)? = <span class="n">nil</span>)
<a class="method-permalink" href="#new%28data%3AArray%28Number%29%2Cshape%3AArray%28Int32%29%3F%3Dnil%29-class-method">#</a>
</div>
<br/>
<div>
[<a href="https://github.com/watzon/apatite/blob/fd6da99f9d1de3a90a6315a813fcb7206a053d9a/src/apatite/linear_algebra/ndarray.cr#L18" target="_blank">View source</a>]
</div>
</div>
<h2>Instance Method Detail</h2>
<div class="entry-detail" id="[](*args,**options)-instance-method">
<div class="signature">
def <strong>[]</strong>(*args, **options)
<a class="method-permalink" href="#%5B%5D%28%2Aargs%2C%2A%2Aoptions%29-instance-method">#</a>
</div>
<br/>
<div>
[<a href="https://github.com/watzon/apatite/blob/fd6da99f9d1de3a90a6315a813fcb7206a053d9a/src/apatite/linear_algebra/ndarray.cr#L11" target="_blank">View source</a>]
</div>
</div>
<div class="entry-detail" id="[](*args,**options,&amp;block)-instance-method">
<div class="signature">
def <strong>[]</strong>(*args, **options, &block)
<a class="method-permalink" href="#%5B%5D%28%2Aargs%2C%2A%2Aoptions%2C%26block%29-instance-method">#</a>
</div>
<br/>
<div>
[<a href="https://github.com/watzon/apatite/blob/fd6da99f9d1de3a90a6315a813fcb7206a053d9a/src/apatite/linear_algebra/ndarray.cr#L11" target="_blank">View source</a>]
</div>
</div>
<div class="entry-detail" id="[]=(*args,**options)-instance-method">
<div class="signature">
def <strong>[]=</strong>(*args, **options)
<a class="method-permalink" href="#%5B%5D%3D%28%2Aargs%2C%2A%2Aoptions%29-instance-method">#</a>
</div>
<br/>
<div>
[<a href="https://github.com/watzon/apatite/blob/fd6da99f9d1de3a90a6315a813fcb7206a053d9a/src/apatite/linear_algebra/ndarray.cr#L13" target="_blank">View source</a>]
</div>
</div>
<div class="entry-detail" id="[]?(*args,**options)-instance-method">
<div class="signature">
def <strong>[]?</strong>(*args, **options)
<a class="method-permalink" href="#%5B%5D%3F%28%2Aargs%2C%2A%2Aoptions%29-instance-method">#</a>
</div>
<br/>
<div>
[<a href="https://github.com/watzon/apatite/blob/fd6da99f9d1de3a90a6315a813fcb7206a053d9a/src/apatite/linear_algebra/ndarray.cr#L12" target="_blank">View source</a>]
</div>
</div>
<div class="entry-detail" id="[]?(*args,**options,&amp;block)-instance-method">
<div class="signature">
def <strong>[]?</strong>(*args, **options, &block)
<a class="method-permalink" href="#%5B%5D%3F%28%2Aargs%2C%2A%2Aoptions%2C%26block%29-instance-method">#</a>
</div>
<br/>
<div>
[<a href="https://github.com/watzon/apatite/blob/fd6da99f9d1de3a90a6315a813fcb7206a053d9a/src/apatite/linear_algebra/ndarray.cr#L12" target="_blank">View source</a>]
</div>
</div>
<div class="entry-detail" id="abs-instance-method">
<div class="signature">
def <strong>abs</strong>
<a class="method-permalink" href="#abs-instance-method">#</a>
</div>
<div class="doc"><p>Returns the absolute value of every item in the array</p></div>
<br/>
<div>
[<a href="https://github.com/watzon/apatite/blob/fd6da99f9d1de3a90a6315a813fcb7206a053d9a/src/apatite/linear_algebra/ndarray.cr#L24" target="_blank">View source</a>]
</div>
</div>
<div class="entry-detail" id="acos-instance-method">
<div class="signature">
def <strong>acos</strong>
<a class="method-permalink" href="#acos-instance-method">#</a>
</div>
<div class="doc"><p>Returns the arccosine of each element in the current array.</p></div>
<br/>
<div>
[<a href="https://github.com/watzon/apatite/blob/fd6da99f9d1de3a90a6315a813fcb7206a053d9a/src/apatite/linear_algebra/ndarray.cr#L29" target="_blank">View source</a>]
</div>
</div>
<div class="entry-detail" id="data:Array(Float64)-instance-method">
<div class="signature">
def <strong>data</strong> : <a href="../../Array.html">Array</a>(Float64)
<a class="method-permalink" href="#data%3AArray%28Float64%29-instance-method">#</a>
</div>
<br/>
<div>
[<a href="https://github.com/watzon/apatite/blob/fd6da99f9d1de3a90a6315a813fcb7206a053d9a/src/apatite/linear_algebra/ndarray.cr#L9" target="_blank">View source</a>]
</div>
</div>
<div class="entry-detail" id="shape:Array(Int32)-instance-method">
<div class="signature">
def <strong>shape</strong> : <a href="../../Array.html">Array</a>(Int32)
<a class="method-permalink" href="#shape%3AArray%28Int32%29-instance-method">#</a>
</div>
<br/>
<div>
[<a href="https://github.com/watzon/apatite/blob/fd6da99f9d1de3a90a6315a813fcb7206a053d9a/src/apatite/linear_algebra/ndarray.cr#L11" target="_blank">View source</a>]
</div>
</div>
<div class="entry-detail" id="size(*args,**options)-instance-method">
<div class="signature">
def <strong>size</strong>(*args, **options)
<a class="method-permalink" href="#size%28%2Aargs%2C%2A%2Aoptions%29-instance-method">#</a>
</div>
<br/>
<div>
[<a href="https://github.com/watzon/apatite/blob/fd6da99f9d1de3a90a6315a813fcb7206a053d9a/src/apatite/linear_algebra/ndarray.cr#L16" target="_blank">View source</a>]
</div>
</div>
<div class="entry-detail" id="size(*args,**options,&amp;block)-instance-method">
<div class="signature">
def <strong>size</strong>(*args, **options, &block)
<a class="method-permalink" href="#size%28%2Aargs%2C%2A%2Aoptions%2C%26block%29-instance-method">#</a>
</div>
<br/>
<div>
[<a href="https://github.com/watzon/apatite/blob/fd6da99f9d1de3a90a6315a813fcb7206a053d9a/src/apatite/linear_algebra/ndarray.cr#L16" target="_blank">View source</a>]
</div>
</div>
<div class="entry-detail" id="to_unsafe(*args,**options)-instance-method">
<div class="signature">
def <strong>to_unsafe</strong>(*args, **options)
<a class="method-permalink" href="#to_unsafe%28%2Aargs%2C%2A%2Aoptions%29-instance-method">#</a>
</div>
<br/>
<div>
[<a href="https://github.com/watzon/apatite/blob/fd6da99f9d1de3a90a6315a813fcb7206a053d9a/src/apatite/linear_algebra/ndarray.cr#L15" target="_blank">View source</a>]
</div>
</div>
<div class="entry-detail" id="to_unsafe(*args,**options,&amp;block)-instance-method">
<div class="signature">
def <strong>to_unsafe</strong>(*args, **options, &block)
<a class="method-permalink" href="#to_unsafe%28%2Aargs%2C%2A%2Aoptions%2C%26block%29-instance-method">#</a>
</div>
<br/>
<div>
[<a href="https://github.com/watzon/apatite/blob/fd6da99f9d1de3a90a6315a813fcb7206a053d9a/src/apatite/linear_algebra/ndarray.cr#L15" target="_blank">View source</a>]
</div>
</div>
<div class="entry-detail" id="unsafe_fetch(*args,**options)-instance-method">
<div class="signature">
def <strong>unsafe_fetch</strong>(*args, **options)
<a class="method-permalink" href="#unsafe_fetch%28%2Aargs%2C%2A%2Aoptions%29-instance-method">#</a>
</div>
<br/>
<div>
[<a href="https://github.com/watzon/apatite/blob/fd6da99f9d1de3a90a6315a813fcb7206a053d9a/src/apatite/linear_algebra/ndarray.cr#L14" target="_blank">View source</a>]
</div>
</div>
<div class="entry-detail" id="unsafe_fetch(*args,**options,&amp;block)-instance-method">
<div class="signature">
def <strong>unsafe_fetch</strong>(*args, **options, &block)
<a class="method-permalink" href="#unsafe_fetch%28%2Aargs%2C%2A%2Aoptions%2C%26block%29-instance-method">#</a>
</div>
<br/>
<div>
[<a href="https://github.com/watzon/apatite/blob/fd6da99f9d1de3a90a6315a813fcb7206a053d9a/src/apatite/linear_algebra/ndarray.cr#L14" target="_blank">View source</a>]
</div>
</div>

File diff suppressed because it is too large Load Diff

View File

@ -3,7 +3,7 @@
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="generator" content="Crystal Docs 0.29.0"> <meta name="generator" content="Crystal Docs 0.30.1">
<link href="../../css/style.css" rel="stylesheet" type="text/css"> <link href="../../css/style.css" rel="stylesheet" type="text/css">
@ -151,7 +151,7 @@
<h2>Defined in:</h2> <h2>Defined in:</h2>
<a href="https://github.com/watzon/apatite/blob/fd6da99f9d1de3a90a6315a813fcb7206a053d9a/src/apatite/linear_algebra/error.cr#L5" target="_blank"> <a href="https://github.com/watzon/apatite/blob/c19b317dcdeac142e8bda3c957a21679d1e62a01/src/apatite/linear_algebra/error.cr#L5" target="_blank">
apatite/linear_algebra/error.cr apatite/linear_algebra/error.cr
</a> </a>

View File

@ -3,7 +3,7 @@
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="generator" content="Crystal Docs 0.29.0"> <meta name="generator" content="Crystal Docs 0.30.1">
<link href="css/style.css" rel="stylesheet" type="text/css"> <link href="css/style.css" rel="stylesheet" type="text/css">
@ -200,7 +200,7 @@ set <span class="o">&lt;&lt;</span> <span class="n">3</span></code></pre>
<h2>Defined in:</h2> <h2>Defined in:</h2>
<a href="https://github.com/watzon/apatite/blob/fd6da99f9d1de3a90a6315a813fcb7206a053d9a/src/apatite/core_ext/array.cr#L1" target="_blank"> <a href="https://github.com/watzon/apatite/blob/c19b317dcdeac142e8bda3c957a21679d1e62a01/src/apatite/core_ext/array.cr#L1" target="_blank">
apatite/core_ext/array.cr apatite/core_ext/array.cr
</a> </a>
@ -219,14 +219,14 @@ set <span class="o">&lt;&lt;</span> <span class="n">3</span></code></pre>
<ul class="list-summary"> <ul class="list-summary">
<li class="entry-summary"> <li class="entry-summary">
<a href="#all%3F-instance-method" class="signature"><strong>#all?</strong></a> <a href="#all?-instance-method" class="signature"><strong>#all?</strong></a>
<div class="summary"><p>Tests whether all elements evaluate to true</p></div> <div class="summary"><p>Tests whether all elements evaluate to true</p></div>
</li> </li>
<li class="entry-summary"> <li class="entry-summary">
<a href="#any%3F-instance-method" class="signature"><strong>#any?</strong></a> <a href="#any?-instance-method" class="signature"><strong>#any?</strong></a>
<div class="summary"><p>Tests whether any of the elements evaluate to true</p></div> <div class="summary"><p>Tests whether any of the elements evaluate to true</p></div>
@ -326,7 +326,7 @@ set <span class="o">&lt;&lt;</span> <span class="n">3</span></code></pre>
def <strong>all?</strong> def <strong>all?</strong>
<a class="method-permalink" href="#all%3F-instance-method">#</a> <a class="method-permalink" href="#all?-instance-method">#</a>
</div> </div>
<div class="doc"><p>Tests whether all elements evaluate to true</p></div> <div class="doc"><p>Tests whether all elements evaluate to true</p></div>
@ -334,7 +334,7 @@ set <span class="o">&lt;&lt;</span> <span class="n">3</span></code></pre>
<br/> <br/>
<div> <div>
[<a href="https://github.com/watzon/apatite/blob/fd6da99f9d1de3a90a6315a813fcb7206a053d9a/src/apatite/core_ext/array.cr#L3" target="_blank">View source</a>] [<a href="https://github.com/watzon/apatite/blob/c19b317dcdeac142e8bda3c957a21679d1e62a01/src/apatite/core_ext/array.cr#L3" target="_blank">View source</a>]
</div> </div>
</div> </div>
@ -344,7 +344,7 @@ set <span class="o">&lt;&lt;</span> <span class="n">3</span></code></pre>
def <strong>any?</strong> def <strong>any?</strong>
<a class="method-permalink" href="#any%3F-instance-method">#</a> <a class="method-permalink" href="#any?-instance-method">#</a>
</div> </div>
<div class="doc"><p>Tests whether any of the elements evaluate to true</p></div> <div class="doc"><p>Tests whether any of the elements evaluate to true</p></div>
@ -352,7 +352,7 @@ set <span class="o">&lt;&lt;</span> <span class="n">3</span></code></pre>
<br/> <br/>
<div> <div>
[<a href="https://github.com/watzon/apatite/blob/fd6da99f9d1de3a90a6315a813fcb7206a053d9a/src/apatite/core_ext/array.cr#L11" target="_blank">View source</a>] [<a href="https://github.com/watzon/apatite/blob/c19b317dcdeac142e8bda3c957a21679d1e62a01/src/apatite/core_ext/array.cr#L11" target="_blank">View source</a>]
</div> </div>
</div> </div>
@ -370,7 +370,7 @@ set <span class="o">&lt;&lt;</span> <span class="n">3</span></code></pre>
<br/> <br/>
<div> <div>
[<a href="https://github.com/watzon/apatite/blob/fd6da99f9d1de3a90a6315a813fcb7206a053d9a/src/apatite/core_ext/array.cr#L19" target="_blank">View source</a>] [<a href="https://github.com/watzon/apatite/blob/c19b317dcdeac142e8bda3c957a21679d1e62a01/src/apatite/core_ext/array.cr#L19" target="_blank">View source</a>]
</div> </div>
</div> </div>
@ -386,7 +386,7 @@ set <span class="o">&lt;&lt;</span> <span class="n">3</span></code></pre>
<br/> <br/>
<div> <div>
[<a href="https://github.com/watzon/apatite/blob/fd6da99f9d1de3a90a6315a813fcb7206a053d9a/src/apatite/core_ext/array.cr#L24" target="_blank">View source</a>] [<a href="https://github.com/watzon/apatite/blob/c19b317dcdeac142e8bda3c957a21679d1e62a01/src/apatite/core_ext/array.cr#L24" target="_blank">View source</a>]
</div> </div>
</div> </div>

View File

@ -3,7 +3,7 @@
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="generator" content="Crystal Docs 0.29.0"> <meta name="generator" content="Crystal Docs 0.30.1">
<link href="css/style.css" rel="stylesheet" type="text/css"> <link href="css/style.css" rel="stylesheet" type="text/css">
@ -125,7 +125,7 @@
<div class="main-content"> <div class="main-content">
<h1>Apatite</h1> <h1>Apatite</h1>
<p>Apatite is meant to be a collecion of mathmatical and scientific computing algorithms for the Crystal programming language. I don't expect it to ever reach the level of completeness as numpy, but hopefully it can save some people the trouble of implementing these methods on their own.</p> <p>Apatite is a collection of mathematical and scientific algorithms. Currently it implements the API from Ruby's <code>Matrix</code> class for both <code>Matrix</code> and <code>Vector</code>. This API will be added to as needs arise. The goal is for this project to eventually contain everything you could get from SciPy, but in pure Crystal.</p>
<h2>Installation</h2> <h2>Installation</h2>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -2,8 +2,13 @@ name: apatite
version: 0.1.0 version: 0.1.0
authors: authors:
- Chris Watson <chris@watzon.me> - Chris Watson <chris@watzon.tech>
crystal: 0.28.0 crystal: "> 0.28.0"
development_dependencies:
ameba:
github: veelenga/ameba
version: "~> 0.14"
license: MIT license: MIT

View File

@ -0,0 +1,10 @@
require "../spec_helper"
describe "Apatite::Matrix" do
describe ".map_with_index" do
it "returns a new matrix with each element processed according to their indexes" do
matrix = Apatite::Matrix[[1, 2], [3, 4]]
matrix.map_with_index { |e, i, j| e*i + j }.should eq Apatite::Matrix[[0, 1], [3, 5]]
end
end
end

View File

@ -1,161 +1,259 @@
require "../spec_helper" require "../spec_helper"
describe "Apatite::Vector" do describe "Apatite::Vector" do
x = Apatite::Vector.new([3, 4]) describe ".[]" do
it "creates a new vector from a list of elements" do
vec = Apatite::Vector[1, 2, 3]
vec.should be_a Apatite::Vector(Int32)
vec[0].should eq 1
vec[2].should eq 3
end
end
describe ".elements" do
it "creates a new vector from an Array" do
arr = [1, 2, 3]
vec = Apatite::Vector.elements(arr)
vec.should be_a Apatite::Vector(Int32)
vec[0].should eq 1
vec[2].should eq 3
end
end
describe ".basis" do
it "creates a standard basis-n vector" do
vec = Apatite::Vector.basis(5, 1)
vec.should eq Apatite::Vector[0, 1, 0, 0, 0]
vec = Apatite::Vector.basis(3, 2)
vec.should eq Apatite::Vector[0, 0, 1]
end
end
describe ".independent?" do
it "returns true if all of vectors are linearly independent" do
Apatite::Vector.independent?(Apatite::Vector[1, 0], Apatite::Vector[0, 1]).should be_true
Apatite::Vector.independent?(Apatite::Vector[1, 2], Apatite::Vector[2, 4]).should be_false
end
end
describe ".zero" do
it "creates a new zero vector" do
vec = Apatite::Vector(Int32).zero(5)
vec.should eq Apatite::Vector[0, 0, 0, 0, 0]
end
end
describe "#==" do describe "#==" do
it "is true if elements are the same" do it "is true if elements are the same" do
(x == Apatite::Vector.new([3, 4])).should be_true x = Apatite::Vector.elements([3, 4])
(x == Apatite::Vector.elements([3, 4])).should be_true
end end
it "is false if elements are different" do it "is false if elements are different" do
(x == Apatite::Vector.new([7, 1])).should be_false x = Apatite::Vector.elements([3, 4])
(x == Apatite::Vector.elements([7, 1])).should be_false
end end
end end
describe "#e" do describe "#+" do
it "returns the `ith` element in the vector" do it "adds a number to every item in the vector" do
x.e(2).should eq(4) vec = Apatite::Vector[1, 2, 3]
vec2 = vec + 2
vec2.should eq Apatite::Vector[3, 4, 5]
end end
it "returns `nil` if `n` is out of range" do it "adds the items in one vector to each parallel item in another" do
x.e(7).should be_nil vec1 = Apatite::Vector[1, 2, 3]
vec2 = Apatite::Vector[3, 2, 1]
vec3 = vec1 + vec2
vec3.should be_a Apatite::Vector(Int32)
vec3.should eq Apatite::Vector[4, 4, 4]
end
pending "adds a vector to a matrix"
end
describe "#-" do
it "subtracts a number from every item in the vector" do
vec = Apatite::Vector[3, 4, 5]
vec2 = vec - 2
vec2.should eq Apatite::Vector[1, 2, 3]
end
it "subtracts the items in one vector from each parallel item in another" do
vec1 = Apatite::Vector[1, 2, 3]
vec2 = Apatite::Vector[3, 2, 1]
vec3 = vec1 - vec2
vec3.should be_a Apatite::Vector(Int32)
vec3.should eq Apatite::Vector[-2, 0, 2]
end
pending "subtracts a vector from a matrix"
end
describe "#*" do
it "multiplies every item in the vector by a number" do
vec = Apatite::Vector[2, 3, 4]
vec2 = vec * 2
vec2.should eq Apatite::Vector[4, 6, 8]
end
it "multiplies the items in one vector with each parallel item in another" do
vec1 = Apatite::Vector[1, 2, 3]
vec2 = Apatite::Vector[3, 2, 1]
vec3 = vec1 * vec2
vec3.should be_a Apatite::Vector(Int32)
vec3.should eq Apatite::Vector[3, 4, 3]
end
pending "multiplies a vector with a matrix"
end
describe "#/" do
it "divides every item in the vector by a number" do
vec = Apatite::Vector[2.0, 3.0, 4.0]
vec2 = vec / 2
vec2.should eq Apatite::Vector[1.0, 1.5, 2.0]
end
it "divides the items in one vector by each parallel item in another" do
vec1 = Apatite::Vector[1.0, 2.0, 3.0]
vec2 = Apatite::Vector[3.0, 2.0, 1.0]
vec3 = vec1 / vec2
vec3.should be_a Apatite::Vector(Float64)
vec3.should eq Apatite::Vector[0.3333333333333333, 1.0, 3.0]
end
pending "divides a vector by a matrix"
end
describe "#==" do
it "ensures two vectors are equal" do
vec1 = Apatite::Vector[1, 2, 3]
vec2 = Apatite::Vector[1, 2, 3]
(vec1 == vec2).should be_true
end
it "checks equality between a vector and an array" do
vec = Apatite::Vector[1, 2, 3]
arr = [1, 2, 3]
(vec == arr).should be_true
end end
end end
describe "#to_unit_vector" do describe "#angle_with" do
it "successfully converts a vector to a unit vector" do it "returns an angle with another vector" do
res = [0.6, 0.8] vec1 = Apatite::Vector[1, 2, 3]
x.to_unit_vector.each_with_index do |e, i| vec2 = Apatite::Vector[3, 2, 1]
e.should be_close(res[i], 0.1) vec1.angle_with(vec2).should eq 0.7751933733103613
end
end
describe "#clone" do
it "creates a copy of the vector" do
vec = Apatite::Vector[1, 2, 3]
cpy = vec.clone
vec.should eq cpy
vec.object_id.should_not eq cpy.object_id
end
end
describe "#map" do
it "should map over a vector's elements" do
vec1 = Apatite::Vector[1, 2, 3]
vec2 = vec1.map(&.succ)
vec2.should eq Apatite::Vector[2, 3, 4]
end
it "should map over two vectors simultaniously" do
vec1 = Apatite::Vector[1, 2, 3]
vec2 = Apatite::Vector[4, 5, 6]
vec3 = vec1.map(vec2) { |v1, v2| v1 + v2 }
vec3.should eq Apatite::Vector[5, 7, 9]
end
end
describe "#covector" do
it "should create a single row matrix from the vector" do
vec = Apatite::Vector[1, 2, 3]
mat = vec.covector
mat.should be_a Apatite::Matrix(Int32)
mat.row_count.should eq 1
mat[0].should eq [1, 2, 3]
end
end
describe "#cross_product" do
it "should return the cross product of multiple vectors" do
vec1 = Apatite::Vector[1, 2, 3]
cross = vec1.cross_product(Apatite::Vector[3, 4, 5])
cross.should eq Apatite::Vector[-2, 4, -2]
end
end
describe "#inner_product" do
it "should return the inner product of two vectors" do
vec = Apatite::Vector[1, 2, 3]
prod = vec.inner_product(Apatite::Vector[3, 4, 5])
prod.should eq 26
end
end
describe "#each" do
it "should iterate over each item in the vector" do
vec = Apatite::Vector[1, 2, 3]
index = 0
vec.each do |n|
n.should eq index + 1
index += 1
end end
end end
end end
describe "#dimensions" do describe "#magnitude" do
it "gets vector's dimensions" do it "should return the modulus of a vector" do
x.dimensions.should eq({1, 2}) vec = Apatite::Vector[1, 2, 3]
vec.magnitude.should eq 3.7416573867739413
end end
end end
describe "#rows" do describe "#normalize" do
it "gets vector's rows" do it "should return a vector with the same direction, but norm 1" do
x.rows.should eq(1) vec = Apatite::Vector[1, 2, 3]
vec.normalize.should eq Apatite::Vector[0.2672612419124244, 0.5345224838248488, 0.8017837257372732]
end end
end end
describe "#cols" do describe "#round" do
it "gets vector's columns" do it "should round each entry in the vector" do
x.cols.should eq(2) vec = Apatite::Vector[0.2672612419124244, 0.5345224838248488, 0.8017837257372732]
vec.round(2).should eq Apatite::Vector[0.27, 0.53, 0.80]
end end
end end
describe "#product" do describe "#coerce" do
it "computes the product of a vector" do it "should return a new vector with elements of a different type" do
x.product.should eq(12) vec = Apatite::Vector[1, 2, 3]
end flt = vec.coerce(Float64)
flt.should be_a Apatite::Vector(Float64)
end end
describe "#angle_from" do it "should work with big rationals" do
it "should compute the angle between `y` and `z`" do vec = Apatite::Vector[1, 2, 3]
y = Apatite::Vector.create([1, 1]) rat = vec.coerce(BigRational, 2)
z = Apatite::Vector.create([1, 0]) rat.should be_a Apatite::Vector(BigRational)
y.angle_from(z).should be_close(Math::PI / 4, 0.1)
end
end end
describe "#parallel_to?" do it "should work with complex numbers" do
it "correctly determines if a vector is parallel to another" do vec = Apatite::Vector[1, 2, 3]
x.parallel_to?(Apatite::Vector.create([6, 8])).should be_true com = vec.coerce(Complex, 1)
x.parallel_to?(Apatite::Vector.create([1, 1])).should be_false com.should be_a Apatite::Vector(Complex)
end end
end end
describe "#antiparallel_to?" do
it "correctly determines if a vector is antiparallel to another" do
x.antiparallel_to?(Apatite::Vector.create([-3, -4])).should be_true
x.antiparallel_to?(x).should be_false
end
end
describe "#perpendicular_to?" do
it "correctly determines if a vector is antiparallel to another" do
x.perpendicular_to?(Apatite::Vector.create([-4, 3])).should be_true
x.perpendicular_to?(x).should be_false
end
end
describe "#dot" do
it "calculates the dot product of the vector and another vector" do
x.dot(Apatite::Vector.create([2, 3])).should eq(18)
end
end
describe "#add" do
it "adds a number to every item in a vector" do
x.add(2).should eq([5, 6])
end
it "adds an enumerable to a vector" do
x.add([3, 2]).should eq([6, 6])
end
end
describe "#subtract" do
it "subtracts a number from every item in a vector" do
x.subtract(2).should eq([1, 2])
end
it "subtracts an enumerable from a vector" do
x.subtract([3, 2]).should eq([0, 2])
end
end
describe "#multiply" do
it "multiplies a number with every item in a vector" do
x.multiply(2).should eq([6, 8])
end
it "multiplies an enumerable with a vector" do
x.multiply([3, 2]).should eq([9, 8])
end
end
describe "#sum" do
it "sums all items in a vector" do
x.sum.should eq(7)
end
end
describe "#chomp" do
it "returns a new vector with the first `n` items of the old vector" do
x.chomp(1).should eq([4])
end
end
describe "#top" do
it "returns a new vector with the last `n` items of the old vector" do
x.top(1).should eq([3])
end
end
describe "#augment" do
it "creates a new vector with the elements fro vector b appended to those from vector a" do
y = x.clone
y.augment(Apatite::Vector.create([5])).should eq([3, 4, 5])
end
end
describe ".log" do
it "should calculate the log of the vector" do
pp x
x.log.should eq([1.0986122886681098, 1.3862943611198906])
end
end
it "should allow for scalar addition" do
a = Apatite::Vector.create([2, 3, 4])
b = a.add(1)
b.should eq([3, 4, 5])
end
end end

View File

@ -1,6 +1,5 @@
require "./apatite/core_ext/array" require "./apatite/core_ext/*"
require "./apatite/*"
require "./apatite/linear_algebra"
# Apatite is a fundimental package for scientific computing in Crystal. If that # Apatite is a fundimental package for scientific computing in Crystal. If that
# sounds like a modified version of the first line from the NumPy homepage, # sounds like a modified version of the first line from the NumPy homepage,
@ -8,6 +7,4 @@ require "./apatite/linear_algebra"
# of NumPy sitting atop the blazing speed and beautiful syntax # of NumPy sitting atop the blazing speed and beautiful syntax
# of Crystal. # of Crystal.
module Apatite module Apatite
extend self
include Apatite::LinearAlgebra
end end

11
src/apatite/error.cr Normal file
View File

@ -0,0 +1,11 @@
module Apatite
class Error < Exception; end
class ErrDimensionMismatch < Error; end
class ZeroVectorError < Error; end
class ErrNotRegular < Error; end
class ErrOperationNotDefined < Error; end
end

View File

@ -1,28 +0,0 @@
require "./linear_algebra/error"
# require "./linear_algebra/ndarray"
require "./linear_algebra/matrix"
require "./linear_algebra/vector"
module Apatite
module LinearAlgebra
extend self
# Calculates the sigmoid curve for a numeric input.
#
# `f(x) = 1/(1 + e^-x)`
#
# See also: [Sigmoid function (WikiWand)](https://www.wikiwand.com/en/Sigmoid_function)
def sigmoid(input : Number)
num = input.to_f64
1 / (1 + Math.exp(-num))
end
# Calculates the derivative sigmoid curve for a numeric input.
#
# `f'(x) = f(x)(1 - f(x)),`
def sigmoid_d(input : Number)
num = input.to_f64
num * (1 - num)
end
end
end

View File

@ -1,15 +0,0 @@
module Apatite
module LinearAlgebra
class Error < Exception; end
class ErrDimensionMismatch < Error; end
class ZeroVectorError < Error; end
class ErrNotRegular < Error; end
class ErrOperationNotDefined < Error
def initialize(method, this, other)
message = "no overload matches '#{this}##{method}' with type #{other}"
super(message)
end
end
end
end

View File

@ -1,32 +0,0 @@
module Apatite::LinearAlgebra
class NDArray
include Enumerable(Float64)
include Indexable(Float64)
include Comparable(NDArray)
getter data : Array(Float64)
getter shape : Array(Int32)
delegate :[], to: @data
delegate :[]?, to: @data
delegate :[]=, to: @data
delegate :unsafe_fetch, to: @data
delegate :to_unsafe, to: @data
delegate :size, to: @data
def initialize(data : Array(Number), shape : Array(Int32)? = nil)
@data = data.is_a?(Array(Float64)) ? data.flatten : data.flatten.map(&.to_f64)
@shape = shape || [@data.size]
end
# Returns the absolute value of every item in the array
def abs
map { |e| e.abs }
end
# Returns the arccosine of each element in the current array.
def acos
end
end
end

View File

@ -2,7 +2,7 @@ require "./vector"
require "./matrix/eigenvalue_decomposition" require "./matrix/eigenvalue_decomposition"
require "./matrix/lup_decomposition" require "./matrix/lup_decomposition"
module Apatite::LinearAlgebra module Apatite
class Matrix(T) class Matrix(T)
include Enumerable(Vector) include Enumerable(Vector)
include Indexable(Vector) include Indexable(Vector)
@ -70,7 +70,7 @@ module Apatite::LinearAlgebra
row_count = row_count.to_i row_count = row_count.to_i
column_count = column_count.to_i column_count = column_count.to_i
raise ArgumentError.new if row_count < 0 || column_count < 0 raise ArgumentError.new if row_count < 0 || column_count < 0
rows = Array(T).new(row_count) do |i| rows = Array(Array(T)).new(row_count) do |i|
Array(T).new(column_count) do |j| Array(T).new(column_count) do |j|
yield i, j yield i, j
end end
@ -148,10 +148,11 @@ module Apatite::LinearAlgebra
identity(n) identity(n)
end end
# Creates a zero matrix. # Creates a zero matrix. Note that a type definition is
# required.
# #
# ``` # ```
# Matrix.zero(2) # Matrix(Int32).zero(2)
# # => [ 0, 0, # # => [ 0, 0,
# # 0, 0 ] # # 0, 0 ]
# ``` # ```
@ -164,7 +165,7 @@ module Apatite::LinearAlgebra
# `row`. # `row`.
# #
# ``` # ```
# Matrix.row_vector([4,5,6]) # Matrix.row_vector([4, 5, 6])
# # => [ 4, 5, 6 ] # # => [ 4, 5, 6 ]
# ``` # ```
def self.row_vector(row) def self.row_vector(row)
@ -176,7 +177,7 @@ module Apatite::LinearAlgebra
# in `column`. # in `column`.
# #
# ``` # ```
# Matrix.column_vector([4,5,6]) # Matrix.column_vector([4, 5, 6])
# # => [ 4, # # => [ 4,
# # 5, # # 5,
# # 6 ] # # 6 ]
@ -257,7 +258,7 @@ module Apatite::LinearAlgebra
# ``` # ```
# x = Matrix[[6, 6], [4, 4]] # x = Matrix[[6, 6], [4, 4]]
# y = Matrix[[1, 2], [3, 4]] # y = Matrix[[1, 2], [3, 4]]
# Matrix.combine(x, y) {|a, b| a - b} # Matrix.combine(x, y) { |a, b| a - b }
# # => Matrix[[5, 4], [1, 0]] # # => Matrix[[5, 4], [1, 0]]
# ``` # ```
def self.combine(*matrices, &block) def self.combine(*matrices, &block)
@ -271,7 +272,7 @@ module Apatite::LinearAlgebra
rows = Array(T).new(x.row_count) do |i| rows = Array(T).new(x.row_count) do |i|
Array(T).new(x.column_count) do |j| Array(T).new(x.column_count) do |j|
yield matrices.map{ |m| m[i,j] } yield matrices.map { |m| m[i, j] }
end end
end end
@ -292,6 +293,10 @@ module Apatite::LinearAlgebra
@column_count = column_count || rows[0].try &.size @column_count = column_count || rows[0].try &.size
end end
def <=>(other : Matrix)
rows <=> other.rows
end
# Returns row `i` of the matrix as an Array. Raises if the # Returns row `i` of the matrix as an Array. Raises if the
# index is not found. # index is not found.
def [](i) def [](i)
@ -379,6 +384,18 @@ module Apatite::LinearAlgebra
Matrix.new(rows, column_count) Matrix.new(rows, column_count)
end end
# Same as #map, but yields the row index and column index in addition to the element
#
# ```
# matrix = Matrix[[1, 2], [3, 4]].map_with_index do |e, row, col|
# e*row + col
# end
# matrix == Matrix[[0, 1], [3, 5]] # => true
# ```
def map_with_index(&block : T, Int32, Int32 -> U) forall U
Matrix(U).build(@rows.size, @column_count) { |i, j| yield @rows[i][j], i, j }
end
# Yields all elements of the matrix, starting with those of the first row, # Yields all elements of the matrix, starting with those of the first row,
# or returns an Enumerator if no block given. # or returns an Enumerator if no block given.
# Elements can be restricted by passing an argument: # Elements can be restricted by passing an argument:
@ -391,9 +408,9 @@ module Apatite::LinearAlgebra
# * :upper: yields only elements on or above the diagonal # * :upper: yields only elements on or above the diagonal
# #
# ``` # ```
# Matrix[ [1,2], [3,4] ].each { |e| puts e } # Matrix[[1, 2], [3, 4]].each { |e| puts e }
# # => prints the numbers 1 to 4 # # => prints the numbers 1 to 4
# Matrix[ [1,2], [3,4] ].each(:strict_lower).to_a # => [3] # Matrix[[1, 2], [3, 4]].each(:strict_lower).to_a # => [3]
# ``` # ```
def each(which = :all, &block : T ->) def each(which = :all, &block : T ->)
last = column_count last = column_count
@ -404,7 +421,7 @@ module Apatite::LinearAlgebra
end end
when "diagonal" when "diagonal"
@rows.each_with_index do |row, row_index| @rows.each_with_index do |row, row_index|
yield row.fetch(row_index){ return self } yield row.fetch(row_index) { return self }
end end
when "off_diagonal" when "off_diagonal"
@rows.each_with_index do |row, row_index| @rows.each_with_index do |row, row_index|
@ -426,7 +443,7 @@ module Apatite::LinearAlgebra
end end
when "strict_upper" when "strict_upper"
@rows.each_with_index do |row, row_index| @rows.each_with_index do |row, row_index|
(row_index+1).upto(last - 1) do |col_index| (row_index + 1).upto(last - 1) do |col_index|
yield row[col_index] yield row[col_index]
end end
end end
@ -441,10 +458,10 @@ module Apatite::LinearAlgebra
end end
end end
# Same as #each, but the row index and column index in addition to the element # Same as #each, but yields the row index and column index in addition to the element
# #
# ``` # ```
# Matrix[ [1,2], [3,4] ].each_with_index do |e, row, col| # Matrix[[1, 2], [3, 4]].each_with_index do |e, row, col|
# puts "#{e} at #{row}, #{col}" # puts "#{e} at #{row}, #{col}"
# end # end
# # => Prints: # # => Prints:
@ -464,7 +481,7 @@ module Apatite::LinearAlgebra
end end
when "diagonal" when "diagonal"
@rows.each_with_index do |row, row_index| @rows.each_with_index do |row, row_index|
block.call(row.fetch(row_index){return self}, row_index, row_index) block.call(row.fetch(row_index) { return self }, row_index, row_index)
end end
when "off_diagonal" when "off_diagonal"
@rows.each_with_index do |row, row_index| @rows.each_with_index do |row, row_index|
@ -486,7 +503,7 @@ module Apatite::LinearAlgebra
end end
when "strict_upper" when "strict_upper"
@rows.each_with_index do |row, row_index| @rows.each_with_index do |row, row_index|
(row_index+1).upto(last - 1) do |col_index| (row_index + 1).upto(last - 1) do |col_index|
block.call(row[col_index], row_index, col_index) block.call(row[col_index], row_index, col_index)
end end
end end
@ -507,7 +524,7 @@ module Apatite::LinearAlgebra
# It also accepts an optional `selector` argument, see `#each` for details. # It also accepts an optional `selector` argument, see `#each` for details.
# #
# ``` # ```
# Matrix[ [1,1], [1,1] ].index(1, :strict_lower) # Matrix[[1, 1], [1, 1]].index(1, :strict_lower)
# # => {1, 0} # # => {1, 0}
# ``` # ```
def index(i, selector = :all) def index(i, selector = :all)
@ -526,7 +543,7 @@ module Apatite::LinearAlgebra
# `#each` for details. # `#each` for details.
# #
# ``` # ```
# Matrix[ [1,2], [3,4] ].index(&.even?) # Matrix[[1, 2], [3, 4]].index(&.even?)
# # => {0, 1} # # => {0, 1}
# ``` # ```
def index(selector = :all, &block : T -> Bool) def index(selector = :all, &block : T -> Bool)
@ -651,7 +668,7 @@ module Apatite::LinearAlgebra
# Returns the Laplace expansion along given row or column. # Returns the Laplace expansion along given row or column.
# #
# ``` # ```
# Matrix[[7,6], [3,9]].laplace_expansion(column: 1) # Matrix[[7, 6], [3, 9]].laplace_expansion(column: 1)
# # => 45 # # => 45
# #
# Matrix[[Vector[1, 0], Vector[0, 1]], [2, 3]].laplace_expansion(row: 0) # Matrix[[Vector[1, 0], Vector[0, 1]], [2, 3]].laplace_expansion(row: 0)
@ -668,7 +685,7 @@ module Apatite::LinearAlgebra
raise "laplace_expansion of empty matrix is not defined" if empty? raise "laplace_expansion of empty matrix is not defined" if empty?
unless 0 <= num && num < row_count unless 0 <= num && num < row_count
raise ArgumentError, "invalid num (#{num.inspect} for 0..#{row_count - 1})" raise ArgumentError.new("invalid num (#{num.inspect} for 0..#{row_count - 1})")
end end
if row if row
@ -676,7 +693,7 @@ module Apatite::LinearAlgebra
e * cofactor(num, k) e * cofactor(num, k)
end.reduce(&.+) end.reduce(&.+)
else else
col(num).map_with_index do |e, k| column(num).map_with_index do |e, k|
e * cofactor(k, num) e * cofactor(k, num)
end.reduce(&.+) end.reduce(&.+)
end end
@ -706,9 +723,9 @@ module Apatite::LinearAlgebra
self self
end end
#-- # --
# TESTING -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- # TESTING -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
#++ # ++
# Returns `true` if this is a diagonal matrix. # Returns `true` if this is a diagonal matrix.
# #
@ -878,9 +895,9 @@ module Apatite::LinearAlgebra
@rows.flatten.all?(&.zero?) @rows.flatten.all?(&.zero?)
end end
#-- # --
# OBJECT METHODS -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- # OBJECT METHODS -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
#++ # ++
# Equality operator # Equality operator
def ==(other : Matrix) def ==(other : Matrix)
@ -895,14 +912,14 @@ module Apatite::LinearAlgebra
Matrix.new(@rows.map(&.dup), column_count) Matrix.new(@rows.map(&.dup), column_count)
end end
#-- # --
# ARITHMETIC -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- # ARITHMETIC -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
#++ # ++
# Matrix multiplication # Matrix multiplication
# #
# ``` # ```
# Matrix[[2,4], [6,8]] * Matrix.identity(2) # Matrix[[2, 4], [6, 8]] * Matrix.identity(2)
# # => [ 2, 4, # # => [ 2, 4,
# # 6, 8 ] # # 6, 8 ]
# ``` # ```
@ -935,7 +952,7 @@ module Apatite::LinearAlgebra
# Matrix addition # Matrix addition
# #
# ``` # ```
# Matrix.scalar(2,5) + Matrix[[1,0], [-4,7]] # Matrix.scalar(2, 5) + Matrix[[1, 0], [-4, 7]]
# # => [ 6, 0, # # => [ 6, 0,
# # -4, 1 ] # # -4, 1 ]
# ``` # ```
@ -962,7 +979,7 @@ module Apatite::LinearAlgebra
# Matrix subtraction # Matrix subtraction
# #
# ``` # ```
# Matrix[[1,5], [4,2]] - Matrix[[9,3], [-4,1]] # Matrix[[1, 5], [4, 2]] - Matrix[[9, 3], [-4, 1]]
# # => [-8, 2, # # => [-8, 2,
# # 8, 1 ] # # 8, 1 ]
# ``` # ```
@ -989,7 +1006,7 @@ module Apatite::LinearAlgebra
# Matrix division (multiplication by the inverse). # Matrix division (multiplication by the inverse).
# #
# ``` # ```
# Matrix[[7,6], [3,9]] / Matrix[[2,9], [3,1]] # Matrix[[7, 6], [3, 9]] / Matrix[[2, 9], [3, 1]]
# # => [ -7, 1, # # => [ -7, 1,
# # -3, -6 ] # # -3, -6 ]
# ``` # ```
@ -997,7 +1014,7 @@ module Apatite::LinearAlgebra
case other case other
when Number when Number
rows = @rows.map do |row| rows = @rows.map do |row|
row.map {|e| (e / other).as(T) } row.map { |e| (e / other).as(T) }
end end
return Matrix.new(rows, column_count) return Matrix.new(rows, column_count)
when Matrix when Matrix
@ -1010,12 +1027,12 @@ module Apatite::LinearAlgebra
# Hadamard product # Hadamard product
# #
# ``` # ```
# Matrix[[1,2], [3,4]].hadamard_product Matrix[[1,2], [3,2]] # Matrix[[1, 2], [3, 4]].hadamard_product Matrix[[1, 2], [3, 2]]
# # => [ 1, 4, # # => [ 1, 4,
# # 9, 8 ] # # 9, 8 ]
# ``` # ```
def hadamard_product(m) def hadamard_product(m)
combine(m){ |a, b| a * b } combine(m) { |a, b| a * b }
end end
# Returns the inverse of the matrix. # Returns the inverse of the matrix.
@ -1072,7 +1089,7 @@ module Apatite::LinearAlgebra
# Non integer exponents will be handled by diagonalizing the matrix. # Non integer exponents will be handled by diagonalizing the matrix.
# #
# ``` # ```
# Matrix[[7,6], [3,9]] ** 2 # Matrix[[7, 6], [3, 9]] ** 2
# # => [ 67, 96, # # => [ 67, 96,
# # 48, 99 ] # # 48, 99 ]
# ``` # ```
@ -1080,9 +1097,9 @@ module Apatite::LinearAlgebra
# TODO # TODO
end end
#-- # --
# MATRIX FUNCTIONS -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- # MATRIX FUNCTIONS -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
#++ # ++
# Returns the determinant of the matrix. # Returns the determinant of the matrix.
# #
@ -1091,7 +1108,7 @@ module Apatite::LinearAlgebra
# Consider using exact types like Rational or BigDecimal instead. # Consider using exact types like Rational or BigDecimal instead.
# #
# ``` # ```
# Matrix[[7,6], [3,9]].determinant # Matrix[[7, 6], [3, 9]].determinant
# # => 45 # # => 45
# ``` # ```
def determinant def determinant
@ -1144,7 +1161,6 @@ module Apatite::LinearAlgebra
size = row_count size = row_count
last = size - 1 last = size - 1
a = to_a a = to_a
no_pivot = Proc(Int32).new { return 0 }
sign = +1 sign = +1
pivot = 1 pivot = 1
size.times do |k| size.times do |k|
@ -1161,7 +1177,7 @@ module Apatite::LinearAlgebra
(k + 1).upto(last) do |i| (k + 1).upto(last) do |i|
ai = a[i] ai = a[i]
(k + 1).upto(last) do |j| (k + 1).upto(last) do |j|
ai[j] = (pivot * ai[j] - ai[k] * a[k][j]) / previous_pivot ai[j] = (pivot * ai[j] - ai[k] * a[k][j]) // previous_pivot
end end
end end
end end
@ -1187,7 +1203,7 @@ module Apatite::LinearAlgebra
# Consider using exact types like Rational or BigDecimal instead. # Consider using exact types like Rational or BigDecimal instead.
# #
# ``` # ```
# Matrix[[7,6], [3,9]].rank # Matrix[[7, 6], [3, 9]].rank
# # => 2 # # => 2
# ``` # ```
def rank def rank
@ -1199,16 +1215,16 @@ module Apatite::LinearAlgebra
pivot_row = 0 pivot_row = 0
previous_pivot = 1 previous_pivot = 1
0.upto(last_column) do |k| 0.upto(last_column) do |k|
switch_row = (pivot_row .. last_row).find {|row| switch_row = (pivot_row..last_row).find { |row|
a[row][k] != 0 a[row][k] != 0
} }
if switch_row if switch_row
a[switch_row], a[pivot_row] = a[pivot_row], a[switch_row] unless pivot_row == switch_row a[switch_row], a[pivot_row] = a[pivot_row], a[switch_row] unless pivot_row == switch_row
pivot = a[pivot_row][k] pivot = a[pivot_row][k]
(pivot_row+1).upto(last_row) do |i| (pivot_row + 1).upto(last_row) do |i|
ai = a[i] ai = a[i]
(k+1).upto(last_column) do |j| (k + 1).upto(last_column) do |j|
ai[j] = (pivot * ai[j] - ai[k] * a[pivot_row][j]) / previous_pivot ai[j] = (pivot * ai[j] - ai[k] * a[pivot_row][j]) // previous_pivot
end end
end end
pivot_row += 1 pivot_row += 1
@ -1221,13 +1237,13 @@ module Apatite::LinearAlgebra
# Returns a matrix with entries rounded to the given precision # Returns a matrix with entries rounded to the given precision
# (see `Float#round`) # (see `Float#round`)
def round(n = 0) def round(n = 0)
map {|e| e.round(n) } map { |e| e.round(n) }
end end
# Returns the trace (sum of diagonal elements) of the matrix. # Returns the trace (sum of diagonal elements) of the matrix.
# #
# ``` # ```
# Matrix[[7,6], [3,9]].trace # Matrix[[7, 6], [3, 9]].trace
# # => 16 # # => 16
# ``` # ```
def trace def trace
@ -1245,11 +1261,11 @@ module Apatite::LinearAlgebra
# Returns the transpose of the matrix. # Returns the transpose of the matrix.
# #
# ``` # ```
# Matrix[[1,2], [3,4], [5,6]] # Matrix[[1, 2], [3, 4], [5, 6]]
# # => [ 1, 2, # # => [ 1, 2,
# # 3, 4, # # 3, 4,
# # 5, 6 ] # # 5, 6 ]
# Matrix[[1,2], [3,4], [5,6]].transpose # Matrix[[1, 2], [3, 4], [5, 6]].transpose
# # => [ 1, 3, 5, # # => [ 1, 3, 5,
# # 2, 4, 6 ] # # 2, 4, 6 ]
# ``` # ```
@ -1276,9 +1292,9 @@ module Apatite::LinearAlgebra
self.class.vstack(self, *matrices) self.class.vstack(self, *matrices)
end end
#-- # --
# DECOMPOSITIONS -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= # DECOMPOSITIONS -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
#++ # ++
# Returns the Eigensystem of the matrix # Returns the Eigensystem of the matrix
# See `EigenvalueDecomposition`. # See `EigenvalueDecomposition`.
@ -1314,17 +1330,17 @@ module Apatite::LinearAlgebra
LUPDecomposition.new(self) LUPDecomposition.new(self)
end end
#-- # --
# COMPLEX ARITHMETIC -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= # COMPLEX ARITHMETIC -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
#++ # ++
# Returns the conjugate of the matrix. # Returns the conjugate of the matrix.
# #
# ``` # ```
# Matrix[[Complex(1,2), Complex(0,1), 0], [1, 2, 3]] # Matrix[[Complex(1, 2), Complex(0, 1), 0], [1, 2, 3]]
# # => 1+2i i 0 # # => 1+2i i 0
# # 1 2 3 # # 1 2 3
# Matrix[[Complex(1,2), Complex(0,1), 0], [1, 2, 3]].conj # Matrix[[Complex(1, 2), Complex(0, 1), 0], [1, 2, 3]].conj
# # => 1-2i -i 0 # # => 1-2i -i 0
# # 1 2 3 # # 1 2 3
# ``` # ```
@ -1336,10 +1352,10 @@ module Apatite::LinearAlgebra
# Returns the imaginary part of the matrix. # Returns the imaginary part of the matrix.
# #
# ``` # ```
# Matrix[[Complex(1,2), Complex(0,1), 0], [1, 2, 3]] # Matrix[[Complex(1, 2), Complex(0, 1), 0], [1, 2, 3]]
# # => [ 1+2i, i, 0, # # => [ 1+2i, i, 0,
# # 1, 2, 3 ] # # 1, 2, 3 ]
# Matrix[[Complex(1,2), Complex(0,1), 0], [1, 2, 3]].imag # Matrix[[Complex(1, 2), Complex(0, 1), 0], [1, 2, 3]].imag
# # => [ 2i, i, 0, # # => [ 2i, i, 0,
# # 0, 0, 0 ] # # 0, 0, 0 ]
# ``` # ```
@ -1351,10 +1367,10 @@ module Apatite::LinearAlgebra
# Returns the real part of the matrix. # Returns the real part of the matrix.
# #
# ``` # ```
# Matrix[[Complex(1,2), Complex(0,1), 0], [1, 2, 3]] # Matrix[[Complex(1, 2), Complex(0, 1), 0], [1, 2, 3]]
# # => [ 1+2i, i, 0, # # => [ 1+2i, i, 0,
# # 1, 2, 3 ] # # 1, 2, 3 ]
# Matrix[[Complex(1,2), Complex(0,1), 0], [1, 2, 3]].real # Matrix[[Complex(1, 2), Complex(0, 1), 0], [1, 2, 3]].real
# # => [ 1, 0, 0, # # => [ 1, 0, 0,
# # 1, 2, 3 ] # # 1, 2, 3 ]
# ``` # ```
@ -1375,9 +1391,9 @@ module Apatite::LinearAlgebra
[real, imag] [real, imag]
end end
#-- # --
# CONVERTING -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- # CONVERTING -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
#++ # ++
# Attempt to coerce the elements in the matrix to another type. # Attempt to coerce the elements in the matrix to another type.
def coerce(klass) def coerce(klass)
@ -1387,18 +1403,23 @@ module Apatite::LinearAlgebra
# Returns an array of the row vectors of the matrix. See `Vector`. # Returns an array of the row vectors of the matrix. See `Vector`.
def row_vectors def row_vectors
Array.new(row_count) {|i| Array.new(row_count) { |i|
row(i) row(i)
} }
end end
# Returns an array of the column vectors of the matrix. See `Vector`. # Returns an array of the column vectors of the matrix. See `Vector`.
def column_vectors def column_vectors
Array.new(column_count) {|i| Array.new(column_count) { |i|
column(i) column(i)
} }
end end
# Return the row and column size as a `Tuple(Int32, Int32)`
def size
{row_count, column_count}
end
# Returns an array of arrays that describe the rows of the matrix. # Returns an array of arrays that describe the rows of the matrix.
def to_a def to_a
@rows.clone @rows.clone
@ -1416,9 +1437,9 @@ module Apatite::LinearAlgebra
self self
end end
#-- # --
# PRINTING -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- # PRINTING -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
#++ # ++
def to_s(io) def to_s(io)
if empty? if empty?
@ -1443,8 +1464,8 @@ module Apatite::LinearAlgebra
end end
end end
def unsafe_fetch(i) def unsafe_fetch(index : Int)
@rows.unsafe_fetch(i) @rows.unsafe_fetch(index)
end end
end end
end end

View File

@ -1,4 +1,4 @@
module Apatite::LinearAlgebra module Apatite
class Matrix(T) class Matrix(T)
# Eigenvalues and eigenvectors of a real matrix. # Eigenvalues and eigenvectors of a real matrix.
# #

View File

@ -1,4 +1,4 @@
module Apatite::LinearAlgebra module Apatite
class Matrix(T) class Matrix(T)
# For an m-by-n matrix A with m >= n, the LU decomposition is an m-by-n # For an m-by-n matrix A with m >= n, the LU decomposition is an m-by-n
# unit lower triangular matrix L, an n-by-n upper triangular matrix U, # unit lower triangular matrix L, an n-by-n upper triangular matrix U,

32
src/apatite/ndarray.cr Normal file
View File

@ -0,0 +1,32 @@
module Apatite
class NDArray
# include Enumerable(Float64)
# include Indexable(Float64)
# include Comparable(NDArray)
# getter data : Array(Float64)
# getter shape : Array(Int32)
# delegate :[], to: @data
# delegate :[]?, to: @data
# delegate :[]=, to: @data
# delegate :unsafe_fetch, to: @data
# delegate :to_unsafe, to: @data
# delegate :size, to: @data
# def initialize(data : Array(Number), shape : Array(Int32)? = nil)
# @data = data.is_a?(Array(Float64)) ? data.flatten : data.flatten.map(&.to_f64)
# @shape = shape || [@data.size]
# end
# # Returns the absolute value of every item in the array
# def abs
# map { |e| e.abs }
# end
# # Returns the arccosine of each element in the current array.
# def acos
# end
end
end

View File

@ -1,7 +1,8 @@
require "complex"
require "big"
require "json" require "json"
require "./matrix"
module Apatite::LinearAlgebra module Apatite
# Represents a mathematical vector, and also constitutes a row or column # Represents a mathematical vector, and also constitutes a row or column
# of a `Matrix` # of a `Matrix`
class Vector(T) class Vector(T)
@ -11,7 +12,7 @@ module Apatite::LinearAlgebra
protected getter elements : Array(T) protected getter elements : Array(T)
delegate :each, to: @elements delegate :each, :size, to: @elements
private def initialize(array : Indexable(T)) private def initialize(array : Indexable(T))
{% raise "Vectors must be one type only" if T.union? %} {% raise "Vectors must be one type only" if T.union? %}
@ -42,13 +43,13 @@ module Apatite::LinearAlgebra
# Returns `true` if all of vectors are linearly independent. # Returns `true` if all of vectors are linearly independent.
# #
# ``` # ```
# Vector.independent?(Vector[1,0], Vector[0,1]) # Vector.independent?(Vector[1, 0], Vector[0, 1])
# # => true # # => true
# #
# Vector.independent?(Vector[1,2], Vector[2,4]) # Vector.independent?(Vector[1, 2], Vector[2, 4])
# # => false # # => false
# ``` # ```
def Vector.independent?(*vs) def self.independent?(*vs)
vs.each do |v| vs.each do |v|
raise "expected Vector, but got #{v.class}" unless v.is_a?(Vector) raise "expected Vector, but got #{v.class}" unless v.is_a?(Vector)
raise ErrDimensionMismatch.new unless v.size == vs.first.size raise ErrDimensionMismatch.new unless v.size == vs.first.size
@ -60,68 +61,86 @@ module Apatite::LinearAlgebra
# Return a zero vector. # Return a zero vector.
def self.zero(size) def self.zero(size)
raise ArgumentError.new("invalid size (#{size} for 0..)") if size < 0 raise ArgumentError.new("invalid size (#{size} for 0..)") if size < 0
array = Array.new(size, 0) array = Array.new(size, T.new(0))
new(array) new(array)
end end
# Multiplies the vector by x, where x is a number or a matrix. # Alien mothership
def *(x) def <=>(other : Vector | Indexable)
case x case other
when Number
els = @elements.map { |e| (e * x).as(T) }
self.class.elements(els, false)
when Matrix
Matrix.column_vector(self) * x
when Vector when Vector
self.elements.zip(x.elements).map { |(x, y)| x * y } elements <=> other.elements
else when Indexable
raise ArgumentError.new elements <=> other
end end
end end
# Multiplies the vector by x, where x is a number.
def *(x : Number)
els = @elements.map { |e| (e * x).as(T) }
{{ @type.name(generic_args: false) }}.elements(els, false)
end
# Multiplies the vector by x, where x is a matrix.
def *(x : Matrix)
Matrix.column_vector(self) * x
end
# Multiplies the vector by x, where x is another vector.
def *(x : Vector)
els = self.elements.zip(x.elements).map { |(x, y)| x * y }
{{ @type.name(generic_args: false) }}.elements(els, false)
end
# Vector addition. # Vector addition.
def +(x) def +(x : Number)
case x
when Number
els = @elements.map { |e| (e + x).as(T) } els = @elements.map { |e| (e + x).as(T) }
self.class.elements(els, false) {{ @type.name(generic_args: false) }}.elements(els, false)
when Matrix
Matrix.column_vector(self) + x
when Vector
self.elements.zip(x.elements).map { |(x, y)| x + y }
else
raise ArgumentError.new
end end
# Vector addition.
def +(x : Matrix)
Matrix.column_vector(self) + x
end
# Vector addition.
def +(x : Vector)
els = self.elements.zip(x.elements).map { |(x, y)| x + y }
{{ @type.name(generic_args: false) }}.elements(els, false)
end end
# Vector subtraction. # Vector subtraction.
def -(x) def -(x : Number)
case x
when Number
els = @elements.map { |e| (e - x).as(T) } els = @elements.map { |e| (e - x).as(T) }
self.class.elements(els, false) {{ @type.name(generic_args: false) }}.elements(els, false)
when Matrix
Matrix.column_vector(self) - x
when Vector
self.elements.zip(x.elements).map { |(x, y)| x - y }
else
raise ArgumentError.new
end end
# Vector subtraction.
def -(x : Matrix)
Matrix.column_vector(self) - x
end
# Vector subtraction.
def -(x : Vector)
els = self.elements.zip(x.elements).map { |(x, y)| x - y }
{{ @type.name(generic_args: false) }}.elements(els, false)
end end
# Vector division. # Vector division.
def /(x) def /(x : Number)
case x
when Number
els = @elements.map { |e| (e / x).as(T) } els = @elements.map { |e| (e / x).as(T) }
self.class.elements(els, false) {{ @type.name(generic_args: false) }}.elements(els, false)
when Matrix
Matrix.column_vector(self) / x
when Vector
self.elements.zip(x.elements).map { |(x, y)| x / y }
else
raise ArgumentError.new
end end
# Vector division.
def /(x : Matrix)
Matrix.column_vector(self) / x
end
# Vector division.
def /(x : Vector)
els = self.elements.zip(x.elements).map { |(x, y)| x / y }
{{ @type.name(generic_args: false) }}.elements(els, false)
end end
# Equality operator # Equality operator
@ -134,7 +153,7 @@ module Apatite::LinearAlgebra
end end
# Take me to your leader # Take me to your leader
def <=> def <=>(other)
if other.is_a?(Vector) if other.is_a?(Vector)
@elements <=> other.elements @elements <=> other.elements
else else
@ -148,25 +167,28 @@ module Apatite::LinearAlgebra
prod = magnitude * v.magnitude prod = magnitude * v.magnitude
raise ZeroVectorError.new("Can't get angle of zero vector") if prod == 0 raise ZeroVectorError.new("Can't get angle of zero vector") if prod == 0
Math.acos( inner_product(v) / prod ) Math.acos(inner_product(v) / prod)
end end
# Returns a copy of the vector. # Returns a copy of the vector.
def clone def clone
self.class.elements(@elements) {{ @type.name(generic_args: false) }}.elements(@elements)
end end
# Maps over a vector, passing each element to the block
def map(&block : T -> _) def map(&block : T -> _)
els = @elements.map(&block) els = @elements.map(&block)
self.class.elements(els, false) {{ @type.name(generic_args: false) }}.elements(els, false)
end end
def map2(v, &block : T, T -> _) # Maps over the current vector and `v` in conjunction, passing each
# element in each to the block and returning a new vector
def map(v, &block : T, T -> _)
raise ErrDimensionMismatch.new if size != v.size raise ErrDimensionMismatch.new if size != v.size
arr = Array.new(size) do |i| arr = Array.new(size) do |i|
yield @elements[i], v[i] yield @elements[i], v[i]
end end
self.class.elements(arr, false) {{ @type.name(generic_args: false) }}.elements(arr, false)
end end
# Creates a single-row matrix from this vector. # Creates a single-row matrix from this vector.
@ -188,11 +210,11 @@ module Apatite::LinearAlgebra
Vector[-@elements[1], @elements[0]] Vector[-@elements[1], @elements[0]]
when 3 when 3
v = vs[0] v = vs[0]
Vector[ v[2]*@elements[1] - v[1]*@elements[2], Vector[v[2]*@elements[1] - v[1]*@elements[2],
v[0]*@elements[2] - v[2]*@elements[0], v[0]*@elements[2] - v[2]*@elements[0],
v[1]*@elements[0] - v[0]*@elements[1] ] v[1]*@elements[0] - v[0]*@elements[1]]
else else
rows = self + vs + Array.new(size) {|i| Vector.basis(size, i) } rows = [self, vs.to_a, Array.new(size) { |i| Vector.basis(size, i) }].flatten
Matrix.rows(rows).laplace_expansion(row: size - 1) Matrix.rows(rows).laplace_expansion(row: size - 1)
end end
end end
@ -205,10 +227,7 @@ module Apatite::LinearAlgebra
# Returns the inner product of this vector with the other. # Returns the inner product of this vector with the other.
def inner_product(v) def inner_product(v)
raise ErrDimensionMismatch.new if size != v.size raise ErrDimensionMismatch.new if size != v.size
map(v) { |v1, v2| v1 * v2 }.sum
p = 0
each2(v) { |v1, v2| p += v1 * v2 }
p
end end
# ditto # ditto
@ -217,7 +236,7 @@ module Apatite::LinearAlgebra
end end
# Iterate over the elements of this vector and `v` in conjunction. # Iterate over the elements of this vector and `v` in conjunction.
def each2(v, &block) def each(v, &block)
raise ErrDimensionMismatch.new if size != v.size raise ErrDimensionMismatch.new if size != v.size
size.times do |i| size.times do |i|
yield @elements[i], v[i] yield @elements[i], v[i]
@ -239,7 +258,7 @@ module Apatite::LinearAlgebra
def normalize def normalize
n = magnitude n = magnitude
raise ZeroVectorError.new("Zero vectors can not be normalized") if n == 0 raise ZeroVectorError.new("Zero vectors can not be normalized") if n == 0
self / n self.coerce(Float64) / n
end end
# ditto # ditto
@ -249,27 +268,35 @@ module Apatite::LinearAlgebra
# Returns a vector with entries rounded to the given precision. # Returns a vector with entries rounded to the given precision.
def round(ndigits = 0) def round(ndigits = 0)
map{ |e| e.round(ndigits) } map { |e| e.round(ndigits) }
end
# Attempt to coerce the elements in a vector to Complex with
# `imag` as the imaginary number.
def coerce(klass : Complex.class, imag : Number)
els = @elements.map { |e| Complex.new(e, imag) }
{{ @type.name(generic_args: false) }}.elements(els)
end
# Attempt to coerce the elements in a vector to BigInt with
# an optional `base` value.
def coerce(klass : BigInt.class, base = 10)
els = @elements.map { |e| BigInt.new(e, base) }
{{ @type.name(generic_args: false) }}.elements(els)
end
# Attempt to coerce the elements in a vector to BigRational
# with the given `denominator`.
def coerce(klass : BigRational.class, denominator : Int)
els = @elements.map { |e| BigRational.new(e, denominator) }
{{ @type.name(generic_args: false) }}.elements(els)
end end
# The coerce method allows you to attempt to coerce the elements # The coerce method allows you to attempt to coerce the elements
# in the matrix to another type. The type # in the matrix to another type.
def coerce(klass, *args) def coerce(klass : U.class) : Vector(U) forall U
case klass.to_s els = @elements.map { |e| klass.new(e).as(U) }
when "Complex" {{ @type.name(generic_args: false) }}.elements(els)
raise "coercing to a Complex requires a second argument" unless args[0]?
els = @elements.map { |e| Complex.new(e, args[0].as(Int32)) }
when "BigInt"
base = args[0]? || 10
els = @elements.map { |e| klass.new(e, base) }
when "BigRational"
raise "coercing to a BigRational requires a second argument to use as a denominator" unless args[0]?
els = @elements.map { |e| klass.new(e, args[0]) }
else
els = @elements.map { |e| klass.new(e) }
end
Vector.elements(els)
end end
# Returns the elements of the vector in an array. # Returns the elements of the vector in an array.
@ -291,12 +318,17 @@ module Apatite::LinearAlgebra
all?(&:zero?) all?(&:zero?)
end end
# Returns the number of elements in the vector.
def size
@elements.size
end
def inspect def inspect
"<#Vector(#{T}) [#{@elements.join(", ")}]>" "<#Vector(#{T}) [#{@elements.join(", ")}]>"
end end
def unsafe_fetch(i) def unsafe_fetch(index : Int)
@elements.unsafe_fetch(i) @elements.unsafe_fetch(index)
end end
end end
end end