Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update nimble branch #1

Closed
wants to merge 11 commits into from
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,9 +113,9 @@ Several other methods are in testing and will likely be added in the future rele
scientific computing community.

## Usage in Nim
Usage within Nim is fairly straightforward. You can install it using Nimble as explained earlier, or install it directly from GitHub:
Usage within Nim is fairly straightforward. You can install it using Nimble as explained earlier, or install it directly from GitHub, making sure to use the slightly modified `@#nimble` branch:
```cmd
nimble install -y https://github.com/amkrajewski/nimplex
nimble install -y https://github.com/amkrajewski/nimplex@#nimble
```
or, if you wish to modify the source code, you can simply download the core file `nimplex.nim` and place it in your own code, as long as you have the dependencies installed, since it is standalone.
**Then simply follow the API documentation below.**
Expand Down
14 changes: 7 additions & 7 deletions docs/docs.nim
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
## ```cmd
## ./nimplex -c IFP 3 10
## ```
## 2. An **compiled Python library**, which you can import and use in your Python code like so:
## 2. A **compiled Python library**, which you can import and use in your Python code like so:
## ```python
## import nimplex
## ```
Expand Down Expand Up @@ -111,9 +111,9 @@
## Critically, unlike the O(N^2) distance-based graph generation methods, this approach **scales linearly** with the resulting number of nodes. Because of that, it is extremely efficient even in high-dimensional spaces, where the number of
## edges goes into trillions and beyond. Nimplex can **both generate and find neighbors** for around **2M points per second in 9-dimensional space** on a modern CPU.
##
## As explored in the manuscript, such representations, even of different dimensions, can can then be used to efficeintly encode complex problem spaces where some prior assumptions and knowledge are available. In the Example #2 from
## manuscript, inspired by problem of joining titanium with stainless steel in [10.1016/j.addma.2022.102649](https://doi.org/10.1016/j.addma.2022.102649) using 3-component
## spaces, one encode 3 separate paths where some components are shared in predetermined fashion. This to efficiently encode the problem space in form of a structure graph (left panel below) and then use it to construct a
## As explored in the manuscript, such representations, even of different dimensions, can then be used to efficeintly encode complex problem spaces where some prior assumptions and knowledge are available. Consider the problem of joining
## titanium with stainless steel using 3-component spaces as presented in Bobbio et. al, [10.1016/j.addma.2022.102649](https://doi.org/10.1016/j.addma.2022.102649). Example #2 from the manuscript handles this problem by encoding 3
## separate paths where some components are shared in a predetermined fashion. This is done to efficiently encode the problem space in the form of a structure graph (left panel below) and then use it to construct a
## single **simplex graph complex** (right panel below) as a single consistent structure.
##
## .. figure:: ../assets/Fig4.png
Expand All @@ -123,9 +123,9 @@
## scientific computing community.
##
## ## Usage in Nim
## Usage within Nim is fairly straightforward. You can install it using Nimble as explained earlier, or install it directly from GitHub:
## Usage within Nim is fairly straightforward. You can install it using Nimble as explained earlier, or install it directly from GitHub, making sure to use the slightly modified `@#nimble` branch:
## ```cmd
## nimble install -y https://github.com/amkrajewski/nimplex
## nimble install -y https://github.com/amkrajewski/nimplex@#nimble
## ```
## or, if you wish to modify the source code, you can simply download the core file `nimplex.nim` and place it in your own code, as long as you have the dependencies installed, since it is standalone.
## **Then simply follow the API documentation below.**
Expand Down Expand Up @@ -232,4 +232,4 @@
## ## API
##
## **The following sections cover the core API of the library.** Further documentation for auxiliary functions, defined in **utils** submodule, can be found by following links under [Imports](#6) below
## or by searching the API [Index](theindex.html) in the top left corner of this page.
## or by searching the API [Index](theindex.html) in the top left corner of this page.
407 changes: 357 additions & 50 deletions examples/quickstart.ipynb

Large diffs are not rendered by default.

21 changes: 11 additions & 10 deletions nimplex.nim
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import std/sugar
import std/times
import std/strutils
from std/algorithm import reverse
from std/sequtils import foldl, mapIt

