gumath 0.2.0dev5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (78) hide show
  1. checksums.yaml +7 -0
  2. data/CONTRIBUTING.md +61 -0
  3. data/Gemfile +5 -0
  4. data/History.md +0 -0
  5. data/README.md +5 -0
  6. data/Rakefile +105 -0
  7. data/ext/ruby_gumath/examples.c +126 -0
  8. data/ext/ruby_gumath/extconf.rb +97 -0
  9. data/ext/ruby_gumath/functions.c +106 -0
  10. data/ext/ruby_gumath/gufunc_object.c +79 -0
  11. data/ext/ruby_gumath/gufunc_object.h +55 -0
  12. data/ext/ruby_gumath/gumath/AUTHORS.txt +5 -0
  13. data/ext/ruby_gumath/gumath/INSTALL.txt +42 -0
  14. data/ext/ruby_gumath/gumath/LICENSE.txt +29 -0
  15. data/ext/ruby_gumath/gumath/MANIFEST.in +3 -0
  16. data/ext/ruby_gumath/gumath/Makefile.in +62 -0
  17. data/ext/ruby_gumath/gumath/README.rst +20 -0
  18. data/ext/ruby_gumath/gumath/config.guess +1530 -0
  19. data/ext/ruby_gumath/gumath/config.h.in +52 -0
  20. data/ext/ruby_gumath/gumath/config.sub +1782 -0
  21. data/ext/ruby_gumath/gumath/configure +5049 -0
  22. data/ext/ruby_gumath/gumath/configure.ac +167 -0
  23. data/ext/ruby_gumath/gumath/doc/_static/copybutton.js +66 -0
  24. data/ext/ruby_gumath/gumath/doc/conf.py +26 -0
  25. data/ext/ruby_gumath/gumath/doc/gumath/functions.rst +62 -0
  26. data/ext/ruby_gumath/gumath/doc/gumath/index.rst +26 -0
  27. data/ext/ruby_gumath/gumath/doc/index.rst +45 -0
  28. data/ext/ruby_gumath/gumath/doc/libgumath/data-structures.rst +130 -0
  29. data/ext/ruby_gumath/gumath/doc/libgumath/functions.rst +78 -0
  30. data/ext/ruby_gumath/gumath/doc/libgumath/index.rst +25 -0
  31. data/ext/ruby_gumath/gumath/doc/libgumath/kernels.rst +41 -0
  32. data/ext/ruby_gumath/gumath/doc/releases/index.rst +11 -0
  33. data/ext/ruby_gumath/gumath/install-sh +527 -0
  34. data/ext/ruby_gumath/gumath/libgumath/Makefile.in +170 -0
  35. data/ext/ruby_gumath/gumath/libgumath/Makefile.vc +160 -0
  36. data/ext/ruby_gumath/gumath/libgumath/apply.c +201 -0
  37. data/ext/ruby_gumath/gumath/libgumath/extending/bfloat16.c +130 -0
  38. data/ext/ruby_gumath/gumath/libgumath/extending/examples.c +176 -0
  39. data/ext/ruby_gumath/gumath/libgumath/extending/graph.c +393 -0
  40. data/ext/ruby_gumath/gumath/libgumath/extending/pdist.c +140 -0
  41. data/ext/ruby_gumath/gumath/libgumath/extending/quaternion.c +156 -0
  42. data/ext/ruby_gumath/gumath/libgumath/func.c +177 -0
  43. data/ext/ruby_gumath/gumath/libgumath/gumath.h +205 -0
  44. data/ext/ruby_gumath/gumath/libgumath/kernels/binary.c +547 -0
  45. data/ext/ruby_gumath/gumath/libgumath/kernels/unary.c +449 -0
  46. data/ext/ruby_gumath/gumath/libgumath/nploops.c +219 -0
  47. data/ext/ruby_gumath/gumath/libgumath/tbl.c +223 -0
  48. data/ext/ruby_gumath/gumath/libgumath/thread.c +175 -0
  49. data/ext/ruby_gumath/gumath/libgumath/xndloops.c +130 -0
  50. data/ext/ruby_gumath/gumath/python/extending.py +24 -0
  51. data/ext/ruby_gumath/gumath/python/gumath/__init__.py +74 -0
  52. data/ext/ruby_gumath/gumath/python/gumath/_gumath.c +577 -0
  53. data/ext/ruby_gumath/gumath/python/gumath/examples.c +93 -0
  54. data/ext/ruby_gumath/gumath/python/gumath/functions.c +77 -0
  55. data/ext/ruby_gumath/gumath/python/gumath/pygumath.h +95 -0
  56. data/ext/ruby_gumath/gumath/python/test_gumath.py +405 -0
  57. data/ext/ruby_gumath/gumath/setup.py +298 -0
  58. data/ext/ruby_gumath/gumath/vcbuild/INSTALL.txt +36 -0
  59. data/ext/ruby_gumath/gumath/vcbuild/vcbuild32.bat +21 -0
  60. data/ext/ruby_gumath/gumath/vcbuild/vcbuild64.bat +21 -0
  61. data/ext/ruby_gumath/gumath/vcbuild/vcclean.bat +10 -0
  62. data/ext/ruby_gumath/gumath/vcbuild/vcdistclean.bat +11 -0
  63. data/ext/ruby_gumath/include/gumath.h +205 -0
  64. data/ext/ruby_gumath/include/ruby_gumath.h +41 -0
  65. data/ext/ruby_gumath/lib/libgumath.a +0 -0
  66. data/ext/ruby_gumath/lib/libgumath.so +1 -0
  67. data/ext/ruby_gumath/lib/libgumath.so.0 +1 -0
  68. data/ext/ruby_gumath/lib/libgumath.so.0.2.0dev3 +0 -0
  69. data/ext/ruby_gumath/ruby_gumath.c +295 -0
  70. data/ext/ruby_gumath/ruby_gumath.h +41 -0
  71. data/ext/ruby_gumath/ruby_gumath_internal.h +45 -0
  72. data/ext/ruby_gumath/util.c +68 -0
  73. data/ext/ruby_gumath/util.h +48 -0
  74. data/gumath.gemspec +47 -0
  75. data/lib/gumath.rb +7 -0
  76. data/lib/gumath/version.rb +5 -0
  77. data/lib/ruby_gumath.so +0 -0
  78. metadata +206 -0
