1 module dnum.tensor;
2 
3 // =============================================================================
4 // Enum Section
5 // =============================================================================
6 /++
7   Shape Enum - Row or Col
8 +/
9 enum Shape {
10   Row,
11   Col
12 }
13 
14 /++
15   Range Struct - start, end
16 +/
17 struct Range {
18   double start;
19   double end;
20   double step = 1;
21 
22   this(double x, double y) {
23     this.start = x;
24     this.end = y;
25   }
26 
27   this(double x, double y, double step) {
28     this.start = x;
29     this.end = y;
30     this.step = step;
31   }
32 
33   Tensor toTensor(Shape byRow = Shape.Row) {
34     return Tensor(this, byRow);
35   }
36 }
37 
38 /++
39   Size Struct - row, col
40 +/
41 struct Size {
42   int row;
43   int col;
44 
45   this(int x, int y) {
46     this.row = x;
47     this.col = y;
48   }
49 }
50 
51 // =============================================================================
52 // Main Section
53 // =============================================================================
54 /++
55   Tensor - Lightweight Numerical Structrue
56 +/
57 struct Tensor {
58   /++
59     Main Components
60   +/
61   double[][] data;
62 
63   /++
64     Constructor of single row or column tensor
65   +/
66   this(double[] vec, Shape byRow = Shape.Row) {
67     switch (byRow) with (Shape) {
68       case Row:
69         this.data.length = 1;
70         this.data[0] = vec[]; // Copy
71         break;
72       default:
73         this.data.length = vec.length;
74         foreach (i, ref rows; this.data) {
75           rows.length = 1;
76           rows[0] = vec[i];
77         }
78         break;
79     }
80   }
81 
82   /++
83     Constructor with array of array
84   +/
85   this(double[][] mat) {
86     this.data.length = mat.length;
87     foreach (i, ref rows; this.data) {
88       rows = mat[i][]; // Copy, not ref
89     }
90   }
91 
92   /++
93     Initialize with single number
94   +/
95   this(double val, long row, long col) {
96     this.data.length = row;
97     foreach (i, ref rows; this.data) {
98       rows.length = col;
99       foreach (j; 0 .. col) {
100         rows[j] = val;
101       }
102     }
103   }
104 
105   /++
106     Empty Matrix
107   +/
108   this(long row, long col) {
109     this.data.length = row;
110     foreach (i, ref rows; this.data) {
111       rows.length = col;
112     }
113   }
114 
115   /++
116     Copy Matrix
117   +/
118   this(Tensor m) {
119     this.data.length = m.nrow;
120     foreach (i, ref rows; this.data) {
121       rows.length = m.ncol;
122       foreach (j, ref elem; rows) {
123         elem = m[i, j];
124       }
125     }
126   }
127 
128   /++
129     Range to Tensor
130   +/
131   this(Range r, Shape byRow = Shape.Row) {
132     auto factor = (r.end - r.start) / r.step;
133     auto l = cast(int)(factor + 1);
134     double[] vec;
135     vec.length = l;
136 
137     foreach (i; 0 .. l) {
138       vec[i] = r.start + i * r.step;
139     }
140 
141     switch (byRow) with (Shape) {
142       case Row:
143         this.data.length = 1;
144         this.data[0] = vec[]; // Copy
145         break;
146       default:
147         this.data.length = l;
148         foreach (i, ref rows; this.data) {
149           rows.length = 1;
150           rows[0] = vec[i];
151         }
152         break;
153     }
154   }
155 
156   /++
157     Return row (Same as R)
158   +/
159   pure auto nrow() {
160     return this.data.length;
161   }
162 
163   /++
164     Return col (Same as R)
165   +/
166   pure auto ncol() {
167     return this.data[0].length;
168   }
169 
170   // =========================================================================
171   // Operator Overloading
172   // =========================================================================
173   /++
174     Getter
175   +/
176   pure double opIndex(size_t i, size_t j) const {
177     return this.data[i][j];
178   }
179 
180   /++
181     Setter
182   +/
183   void opIndexAssign(double value, size_t i, size_t j) {
184     this.data[i][j] = value;
185   }
186 
187   /++
188     opIndex with Slice
189   +/
190   Tensor opIndex(Range x, Range y) {
191     long idiff = cast(long)(x.end - x.start);
192     long jdiff = cast(long)(y.end - y.start);
193     auto result = Tensor(idiff, jdiff);
194     foreach (i; 0 .. idiff) {
195       foreach (j; 0 .. jdiff) {
196         result[i, j] = this.data[cast(long)x.start + i][cast(long)y.start + j];
197       }
198     }
199     return result;
200   }
201 
202   /++
203     opSlice
204   +/
205   long[2] opSlice(size_t dim)(long start, long end) {
206     return [start, end];
207   }
208 
209   /++
210     Unary Operator
211   +/
212   Tensor opUnary(string op)() {
213     auto temp = Tensor(this.nrow, this.ncol);
214 
215     switch (op) {
216     case "-":
217       foreach (i, ref rows; temp.data) {
218         pure auto memrow = this.data[i][];
219         foreach (j, ref elem; rows) {
220           elem = -memrow[j];
221         }
222       }
223       break;
224     default:
225       break;
226     }
227     return temp;
228   }
229 
230   /++
231     Binary Operator with Scalar
232   +/
233   Tensor opBinary(string op)(double rhs) {
234     auto temp = Tensor(this.nrow, this.ncol);
235 
236     switch (op) {
237     case "+":
238       foreach (i, ref rows; temp.data) {
239         auto memrow = this.data[i][];
240         foreach (j, ref elem; rows) {
241           elem = memrow[j] + rhs;
242         }
243       }
244       break;
245     case "-":
246       foreach (i, ref rows; temp.data) {
247         pure auto memrow = this.data[i][];
248         foreach (j, ref elem; rows) {
249           elem = memrow[j] - rhs;
250         }
251       }
252       break;
253     case "*":
254       foreach (i, ref rows; temp.data) {
255         pure auto memrow = this.data[i][];
256         foreach (j, ref elem; rows) {
257           elem = memrow[j] * rhs;
258         }
259       }
260       break;
261     case "/":
262       foreach (i, ref rows; temp.data) {
263         pure auto memrow = this.data[i][];
264         foreach (j, ref elem; rows) {
265           elem = memrow[j] / rhs;
266         }
267       }
268       break;
269     case "^^":
270       foreach (i, ref rows; temp.data) {
271         pure auto memrow = this.data[i][];
272         foreach (j, ref elem; rows) {
273           elem = memrow[j] ^^ rhs;
274         }
275       }
276       break;
277     default:
278       break;
279     }
280     return temp;
281   }
282 
283   /++
284         Binary Operator (Right) with Scalar
285     +/
286   Tensor opBinaryRight(string op)(double lhs) {
287     auto temp = Tensor(this.nrow, this.ncol);
288 
289     switch (op) {
290     case "+":
291       foreach (i, ref rows; temp.data) {
292         auto memrow = this.data[i][];
293         foreach (j, ref elem; rows) {
294           elem = memrow[j] + lhs;
295         }
296       }
297       break;
298     case "-":
299       foreach (i, ref rows; temp.data) {
300         pure auto memrow = this.data[i][];
301         foreach (j, ref elem; rows) {
302           elem = lhs - memrow[j];
303         }
304       }
305       break;
306     case "*":
307       foreach (i, ref rows; temp.data) {
308         pure auto memrow = this.data[i][];
309         foreach (j, ref elem; rows) {
310           elem = memrow[j] * lhs;
311         }
312       }
313       break;
314     case "/":
315       foreach (i, ref rows; temp.data) {
316         pure auto memrow = this.data[i][];
317         foreach (j, ref elem; rows) {
318           elem = lhs / memrow[j];
319         }
320       }
321       break;
322     case "^^":
323       foreach (i, ref rows; temp.data) {
324         pure auto memrow = this.data[i][];
325         foreach (j, ref elem; rows) {
326           elem = memrow[j] ^^ lhs;
327         }
328       }
329       break;
330     default:
331       break;
332     }
333     return temp;
334   }
335 
336   /++
337         Binary Operator with Tensor
338     +/
339   Tensor opBinary(string op)(Tensor rhs) {
340     auto temp = Tensor(this.nrow, this.ncol);
341 
342     switch (op) {
343     case "+":
344       foreach (i, ref rows; temp.data) {
345         pure auto memrow1 = this.data[i][];
346         pure auto memrow2 = rhs.data[i][];
347         foreach (j, ref elem; rows) {
348           elem = memrow1[j] + memrow2[j];
349         }
350       }
351       break;
352     case "-":
353       foreach (i, ref rows; temp.data) {
354         pure auto memrow1 = this.data[i][];
355         pure auto memrow2 = rhs.data[i][];
356         foreach (j, ref elem; rows) {
357           elem = memrow1[j] - memrow2[j];
358         }
359       }
360       break;
361     case "*":
362       foreach (i, ref rows; temp.data) {
363         pure auto memrow1 = this.data[i][];
364         pure auto memrow2 = rhs.data[i][];
365         foreach (j, ref elem; rows) {
366           elem = memrow1[j] * memrow2[j];
367         }
368       }
369       break;
370     case "/":
371       foreach (i, ref rows; temp.data) {
372         pure auto memrow1 = this.data[i][];
373         pure auto memrow2 = rhs.data[i][];
374         foreach (j, ref elem; rows) {
375           elem = memrow1[j] / memrow2[j];
376         }
377       }
378       break;
379     case "%":
380       temp = Tensor(this.nrow, rhs.ncol);
381       foreach (i, ref rows; temp.data) {
382         pure auto memrow1 = this.data[i][];
383         foreach (j, ref elem; rows) {
384           elem = 0;
385           foreach (k; 0 .. this.ncol) {
386             elem += memrow1[k] * rhs.data[k][j];
387           }
388         }
389       }
390       break;
391     default:
392       break;
393     }
394     return temp;
395   }
396 
397   // =========================================================================
398   // Operators (Utils)
399   // =========================================================================
400   /++
401     Function apply whole tensor
402   +/
403   Tensor fmap(double delegate(double) f) {
404     Tensor temp = Tensor(this.nrow, this.ncol);
405     foreach (i, ref rows; temp.data) {
406       pure auto memrow1 = this.data[i][];
407       foreach (j, ref elem; rows) {
408         elem = f(memrow1[j]);
409       }
410     }
411     return temp;
412   }
413 
414   /++
415     Function apply whole tensor - parallel - choose row or column which contain many components
416   +/
417   Tensor pmap(double delegate(double) f, Shape byRow = Shape.Col) {
418     import std.parallelism : taskPool;
419     Tensor temp = Tensor(this.nrow, this.ncol);
420 
421     switch (byRow) with (Shape) {
422       case Row:
423         foreach (i, ref rows; taskPool.parallel(temp.data)) {
424           pure auto memrow1 = this.data[i][];
425           foreach (j, ref elem; rows) {
426             elem = f(memrow1[j]);
427           }
428         }
429         break;
430       default:
431         foreach (i, ref rows; temp.data) {
432           pure auto memrow1 = this.data[i][];
433           foreach (j, ref elem; taskPool.parallel(rows)) {
434             elem = f(memrow1[j]);
435           }
436         }
437         break;
438     }
439     return temp;
440   }
441 
442   /++
443     Transpose
444   +/
445   Tensor transpose() {
446     Tensor temp = Tensor(this.ncol, this.nrow);
447     foreach (i, ref rows; temp.data) {
448       foreach (j, ref elem; rows) {
449         elem = this.data[j][i];
450       }
451     }
452     return temp;
453   }
454 
455   /++
456     Alias for Transpose
457   +/
458   Tensor t() {
459     return this.transpose;
460   }
461 }