# Arraymancer library for tensor operations
import arraymancer/Tensor
Expand Down Expand Up @@ -402,12 +403,12 @@ proc outFunction(config: string, npyName: string, outputData: Tensor) =
## and the destiantion filename `npyName`, which should include the extension.
case config[2]:
of 'P':
echo "Full Output:", outputData
echo "Full Output: ", outputData
of 'N':
outputData.write_npy(npyName)
echo "Shape:", outputData.shape
echo "Shape: ", outputData.shape
of 'S':
echo "Shape:", outputData.shape
echo "Shape: ", outputData.shape
else:
echo "Invalid Config"

Expand All @@ -434,11 +435,11 @@ proc outFunction_graph(config: string, dim: int, ndiv: int, npyName: string, out
else:
neighborsTensor[i, j] = -1
neighborsTensor.write_npy(npyName.replace(".npy", "_neighbors.npy"))
echo "Nodes Shape:", outputData[0].shape
echo "Neighbors Lenght:", outputData[1].len
echo "Nodes Shape: ", outputData[0].shape
echo "Edges Count: ", outputData[1].mapIt(it.len).foldl(a + b, 0)
of 'S':
echo "Nodes Shape:", outputData[0].shape
echo "Neighbors Lenght:", outputData[1].len
echo "Nodes Shape: ", outputData[0].shape
echo "Edges Count: ", outputData[1].mapIt(it.len).foldl(a + b, 0)
else:
echo "Invalid Congig"

Expand Down Expand Up @@ -495,14 +496,14 @@ when appType != "lib":
let tempIn = readLine(stdin)
if tempIn.len > 0:
npyName = tempIn
echo "Persisting to NumPy array file:", npyName
echo "Persisting to NumPy array file: ", npyName

taskRouter(config, dim, ndiv, npyName)

# Configured
elif args[0] == "-c" or args[0] == "--config":
let config = args[1]
echo "Running with configuration:", args[1..<args.len]
echo "Running with configuration: ", args[1..<args.len]
configValidation(config)

let dim = args[2].parseInt()
Expand All @@ -520,7 +521,7 @@ when appType != "lib":
if config[2] == 'N':
if args.len == 5:
npyName = args[4]
echo "Persisting to NumPy array file:", npyName
echo "Persisting to NumPy array file: ", npyName

taskRouter(config, dim, ndiv, npyName)

Expand Down
66 changes: 48 additions & 18 deletions tests/cli.nim
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,15 @@ import system
import std/unittest
import std/strutils
import std/sequtils
import std/sugar
import arraymancer/Tensor
import arraymancer/io
import std/hashes
import ../nimplex
import std/sets
import std/times

let t0 = cpuTime()
echo "***** CLI Tests *****"

suite "test if correct grid output is given when nimplex is run in command line with some selected configurations":
test "check if compiled nimplex is present in the current working directory":
Expand All @@ -22,7 +25,9 @@ suite "test if correct grid output is given when nimplex is run in command line
let
(output, exitCode) = execCmdEx("./nimplex -c FIS 3 3")
outputLines = output.splitLines
reference = @["Running with configuration:@[\"FIS\", \"3\", \"3\"]", "Shape:[10, 3]"]
reference = @[
"Running with configuration: @[\"FIS\", \"3\", \"3\"]",
"Shape: [10, 3]"]
check exitCode == 0
for i in 0..<reference.len:
check outputLines[i] == reference[i]
Expand All @@ -31,7 +36,9 @@ suite "test if correct grid output is given when nimplex is run in command line
let
(output, exitCode) = execCmdEx("./nimplex -c FIS 9 12")
outputLines = output.splitLines
reference = @["Running with configuration:@[\"FIS\", \"9\", \"12\"]", "Shape:[125970, 9]"]
reference = @[
"Running with configuration: @[\"FIS\", \"9\", \"12\"]",
"Shape: [125970, 9]"]
check exitCode == 0
for i in 0..<reference.len:
check outputLines[i] == reference[i]
Expand All @@ -40,7 +47,9 @@ suite "test if correct grid output is given when nimplex is run in command line
let
(output, exitCode) = execCmdEx("./nimplex -c IIS 7 12")
outputLines = output.splitLines
reference = @["Running with configuration:@[\"IIS\", \"7\", \"12\"]", "Shape:[462, 7]"]
reference = @[
"Running with configuration: @[\"IIS\", \"7\", \"12\"]",
"Shape: [462, 7]"]
check exitCode == 0
for i in 0..<reference.len:
check outputLines[i] == reference[i]
Expand All @@ -50,8 +59,8 @@ suite "test if correct grid output is given when nimplex is run in command line
(output, exitCode) = execCmdEx("./nimplex -c FIP 3 3")
outputLines = output.splitLines
reference = @[
"Running with configuration:@[\"FIP\", \"3\", \"3\"]",
"Full Output:Tensor[system.int] of shape \"[10, 3]\" on backend \"Cpu\"",
"Running with configuration: @[\"FIP\", \"3\", \"3\"]",
"Full Output: Tensor[system.int] of shape \"[10, 3]\" on backend \"Cpu\"",
"|0 0 3|",
"|0 1 2|",
"|0 2 1|",
Expand All @@ -72,8 +81,8 @@ suite "test if correct grid output is given when nimplex is run in command line
(output, exitCode) = execCmdEx("./nimplex -c IIP 3 5")
outputLines = output.splitLines
reference = @[
"Running with configuration:@[\"IIP\", \"3\", \"5\"]",
"Full Output:Tensor[system.int] of shape \"[6, 3]\" on backend \"Cpu\"",
"Running with configuration: @[\"IIP\", \"3\", \"5\"]",
"Full Output: Tensor[system.int] of shape \"[6, 3]\" on backend \"Cpu\"",
"|1 1 3|",
"|1 2 2|",
"|1 3 1|",
Expand Down Expand Up @@ -145,8 +154,23 @@ suite "test if correct graph output is given when nimplex is run in command line
(output, exitCode) = execCmdEx("./nimplex -c GIS 3 3")
outputLines = output.splitLines
reference = @[
"Running with configuration:@[\"GIS\", \"3\", \"3\"]",
"Nodes Shape:[10, 3]"]
"Running with configuration: @[\"GIS\", \"3\", \"3\"]",
"Nodes Shape: [10, 3]",
"Edges Count: 36"
]
check exitCode == 0
for i in 0..<reference.len:
check outputLines[i] == reference[i]

test "generate medium size integer simplex graph (GIS 7 12) and print shape to stdout":
let
(output, exitCode) = execCmdEx("./nimplex -c GIS 7 12")
outputLines = output.splitLines
reference = @[
"Running with configuration: @[\"GIS\", \"7\", \"12\"]",
"Nodes Shape: [18564, 7]",
"Edges Count: 519792"
]
check exitCode == 0
for i in 0..<reference.len:
check outputLines[i] == reference[i]
Expand All @@ -156,7 +180,7 @@ suite "test if correct graph output is given when nimplex is run in command line
(output, exitCode) = execCmdEx("./nimplex -c GIP 3 3")
outputLines = output.splitLines
reference = @[
"Running with configuration:@[\"GIP\", \"3\", \"3\"]",
"Running with configuration: @[\"GIP\", \"3\", \"3\"]",
"Nodes:",
"Tensor[system.int] of shape \"[10, 3]\" on backend \"Cpu\"",
"|0 0 3|",
Expand All @@ -181,8 +205,10 @@ suite "test if correct graph output is given when nimplex is run in command line
(output, exitCode) = execCmdEx("./nimplex -c GFS 3 3")
outputLines = output.splitLines
reference = @[
"Running with configuration:@[\"GFS\", \"3\", \"3\"]",
"Nodes Shape:[10, 3]"]
"Running with configuration: @[\"GFS\", \"3\", \"3\"]",
"Nodes Shape: [10, 3]",
"Edges Count: 36"
]
check exitCode == 0
for i in 0..<reference.len:
check outputLines[i] == reference[i]
Expand Down Expand Up @@ -250,12 +276,12 @@ suite "Test NumPy exports corectness for grids":
require fileExists("nimplex")

test "generate auto-named a medium fractional internal simplex grid (IFP 7 11) and export it to NumPy (nimplex_IF_7_11.npy)":
let (output, exitCode) = execCmdEx("./nimplex -c IFN 7 11")
let (_, exitCode) = execCmdEx("./nimplex -c IFN 7 11")
check exitCode == 0
check fileExists("nimplex_IF_7_11.npy")

test "generate a medium fractional internal simplex grid (IFP 7 11) named testExport.npy and export it to NumPy":
let (output, exitCode) = execCmdEx("./nimplex -c IFN 7 11 testExport.npy")
let (_, exitCode) = execCmdEx("./nimplex -c IFN 7 11 testExport.npy")
check exitCode == 0
check fileExists("testExport.npy")

Expand Down Expand Up @@ -283,13 +309,13 @@ suite "Test NumPy exports corectness for graphs":
require fileExists("nimplex")

test "generate auto-named a medium fractional simplex graph (GFP 7 11) and export it to NumPy (nimplex_GF_7_11_nodes.npy and nimplex_GF_7_11_neighbors.npy)":
let (output, exitCode) = execCmdEx("./nimplex -c GFN 7 11")
let (_, exitCode) = execCmdEx("./nimplex -c GFN 7 11")
check exitCode == 0
check fileExists("nimplex_GF_7_11_nodes.npy")
check fileExists("nimplex_GF_7_11_neighbors.npy")

test "generate a medium fractional simplex graph (GFP 7 11) named testExport.npy and testExport.npy and export it to NumPy":
let (output, exitCode) = execCmdEx("./nimplex -c GFN 7 11 testExport.npy")
let (_, exitCode) = execCmdEx("./nimplex -c GFN 7 11 testExport.npy")
check exitCode == 0
check fileExists("testExport_nodes.npy")
check fileExists("testExport_neighbors.npy")
Expand Down Expand Up @@ -326,4 +352,8 @@ suite "Test NumPy exports corectness for graphs":
check resultNeighbors.len == loadedNumpyNeighborsParsed.len
for i in 0..<resultNeighbors.len:
check resultNeighbors[i].toHashSet() == loadedNumpyNeighborsParsed[i].toHashSet()


let t1 = cpuTime()
echo "\n***** CLI BENCHMARK RESULTS *****\n"
echo "Large Graphs:\n" & $initDuration(milliseconds = ((t1 - t0)*1e3).int) & "\n"
echo "-----------------------------------\n"
9 changes: 5 additions & 4 deletions tests/graph.nim
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import std/times

# SMALL GRAPHS
let t0 = cpuTime()
echo "*** SMALL GRAPHS ***"
echo "***** SMALL GRAPHS *****"

suite "small simplex integer 2-component (binary) graph":
let
Expand Down Expand Up @@ -174,7 +174,7 @@ suite "small simplex fractional 4-component (quaternary) graph":

# LARGE GRAPHS
let t1 = cpuTime()
echo "*** LARGE GRAPHS ***"
echo "***** LARGE GRAPHS *****"

suite "large simplex integer 3-component (ternary) graph":
let
Expand Down Expand Up @@ -273,6 +273,7 @@ suite "very large simplex fractional 12-component graph (1M+ nodes / 93M+ edges

# BENCHMARK RESULTS
let t2 = cpuTime()
echo "\n*** BENCHMARK RESULTS ***\n"
echo "\n***** GRAPH BENCHMARK RESULTS *****\n"
echo "Small Graphs:\n" & $initDuration(microseconds = ((t1 - t0)*1e6).int) & "\n"
echo "Large Graphs:\n" & $initDuration(milliseconds = ((t2 - t1)*1e3).int) & "\n"
echo "Large Graphs:\n" & $initDuration(milliseconds = ((t2 - t1)*1e3).int) & "\n"
echo "-------------------------------------\n"
6 changes: 5 additions & 1 deletion tests/grid.nim
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ import ../nimplex
import arraymancer/Tensor
import std/times

let t0 = cpuTime()
# SMALL GRIDS
let t0 = cpuTime()
echo "***** SMALL GRIDS *****"

suite "small simplex full integer grids":
let result = nimplex.simplex_grid(2, 5)
Expand Down Expand Up @@ -97,6 +98,7 @@ suite "small simplex attainable grids (binary with ndiv=6 in ternary from [1,1,1

let t1 = cpuTime()
# LARGE GRIDS
echo "***** LARGE GRIDS *****"

suite "large simplex full integer grids":
let result = nimplex.simplex_grid(7, 24)
Expand Down Expand Up @@ -152,6 +154,8 @@ suite "large simplex internal fractional grids":
for i in 0..6:
check abs(resultCherryPick[i] - reference[i]) < 0.0001

echo "***** GRIDS BENCHMARK RESULTS *****"
let t2 = cpuTime()
echo "Small Grids:\n" & $initDuration(microseconds = ((t1 - t0)*1e6).int) & "\n"
echo "Large Grids:\n" & $initDuration(milliseconds = ((t2 - t1)*1e3).int) & "\n"
echo "---------------------------------\n"
Loading