@@ -0,0 +1,130 @@
1
+ /*
2
+ * BSD 3-Clause License
3
+ *
4
+ * Copyright (c) 2017-2018, plures
5
+ * All rights reserved.
6
+ *
7
+ * Redistribution and use in source and binary forms, with or without
8
+ * modification, are permitted provided that the following conditions are met:
9
+ *
10
+ * 1. Redistributions of source code must retain the above copyright notice,
11
+ * this list of conditions and the following disclaimer.
12
+ *
13
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
14
+ * this list of conditions and the following disclaimer in the documentation
15
+ * and/or other materials provided with the distribution.
16
+ *
17
+ * 3. Neither the name of the copyright holder nor the names of its
18
+ * contributors may be used to endorse or promote products derived from
19
+ * this software without specific prior written permission.
20
+ *
21
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
25
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31
+ */
32
+
33
+
34
+ #include <stdlib.h>
35
+ #include <stdint.h>
36
+ #include <string.h>
37
+ #include <math.h>
38
+ #include <complex.h>
39
+ #include <inttypes.h>
40
+ #include "ndtypes.h"
41
+ #include "xnd.h"
42
+ #include "gumath.h"
43
+
44
+
45
+ /* Avoid Python.h dependency. */
46
+ struct _object;
47
+ double PyFloat_AsDouble(const struct _object *);
48
+ struct _object *PyFloat_FromDouble (double v);
49
+ struct _object *PyErr_Occurred(void);
50
+
51
+
52
+ /*****************************************************************************/
53
+ /* Initialize a single bfloat16 from a PyObject */
54
+ /*****************************************************************************/
55
+
56
+ /* XXX Maybe rename "init", "repr" to "from_object", "to_object". */
57
+ static bool
58
+ bfloat16_init(void *dest, const void *src, ndt_context_t *ctx)
59
+ {
60
+ const struct _object *v = (const struct _object *)src;
61
+ xnd_t *x = (xnd_t *)dest;
62
+ const ndt_t *t = x->type;
63
+ float f;
64
+ (void)ctx;
65
+
66
+ f = (float)PyFloat_AsDouble(v);
67
+ if (f == -1 && PyErr_Occurred()) {
68
+ return 0;
69
+ }
70
+
71
+ const char *cp = ndt_is_big_endian(t) ? (char *)&f : ((char *)&f)+2;
72
+ memcpy(x->ptr, cp, sizeof(uint16_t));
73
+ return 1;
74
+ }
75
+
76
+ static void *
77
+ bfloat16_repr(const void *src, ndt_context_t *ctx)
78
+ {
79
+ const xnd_t *x = (const xnd_t *)src;
80
+ const ndt_t *t = x->type;
81
+ float f = 0;
82
+ (void)ctx;
83
+
84
+ char *cp = ndt_is_big_endian(t) ? (char *)&f : ((char *)&f)+2;
85
+ memcpy(cp, x->ptr, sizeof(uint16_t));
86
+
87
+ return PyFloat_FromDouble(f);
88
+ }
89
+
90
+
91
+ static const ndt_methods_t bfloat16_methods = {
92
+ .init = bfloat16_init,
93
+ .constraint = NULL,
94
+ .repr = bfloat16_repr,
95
+ };
96
+
97
+ static const gm_typedef_init_t typedefs[] = {
98
+ { .name = "bfloat16", .type = "uint16", .meth=&bfloat16_methods },
99
+ { .name = NULL, .type = NULL, .meth=NULL }
100
+ };
101
+
102
+ static const gm_kernel_init_t kernels[] = {
103
+ { .name = NULL, .sig = NULL }
104
+ };
105
+
106
+
107
+ /****************************************************************************/
108
+ /* Initialize kernel table */
109
+ /****************************************************************************/
110
+
111
+ int
112
+ gm_init_bfloat16_kernels(gm_tbl_t *tbl, ndt_context_t *ctx)
113
+ {
114
+ const gm_typedef_init_t *t;
115
+ const gm_kernel_init_t *k;
116
+
117
+ for (t = typedefs; t->name != NULL; t++) {
118
+ if (ndt_typedef_from_string(t->name, t->type, t->meth, ctx) < 0) {
119
+ return -1;
120
+ }
121
+ }
122
+
123
+ for (k = kernels; k->name != NULL; k++) {
124
+ if (gm_add_kernel(tbl, k, ctx) < 0) {
125
+ return -1;
126
+ }
127
+ }
128
+
129
+ return 0;
130
+ }
@@ -0,0 +1,176 @@
1
+ /*
2
+ * BSD 3-Clause License
3
+ *
4
+ * Copyright (c) 2017-2018, plures
5
+ * All rights reserved.
6
+ *
7
+ * Redistribution and use in source and binary forms, with or without
8
+ * modification, are permitted provided that the following conditions are met:
9
+ *
10
+ * 1. Redistributions of source code must retain the above copyright notice,
11
+ * this list of conditions and the following disclaimer.
12
+ *
13
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
14
+ * this list of conditions and the following disclaimer in the documentation
15
+ * and/or other materials provided with the distribution.
16
+ *
17
+ * 3. Neither the name of the copyright holder nor the names of its
18
+ * contributors may be used to endorse or promote products derived from
19
+ * this software without specific prior written permission.
20
+ *
21
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
25
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31
+ */
32
+
33
+
34
+ #include <stdlib.h>
35
+ #include <stdint.h>
36
+ #include <string.h>
37
+ #include <math.h>
38
+ #include <complex.h>
39
+ #include <inttypes.h>
40
+ #include "ndtypes.h"
41
+ #include "xnd.h"
42
+ #include "gumath.h"
43
+
44
+
45
+ /****************************************************************************/
46
+ /* Xnd kernels */
47
+ /****************************************************************************/
48
+
49
+ /*
50
+ * Count valid/missing values in a 1D array of records and return the result
51
+ * as a record.
52
+ *
53
+ * Signature:
54
+ * "... * N * {index: int64, name: string, value: ?int64} -> ... * {valid: int64, missing: int64}"
55
+ */
56
+ static int
57
+ count_valid_missing(xnd_t stack[], ndt_context_t *ctx)
58
+ {
59
+ const xnd_t *array = &stack[0];
60
+ int64_t N = array->type->FixedDim.shape; /* N in the above signature */
61
+ xnd_t *out = &stack[1];
62
+ int64_t ok = 0;
63
+ int64_t na = 0;
64
+
65
+ for (int64_t i = 0; i < N; i++) {
66
+ const xnd_t record = xnd_fixed_dim_next(array, i);
67
+ const xnd_t value = xnd_record_next(&record, 2, ctx);
68
+ if (value.ptr == NULL) {
69
+ return -1;
70
+ }
71
+
72
+ if (xnd_is_na(&value)) {
73
+ na++;
74
+ }
75
+ else {
76
+ ok++;
77
+ }
78
+ }
79
+
80
+ xnd_t valid = xnd_record_next(out, 0, ctx);
81
+ *(int64_t *)(valid.ptr) = ok;
82
+
83
+ xnd_t missing = xnd_record_next(out, 1, ctx);
84
+ *(int64_t *)(missing.ptr) = na;
85
+
86
+ return 0;
87
+ }
88
+
89
+ int
90
+ gm_0D_add_scalar(xnd_t stack[], ndt_context_t *ctx)
91
+ {
92
+ const xnd_t *x = &stack[0];
93
+ const xnd_t *y = &stack[1];
94
+ xnd_t *z = &stack[2];
95
+ int64_t N = xnd_fixed_shape(x);
96
+ int64_t yy = *(int64_t *)y->ptr;
97
+ (void)ctx;
98
+
99
+ for (int64_t i = 0; i < N; i++) {
100
+ const xnd_t xx = xnd_fixed_dim_next(x, i);
101
+ const xnd_t zz = xnd_fixed_dim_next(z, i);
102
+ *(int64_t *)zz.ptr = *(int64_t *)xx.ptr + yy;
103
+ }
104
+
105
+ return 0;
106
+ }
107
+
108
+ int
109
+ gm_randint(xnd_t stack[], ndt_context_t *ctx)
110
+ {
111
+ const xnd_t *x = &stack[0];
112
+ (void)ctx;
113
+
114
+ *(int32_t *)x->ptr = rand();
115
+ return 0;
116
+ }
117
+
118
+ int
119
+ gm_randtuple(xnd_t stack[], ndt_context_t *ctx)
120
+ {
121
+ const xnd_t *x = &stack[0];
122
+ const xnd_t *y = &stack[1];
123
+ (void)ctx;
124
+
125
+ *(int32_t *)x->ptr = rand();
126
+ *(int32_t *)y->ptr = rand();
127
+ return 0;
128
+ }
129
+
130
+ int
131
+ gm_divmod10(xnd_t stack[], ndt_context_t *ctx)
132
+ {
133
+ const xnd_t *x = &stack[0];
134
+ int64_t xx = *(int64_t *)x->ptr;
135
+ const xnd_t *y = &stack[1];
136
+ const xnd_t *z = &stack[2];
137
+ (void)ctx;
138
+
139
+ *(int64_t *)y->ptr = xx / 10;
140
+ *(int64_t *)z->ptr = xx % 10;
141
+ return 0;
142
+ }
143
+
144
+
145
+ static const gm_kernel_init_t kernels[] = {
146
+ { .name = "add_scalar", .sig = "... * N * int64, ... * int64 -> ... * N * int64", .Xnd = gm_0D_add_scalar },
147
+
148
+ { .name = "count_valid_missing",
149
+ .sig = "... * N * {index: int64, name: string, value: ?int64} -> ... * {valid: int64, missing: int64}",
150
+ .Xnd = count_valid_missing },
151
+
152
+ { .name = "randint", .sig = "void -> int32", .Xnd = gm_randint },
153
+ { .name = "randtuple", .sig = "void -> int32, int32", .Xnd = gm_randtuple },
154
+ { .name = "divmod10", .sig = "int64 -> int64, int64", .Xnd = gm_divmod10 },
155
+
156
+ { .name = NULL, .sig = NULL }
157
+ };
158
+
159
+
160
+ /****************************************************************************/
161
+ /* Initialize kernel table */
162
+ /****************************************************************************/
163
+
164
+ int
165
+ gm_init_example_kernels(gm_tbl_t *tbl, ndt_context_t *ctx)
166
+ {
167
+ const gm_kernel_init_t *k;
168
+
169
+ for (k = kernels; k->name != NULL; k++) {
170
+ if (gm_add_kernel(tbl, k, ctx) < 0) {
171
+ return -1;
172
+ }
173
+ }
174
+
175
+ return 0;
176
+ }
@@ -0,0 +1,393 @@
1
+ /*
2
+ * BSD 3-Clause License
3
+ *
4
+ * Copyright (c) 2017-2018, plures
5
+ * All rights reserved.
6
+ *
7
+ * Redistribution and use in source and binary forms, with or without
8
+ * modification, are permitted provided that the following conditions are met:
9
+ *
10
+ * 1. Redistributions of source code must retain the above copyright notice,
11
+ * this list of conditions and the following disclaimer.
12
+ *
13
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
14
+ * this list of conditions and the following disclaimer in the documentation
15
+ * and/or other materials provided with the distribution.
16
+ *
17
+ * 3. Neither the name of the copyright holder nor the names of its
18
+ * contributors may be used to endorse or promote products derived from
19
+ * this software without specific prior written permission.
20
+ *
21
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
25
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31
+ */
32
+
33
+
34
+ #include <stdlib.h>
35
+ #include <stdint.h>
36
+ #include <string.h>
37
+ #include <math.h>
38
+ #include <complex.h>
39
+ #include <inttypes.h>
40
+ #include "ndtypes.h"
41
+ #include "xnd.h"
42
+ #include "gumath.h"
43
+
44
+
45
+ /*****************************************************************************/
46
+ /* Check graph invariants */
47
+ /*****************************************************************************/
48
+
49
+ static bool
50
+ graph_constraint(const void *graph, ndt_context_t *ctx)
51
+ {
52
+ const xnd_t *g = (xnd_t *)graph;
53
+ int64_t start2, step2; /* start, step of ndim2 (the graph) */
54
+ int64_t start1, step1; /* start, step of ndim1 (an array of edges) */
55
+ int64_t N; /* number of nodes */
56
+
57
+
58
+ N = ndt_var_indices(&start2, &step2, g->type, g->index, ctx);
59
+ if (N < 0) {
60
+ return false;
61
+ }
62
+
63
+ for (int32_t u = 0; u < N; u++) {
64
+ const xnd_t edges = xnd_var_dim_next(g, start2, step2, u);
65
+ const int64_t nedges = ndt_var_indices(&start1, &step1, edges.type,
66
+ edges.index, ctx);
67
+ if (nedges < 0) {
68
+ return false;
69
+ }
70
+
71
+ for (int64_t k = 0; k < nedges; k++) {
72
+ const xnd_t tuple = xnd_var_dim_next(&edges, start1, step1, k);
73
+ const xnd_t target = xnd_tuple_next(&tuple, 0, ctx);
74
+ const int32_t v = *(int32_t *)target.ptr;
75
+
76
+ if (v < 0 || v >= N) {
77
+ ndt_err_format(ctx, NDT_ValueError,
78
+ "node id must be in range [0, N-1]");
79
+ return false;
80
+ }
81
+ }
82
+ }
83
+
84
+ return true;
85
+ }
86
+
87
+
88
+ /*****************************************************************************/
89
+ /* Bellman-Ford single-source shortest-paths algorithm (CLRS p588) */
90
+ /*****************************************************************************/
91
+
92
+
93
+ static const int32_t NIL = INT32_MIN;
94
+
95
+ /*
96
+ * Allocate and initialize distance and predecessor arrays.
97
+ * d := distance array
98
+ * p := predecessor array
99
+ * N := number of nodes
100
+ * u := single source node id
101
+ */
102
+ static int
103
+ init(double **d, int32_t **p, int64_t N, int32_t u, ndt_context_t *ctx)
104
+ {
105
+ *d = ndt_alloc(N, sizeof **d);
106
+ if (*d == NULL) {
107
+ (void)ndt_memory_error(ctx);
108
+ return -1;
109
+ }
110
+
111
+ *p = ndt_alloc(N, sizeof **p);
112
+ if (*p == NULL) {
113
+ ndt_free(*d);
114
+ (void)ndt_memory_error(ctx);
115
+ return -1;
116
+ }
117
+
118
+ for (int64_t i = 0; i < N; i++) {
119
+ (*d)[i] = INFINITY;
120
+ (*p)[i] = NIL;
121
+ }
122
+
123
+ (*d)[u] = 0;
124
+
125
+ return 0;
126
+ }
127
+
128
+ /*
129
+ * d := distance array
130
+ * p := predecessor array
131
+ * u := source node id
132
+ * v := target node id
133
+ * cost := cost of going from source to target
134
+ */
135
+ static inline void
136
+ relax(double d[], int32_t p[], int32_t u, int32_t v, double cost)
137
+ {
138
+ if (d[u] + cost < d[v]) {
139
+ d[v] = d[u] + cost;
140
+ p[v] = u;
141
+ }
142
+ }
143
+
144
+ /*
145
+ * Write the shortest path single_source ==> v to dest.
146
+ * If dest==NULL and dsize==0, return the length of the path.
147
+ *
148
+ * dest := path array
149
+ * p := predecessor array
150
+ * single_source := single source node id
151
+ * v := target node id
152
+ */
153
+ static int64_t
154
+ write_path(int32_t *dest, const int32_t dsize,
155
+ const int32_t p[], const int64_t psize,
156
+ const int32_t s, int32_t v)
157
+ {
158
+ int64_t n = dsize;
159
+
160
+ while (1) {
161
+
162
+ assert(0 <= v && v < psize);
163
+ assert(dest == NULL || n >= 0);
164
+
165
+ if (v == s || p[v] != NIL) {
166
+ n--;
167
+ if (dest != NULL) dest[n] = v;
168
+ if (v == s) break;
169
+ }
170
+ else if (p[v] == NIL) {
171
+ return 0;
172
+ }
173
+
174
+ v = p[v];
175
+ }
176
+
177
+ return dest == NULL ? -n : n;
178
+ }
179
+
180
+ static xnd_t
181
+ mk_return_array(int32_t p[], const int64_t N, const int32_t u,
182
+ ndt_context_t *ctx)
183
+ {
184
+ int32_t *ndim2_offsets = NULL;
185
+ int32_t *ndim1_offsets = NULL;
186
+ ndt_t *t;
187
+ int64_t sum;
188
+ int32_t v;
189
+
190
+ if (N+1 > INT32_MAX) {
191
+ goto offset_overflow;
192
+ }
193
+
194
+ ndim2_offsets = ndt_alloc(2, sizeof *ndim2_offsets);
195
+ if (ndim2_offsets == NULL) {
196
+ (void)ndt_memory_error(ctx);
197
+ return xnd_error;
198
+ }
199
+ ndim2_offsets[0] = 0;
200
+ ndim2_offsets[1] = (int32_t)N;
201
+
202
+
203
+ ndim1_offsets = ndt_alloc(N+1, sizeof *ndim1_offsets);
204
+ if (ndim1_offsets == NULL) {
205
+ (void)ndt_memory_error(ctx);
206
+ return xnd_error;
207
+ }
208
+
209
+ sum = 0;
210
+ for (v = 0; v < N; v++) {
211
+ ndim1_offsets[v] = (int32_t)sum;
212
+ int64_t n = write_path(NULL, 0, p, N, u, v);
213
+ sum += n;
214
+ if (sum > INT32_MAX) {
215
+ goto offset_overflow;
216
+ }
217
+ }
218
+ ndim1_offsets[v] = (int32_t)sum;
219
+
220
+
221
+ t = ndt_from_string("node", ctx);
222
+ if (t == NULL) {
223
+ goto error;
224
+ }
225
+
226
+ t = ndt_var_dim(t, InternalOffsets, (int32_t)(N+1), ndim1_offsets, 0, NULL, ctx);
227
+ ndim1_offsets = NULL;
228
+ if (t == NULL) {
229
+ goto error;
230
+ }
231
+
232
+ t = ndt_var_dim(t, InternalOffsets, 2, ndim2_offsets, 0, NULL, ctx);
233
+ ndim2_offsets = NULL;
234
+ if (t == NULL) {
235
+ goto error;
236
+ }
237
+
238
+ xnd_master_t *x = xnd_empty_from_type(t, XND_OWN_EMBEDDED, ctx);
239
+ if (x == NULL) {
240
+ goto error;
241
+ }
242
+ xnd_t out = x->master;
243
+ ndt_free(x);
244
+
245
+ t = out.type->VarDim.type;
246
+ for (v = 0; v < N; v++) {
247
+ int32_t shape = t->Concrete.VarDim.offsets[v+1]-t->Concrete.VarDim.offsets[v];
248
+ char *ptr = out.ptr + t->Concrete.VarDim.offsets[v] * t->Concrete.VarDim.itemsize;
249
+ (void)write_path((int32_t *)ptr, shape, p, N, u, v);
250
+ }
251
+
252
+ return out;
253
+
254
+ error:
255
+ ndt_free(ndim2_offsets);
256
+ ndt_free(ndim1_offsets);
257
+ return xnd_error;
258
+
259
+ offset_overflow:
260
+ ndt_err_format(ctx, NDT_ValueError, "overflow in int32_t offsets");
261
+ goto error;
262
+ }
263
+
264
+ static int
265
+ shortest_path(xnd_t stack[], ndt_context_t *ctx)
266
+ {
267
+ const int32_t single_source = *(int32_t *)stack[1].ptr; /* start node */
268
+ int64_t start2, step2; /* start, step of ndim2 (the graph) */
269
+ int64_t start1, step1; /* start, step of ndim1 (an array of edges) */
270
+ double *d; /* distance array */
271
+ int32_t *p; /* predecessor array */
272
+ int64_t N; /* number of nodes */
273
+
274
+ /* graph in adjacency list representation */
275
+ const xnd_t graph = xnd_nominal_next(&stack[0], ctx);
276
+ if (graph.ptr == NULL) {
277
+ return -1;
278
+ }
279
+
280
+ N = ndt_var_indices(&start2, &step2, graph.type, graph.index, ctx);
281
+ if (N < 0) {
282
+ return -1;
283
+ }
284
+
285
+ if (init(&d, &p, N, single_source, ctx) < 0) {
286
+ return -1;
287
+ }
288
+
289
+ for (int64_t i = 0; i < N-1; i++) {
290
+ for (int32_t u = 0; u < N; u++) {
291
+ const xnd_t edges = xnd_var_dim_next(&graph, start2, step2, u);
292
+ const int64_t nedges = ndt_var_indices(&start1, &step1, edges.type,
293
+ edges.index, ctx);
294
+ if (nedges < 0) {
295
+ return -1;
296
+ }
297
+
298
+ for (int64_t k = 0; k < nedges; k++) {
299
+ const xnd_t tuple = xnd_var_dim_next(&edges, start1, step1, k);
300
+ const xnd_t target = xnd_tuple_next(&tuple, 0, ctx);
301
+ const xnd_t uvcost = xnd_tuple_next(&tuple, 1, ctx);
302
+
303
+ const int32_t v = *(int32_t *)target.ptr;
304
+ const double cost = *(double *)uvcost.ptr;
305
+
306
+ relax(d, p, u, v, cost);
307
+ }
308
+ }
309
+ }
310
+
311
+ for (int32_t u = 0; u < N; u++) {
312
+ const xnd_t edges = xnd_var_dim_next(&graph, start2, step2, u);
313
+ const int64_t nedges = ndt_var_indices(&start1, &step1, edges.type,
314
+ edges.index, ctx);
315
+ if (nedges < 0) {
316
+ return -1;
317
+ }
318
+
319
+ for (int64_t k = 0; k < nedges; k++) {
320
+ const xnd_t tuple = xnd_var_dim_next(&edges, start1, step1, k);
321
+ const xnd_t target = xnd_tuple_next(&tuple, 0, ctx);
322
+ const xnd_t uvcost = xnd_tuple_next(&tuple, 1, ctx);
323
+
324
+ const int32_t v = *(int32_t *)target.ptr;
325
+ const double cost = *(double *)uvcost.ptr;
326
+
327
+ if (d[u] + cost < d[v]) {
328
+ ndt_err_format(ctx, NDT_ValueError,
329
+ "graph contains a negative weight cycle");
330
+ ndt_free(d);
331
+ ndt_free(p);
332
+ return -1;
333
+ }
334
+ }
335
+ }
336
+
337
+ ndt_free(d);
338
+
339
+ /* Push return value (possibly xnd_error) onto the stack. */
340
+ stack[2] = mk_return_array(p, N, single_source, ctx);
341
+
342
+ ndt_free(p);
343
+
344
+ return stack[2].ptr == NULL ? -1 : 0;
345
+ }
346
+
347
+
348
+ static const ndt_methods_t graph_methods = {
349
+ .init = NULL,
350
+ .constraint = graph_constraint,
351
+ .repr = NULL
352
+ };
353
+
354
+ static const gm_typedef_init_t typedefs[] = {
355
+ { .name = "node", .type = "int32", .meth=NULL, },
356
+ { .name = "cost", .type = "float64", .meth=NULL },
357
+ { .name = "graph", .type = "var * var * (node, cost)", .meth=&graph_methods },
358
+ { .name = NULL, .type = NULL, .meth=NULL }
359
+ };
360
+
361
+ static const gm_kernel_init_t kernels[] = {
362
+ { .name = "single_source_shortest_paths",
363
+ .sig = "graph, node -> var * var * node",
364
+ .Xnd = shortest_path },
365
+
366
+ { .name = NULL, .sig = NULL }
367
+ };
368
+
369
+
370
+ /****************************************************************************/
371
+ /* Initialize kernel table */
372
+ /****************************************************************************/
373
+
374
+ int
375
+ gm_init_graph_kernels(gm_tbl_t *tbl, ndt_context_t *ctx)
376
+ {
377
+ const gm_typedef_init_t *t;
378
+ const gm_kernel_init_t *k;
379
+
380
+ for (t = typedefs; t->name != NULL; t++) {
381
+ if (ndt_typedef_from_string(t->name, t->type, t->meth, ctx) < 0) {
382
+ return -1;
383
+ }
384
+ }
385
+
386
+ for (k = kernels; k->name != NULL; k++) {
387
+ if (gm_add_kernel(tbl, k, ctx) < 0) {
388
+ return -1;
389
+ }
390
+ }
391
+
392
+ return 0;
393
+ }