Add plots and concat test

This commit is contained in:
Les De Ridder 2019-07-10 00:46:42 +02:00
parent a28db7bc32
commit 2be5cd88bc
15 changed files with 483 additions and 18 deletions

View File

@ -5,6 +5,7 @@ copyright "Copyright © 2019, Les De Ridder"
license "NCSA"
dependency "emsi_containers" version="~>0.7.0"
dependency "collections" version="~>0.1.0"
dependency "matplotlib-d" path="matplotlib-d"
-- :/
importPaths "/home/lesderid/repos/d/druntime/src"

View File

@ -3,8 +3,7 @@
"versions": {
"collections": "0.1.0",
"emsi_containers": "0.7.0",
"mir-algorithm": "3.5.0-alpha09",
"mir-core": "0.3.5",
"matplotlib-d": {"path":"matplotlib-d"},
"stdx-allocator": "2.77.5"
}
}

11
matplotlib-d/.gitignore vendored Normal file
View File

@ -0,0 +1,11 @@
.dub
docs.json
__dummy.html
*.o
*.obj
__test__*__
*~
views/
build/
README.html
*.png

17
matplotlib-d/.travis.yml Normal file
View File

@ -0,0 +1,17 @@
os:
- linux
- osx
language: d
d:
- ldc2
- dmd
before_install:
- if [ "$TRAVIS_OS_NAME" == "linux" ]; then sudo apt-get install python-tk; fi
- sudo pip install matplotlib
script:
- dub test --compiler=${DC}

21
matplotlib-d/LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2016-2019 koji-kojiro
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

91
matplotlib-d/README.md Normal file
View File

