Here's a simple example of Nifty in action!
Nifty offers three fundamental types of data structures: vectors, matrices, and tensors, with various initializers for each. For example,
var v = Vector([2.34, 6, 0.000000034, 67.234, 98456456.234])
var M = Matrix([[276.0251, 498.3641, 751.2671],
[679.7027, -959.7440, 255.0951],
[655.0980, -340.3857, 505.9571]])
var T = Tensor([3, 1, 3], [0.0, 0.1, 0.2, 1.0, 1.1, 1.2, 2.0, 2.1, 2.2])
Each type provides methods for cleanly displaying the data it contains. For example, calling print(v)
displays,
<2.34 6 3.4E-8 67.234 9.846E7>
calling print(M)
displays,
R0: 276.025 498.364 751.267
R1: 679.703 -959.744 255.095
R2: 655.098 -340.386 505.957
and calling print(T)
displays,
[..., ..., 0]:
R0: 0.0
R1: 1.0
R2: 2.0
[..., ..., 1]:
R0: 0.1
R1: 1.1
R2: 2.1
[..., ..., 2]:
R0: 0.2
R1: 1.2
R2: 2.2
By default, the data is rounded to keep the display compact, but the settings can be adjusted for each instance. Additionally, each can be set to have a name displayed along with the contents,
M.name = "MyNiftyMatrix"
M.showName = true
Now, calling print(M)
displays,
MyNiftyMatrix:
R0: 276.025 498.364 751.267
R1: 679.703 -959.744 255.095
R2: 655.098 -340.386 505.957
Nifty fundamental structures are all zero-indexed, stored in row-major order, and can be easily indexed, subscripted, and sliced. For example,
V[3] = 3.0
M[0] = 0.1
M[4] = 1.1
M[8] = 2.2
M[0,1] = 0.1
M[1,1] = 1.1
M[2,2] = 2.2
M[1...2,1...2] = Matrix(2, [11,12,21,22])
let t = T[0, 0, 0..<3]
Most of the functionality provided by Nifty is based on these fundamental types, which usually contain floating-point numbers. However, all of the fundamental types are generic and can contain any type of values. For example,
let Ms = Matrix(2, 4, value: "X")
creates a 2-by-4 string matrix containing the letter "X" in each cell, which displays as,
R0: X X X X
R1: X X X X
Nifty provides a variety of linear algebra functions, mainly operating on Matrix<Double>
types.
Here's an example of how we could invert a matrix, using both the function inv()
and the ~
prefix operator (Nifty provides symbolic operators for a variety of common math functions):
let Mi = ~M
let _ = inv(M)
Here, we'll use Nifty to solve the system of equations A*x = B
for x
:
let A = Matrix<Double>([[21, 3, 4, 4, 17],
[23, 7, 25, 11, 1],
[ 4, 14, 24, 23, 22],
[23, 24, 13, 20, 24],
[16, 25, 21, 24, 17]], name: "A")
let B = Matrix<Double>([[18, 21, 11, 13, 7],
[ 1, 18, 10, 12, 17],
[ 7, 8, 20, 17, 17],
[ 2, 24, 20, 18, 5],
[ 3, 1, 5, 19, 3]], name: "B")
let x = A-/B // or, mldivide(A, B)
Calling print(x)
displays the following (note that the output matrix was given a name based on the inputs' names and the transform applied--all Nifty functions do this by default!):
mldivide(A, B):
R0: 4.45782 -5.77337 -5.80571 1.63021 -2.26439
R1: -12.5532 17.9503 16.122 -3.95338 5.81648
R2: -10.5024 16.5735 15.0794 -3.31092 6.6158
R3: 23.1 -36.2762 -32.3031 7.87055 -12.7877
R4: -5.19676 9.83532 9.0264 -1.62428 3.63471
There is a lot more linear algebra functionality in Nifty--check out the demo code for further examples or the Nifty documentation for a complete listing.
Nifty also provides a variety of easy-to-use random number functions. For example, creating a 6-by-3 matrix of random whole numbers between 35 and 110:
let Ri = randi(6, 3, min: 35, max: 110)
Or creating a list of 5 random vectors from a multivariate normal distribution:
let mu = Vector([2.0,3.0], name: "μ")
let sigma = Matrix<Double>([[1,1.5],[1.5,3.0]], name: "Σ")
let vectors = mvnrnd(mu: mu, sigma: sigma, cases: 5)
Nifty makes timing code snippets particularly easy. MATLAB users will be familiar with tic
and toc
, which start a stopwatch, and return the time on the stopwatch, respectively. For example,
tic()
let Z = randi(500, 500, min: -999, max: 999)
toc()
displays the number of seconds it took to generate Z. Times are by default in seconds, but the default can be overridden to provide the ellapsed time in the desired units. For example,
tic()
let Zi = inv(Z)
toc(.ms)
displays the ellapsed time in milliseconds. Rather than displaying the time, the result can be stored to a variable. For example,
tic()
let _ = Z * Zi
let time = toc(returning: .minutes)
Getting Nifty running is easy; follow the steps below:
-
Clone this repository
-
Make sure you've met the requirements for Nifty
-
Open a terminal inside your local copy of this repository
-
Run
swift build
. You should see the package manager cloning into various repositories to gather the needed packages, compiling, and then linking. -
Once the build has completed, execute
.build/debug/Nifty-demo
to run the demo!Look here if you get linker errors while building