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 }