@ -0,0 +1,91 @@
# matplotlib-d
[![MIT License](http://img.shields.io/badge/license-MIT-blue.svg?style=flat)](LICENSE)
[![Build Status](https://travis-ci.org/koji-kojiro/matplotlib-d.svg?branch=master)](https://travis-ci.org/koji-kojiro/matplotlib-d)
[![Dub version](https://img.shields.io/dub/v/matplotlib-d.svg)](https://code.dlang.org/packages/matplotlib-d)
[![Dub download](https://img.shields.io/dub/dt/matplotlib-d.svg)](https://code.dlang.org/packages/matplotlib-d)
2D Plotting library for D using python and matplotlib.
## Requirements
- Python
- matplotlib
`matplotlib-d` uses `python3` by default, thus if you want to use `python2`, set `$MATPLOTLIB_D_PYTHON` to `python2`.
## Usage
### Installation
To use this package, put the following dependency into your project's dependencies section:
dub.json: `"matplotlib-d": "~>0.1.4"`
dub.sdl: `dependency "matplotlib-d" version="~>0.1.4"`
For small applications or scripts, add following sentence to the head of your script.
```d
#!/usr/bin/env dub
/+ dub.sdl:
name "name_of_your_application"
dependency "matplotlib-d" version="~>0.1.4"
+/
```
And excute with `dub run --single`.
For more details, please refer to [the documentation of dub](https://code.dlang.org/getting_started).
### Syntax
Most pyplot functions are available.
For more details for each functions, please refer to the [documantation of pyplot](http://matplotlib.org/api/pyplot_summary.html).
Describe Python keyword arguments as an associative array with string of keyword name as key.
- The Python way:
*function(arg1, arg2..., keyword1=kwarg1, keyword2=kwarg2...)*
- The D way:
*function(arg1, arg2..., ["keyword1": kwarg1], ["keyword2": kwarg2])*
## Examples
Simple example:
```d
import std.math;
import std.range;
import std.algorithm;
import plt = matplotlibd.pyplot;
void main() {
auto x = iota(0, 2.05, 0.05).map!(x => x * PI);
auto y = x.map!(sin);
plt.plot(x, y, "r-", ["label": "$y=sin(x)$"]);
plt.xlim(0, 2 * PI);
plt.ylim(-1, 1);
plt.legend();
plt.savefig("simple.png");
plt.clear();
}
```
![Simple example](./examples/simple.png)
Color plot example:
```d
import std.range;
import plt = matplotlibd.pyplot;
void main() {
const n = 100;
auto x = iota(n);
auto y = x[];
double[n][n] z;
foreach (i; 0..n)
foreach (j; 0..n)
z[i][j] = i + j;
plt.contourf(x, y, z, 64, ["cmap": "hsv"]);
plt.colorbar();
plt.savefig("color.png");
plt.clear();
}
```
![Color plot example](./examples/color.png)

12
matplotlib-d/dub.json Normal file
View File

@ -0,0 +1,12 @@
{
"name": "matplotlib-d",
"authors": ["koji-kojiro"],
"description": "2D Plotting library for D using python and matplotlib",
"copyright": "Copyright © 2016-2019, koji-kojiro",
"license": "MIT",
"targetName": "matplotlibd",
"targetType": "staticLibrary",
"targetPath": "build",
"workingDirectory": "build",
"preBuildCommands": ["python3 $PACKAGE_DIR/python/prebuild.py $PACKAGE_DIR"]
}

7
matplotlib-d/examples/.gitignore vendored Normal file
View File

@ -0,0 +1,7 @@
.dub
docs.json
__dummy.html
*.o
*.obj
__test__*__
examples

View File

@ -0,0 +1,6 @@
{
"name": "examples",
"dependencies": {
"matplotlib-d": {"version": "~master", "path": "../"}
}
}

View File

@ -0,0 +1,84 @@
import std.math;
import std.range;
import std.random;
import std.algorithm;
import plt = matplotlibd.pyplot;
void main() {
simple();
color();
polar();
subplots();
}
void bad() {
plt.plot("hoge");
plt.show();
}
void simple() {
auto x = iota(0, 2.05, 0.05).map!(x => x * PI);
auto y = x.map!(sin);
plt.plot(x, y, "r-", ["label": "$y=sin(x)$"]);
plt.xlim(0, 2 * PI);
plt.ylim(-1, 1);
plt.legend();
plt.savefig("simple.png");
plt.clear();
}
void color() {
const n = 100;
auto x = iota(n);
auto y = x[];
double[n][n] z;
foreach (i; 0..n)
foreach (j; 0..n)
z[i][j] = i + j;
plt.contourf(x, y, z, 64, ["cmap": "hsv"]);
plt.colorbar();
plt.savefig("color.png");
plt.clear();
}
void subplots() {
auto x = iota(0, 2 * PI + 0.05, 0.05);
plt.subplot(221);
plt.plot(x, x.map!(sin));
plt.xlim(0, 2 * PI);
plt.ylim(-1, 1);
plt.subplot(222);
plt.plot(x, x.map!(cos));
plt.xlim(0, 2 * PI);
plt.ylim(-1, 1);
plt.subplot(223);
plt.plot(x, x.map!(i => sin(i) * exp(-0.4 * i)));
plt.xlim(0, 2 * PI);
plt.ylim(-1, 1);
plt.subplot(224);
plt.plot(x, x.map!(i => cos(i) * exp(-0.4 * i)));
plt.xlim(0, 2 * PI);
plt.ylim(-1, 1);
plt.savefig("subplots.png");
plt.clear();
}
void polar() {
auto r = iota(0, 1.001, 0.001);
auto theta = r.map!(i => i * 32 * PI);
auto area = r.map!(i => i * 2000);
plt.subplot(111, ["projection": "polar"]);
plt.scatter(theta, r, ["c": r], ["s": area], ["cmap": "hsv"], ["alpha": 0.25]);
plt.savefig("polar.png");
plt.clear();
}

View File

@ -0,0 +1,38 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
def extract_function_names(module):
'''
extract function names from attributes of 'module'.
'''
from importlib import import_module
mod = import_module(module.__name__)
attr_list = dir(mod)
scope = locals()
def iscallable(name):
ignore_decorators = ['dedent','deprecated','silent_list', 'warn_deprecated']
return eval('callable(mod.{})'.format(name), scope) and name not in ignore_decorators
return filter(iscallable, attr_list)
def gen_pyplot_functions(dub_root):
'''
generate 'pyplot_functions.txt' for matplotlibd.pyplot.
'''
import matplotlib.pyplot
from string import ascii_lowercase
functions = filter(lambda i: i[0] != '_' or i[0] in ascii_lowercase,
extract_function_names(matplotlib.pyplot))
with open(dub_root + "/views/pyplot_functions.txt", "w") as f:
f.write("\n".join(functions))
if __name__ == '__main__':
from sys import argv
gen_pyplot_functions(argv[1])

View File

@ -0,0 +1,26 @@
module matplotlibd.core.pycall;
void call(string py_script) {
import std.process: environment, pipeProcess, wait, Redirect;
auto py_path = environment.get("MATPLOTLIB_D_PYTHON", "python3");
auto pipes = pipeProcess(py_path, Redirect.stdin | Redirect.stderr);
pipes.stdin.writeln(py_script);
pipes.stdin.writeln("exit()");
pipes.stdin.close();
auto result = wait(pipes.pid);
if (result != 0) {
string error;
foreach (line; pipes.stderr.byLine)
error ~= line ~ "\n";
throw new Exception("\n\nERROR occurred in Python:\n" ~ error);
}
}
unittest {
call("print(\"Passing!\")\n");
}

View File

@ -0,0 +1,59 @@
module matplotlibd.core.translate;
alias immutable bool PyBool;
alias immutable (void*) PyNone;
PyBool False = false;
PyBool True = true;
PyNone None = null;
string d2py(T)(T v) {
import std.format: format;
static if (is(typeof(v) : PyNone))
return "None";
else static if (is(typeof(v) : bool))
return v ? "True" : "False";
else static if (is(typeof(v) : string))
return format("\"%s\"", v);
else
return format("%s", v);
}
unittest {
import std.range: iota;
assert(d2py(None) == "None");
assert(d2py(null) == "None");
assert(d2py(True) == "True");
assert(d2py(true) == "True");
assert(d2py(False) == "False");
assert(d2py(false) == "False");
assert(d2py("Hello!") == "\"Hello!\"");
assert(d2py(5.iota) == "[0, 1, 2, 3, 4]");
}
string parseArgs(Args)(Args args) {
static if (is(typeof(args.keys) : string[])) {
string parsed;
foreach(key; args.byKey)
parsed ~= key ~ "=" ~ d2py(args[key]) ~ ",";
}
else
string parsed = d2py(args) ~ ",";
return parsed;
}
unittest {
import std.range: iota;
assert(parseArgs(5) == "5,");
assert(parseArgs(5.iota) == "[0, 1, 2, 3, 4],");
assert(parseArgs(["test": 5]) == "test=5,");
assert(parseArgs(["test": "test"]) == "test=\"test\",");
assert(parseArgs(["test": 5.iota]) == "test=[0, 1, 2, 3, 4],");
assert(parseArgs(["test": false]) == "test=False,");
assert(parseArgs(["test": False]) == "test=False,");
}

View File

@ -0,0 +1,50 @@
module matplotlibd.pyplot;
import matplotlibd.core.pycall;
import matplotlibd.core.translate;
private:
string py_script = "import matplotlib.pyplot as plt\n";
immutable string plt_funcs = (){
import std.string: splitLines;
string[] method_names = import("pyplot_functions.txt").splitLines;
string plt_funcs;
foreach(name; method_names) {
plt_funcs ~=
"void " ~ name ~ "(T...)(T a)" ~
"{import std.format: format;" ~
"string p;if(a.length>0){foreach(i;a){p~=parseArgs(i);}" ~
"p = p[0..$-1];}py_script~=format(\"plt."~ name ~ "(%s)\n\",p);";
if (name == "show" || name == "savefig")
plt_funcs ~= "call(py_script);}\n";
else
plt_funcs ~= "}\n";
}
return plt_funcs;
}();
public:
import matplotlibd.core.translate: False, True, None;
void clear() {
py_script = "import matplotlib.pyplot as plt\n";
}
mixin(plt_funcs);
unittest {
import std.string;
auto script = py_script ~ "plt.plot([1, 2],[2, 4],\"r-\",lw=2)\n";
plot([1, 2], [2, 4], "r-", ["lw": 2]);
assert(script == py_script);
clear();
assert(py_script == "import matplotlib.pyplot as plt\n");
}

View File

@ -3,32 +3,38 @@ import util : make, rcarray, StdArray, StdxArray, EMSIArray;
import std.stdio : stderr, writeln;
import std.datetime : Duration;
void testInsert(Container, int times = 100)()
import std.meta : AliasSeq;
alias tests = AliasSeq!(testInsert, testInsertDelete, testConcat);
alias containers = AliasSeq!(int[], StdArray!int, EMSIArray!int, rcarray!int, StdxArray!int);
enum times = 100000;
void testInsert(Container, size_t size = 100)()
{
auto container = make!Container();
//debug stderr.writeln("Testing inserts on ", typeof(container).stringof);
foreach (i; 0 .. times)
foreach (i; 0 .. size)
{
container ~= 42;
}
assert(container.length == times && container[times - 1] == 42);
assert(container.length == size && container[size - 1] == 42);
}
void testInsertDelete(Container, int times = 100)()
void testInsertDelete(Container, size_t size = 100)()
{
auto container = make!Container();
//debug stderr.writeln("Testing inserts+deletes on ", typeof(container).stringof);
foreach (i; 0 .. times)
foreach (i; 0 .. size)
{
container ~= 42;
}
foreach (i; 0 .. times)
foreach (i; 0 .. size)
{
static if (is(Container : StdxArray!U, U))
{
@ -47,24 +53,33 @@ void testInsertDelete(Container, int times = 100)()
assert(container.length == 0);
}
void testConcat(Container, int times = 100)()
void testConcat(Container, size_t size = 100)()
{
auto a = make!Container();
auto b = make!Container();
foreach (i; 0 .. size / 2)
{
a ~= 1;
b ~= 2;
}
auto c = a ~ b;
assert(c.length == size);
}
import std.meta : AliasSeq;
alias tests = AliasSeq!(testInsert, testInsertDelete, testConcat);
auto testContainers(Containers...)(int times = 100000)
auto testContainers(Containers...)()
{
import std.datetime.stopwatch : benchmark;
import std.meta : staticMap;
import std.array : array;
Duration[][string] results;
static foreach (test; tests)
{
results[test.stringof] = benchmark!(staticMap!(test, Containers))(times);
results[test.stringof] = benchmark!(staticMap!(test, Containers))(times).array;
}
return results;
@ -84,12 +99,40 @@ void printResults(Containers...)(Duration[][string] results)
}
}
void plotResults(Containers...)(Duration[][string] results)
{
import plt = matplotlibd.pyplot;
auto i = 0;
foreach (test, testResults; results)
{
import std.range : iota;
import std.algorithm : map;
import std.array : array;
import std.conv : to;
auto title = test ~ " (" ~ times.to!string ~ " runs)";
auto x = iota(testResults.length);
auto height = testResults.map!(d => d.total!"msecs").array;
string[] names;
foreach (Container; Containers)
{
names ~= Container.stringof;
}
plt.subplot(results.length, 1, ++i);
plt.bar(x, height);
plt.xticks(x, names);
plt.ylabel("Time (ms)");
plt.title(title);
}
plt.show();
}
void main()
{
import std.meta : AliasSeq;
alias containers = AliasSeq!(int[], StdArray!int, StdxArray!int, EMSIArray!int, rcarray!int);
auto results = testContainers!containers;
results.printResults!containers;
results.plotResults!containers;
}