1 /++ 2 $(H2 Generic Piecewise Interpolant) 3 4 See_also: $(REF_ALTTEXT $(TT interp1), interp1, mir, interpolate) 5 6 License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0) 7 Copyright: 2020 Ilia Ki, Kaleidic Associates Advisory Limited, Symmetry Investments 8 Authors: Ilia Ki 9 10 Macros: 11 SUBREF = $(REF_ALTTEXT $(TT $2), $2, mir, interpolate, $1)$(NBSP) 12 T2=$(TR $(TDNW $(LREF $1)) $(TD $+)) 13 +/ 14 module mir.interpolate.generic; 15 16 @fmamath: 17 18 /// 19 version(mir_test) 20 @safe pure @nogc unittest 21 { 22 import mir.ndslice; 23 import mir.math.common: approxEqual; 24 25 struct PieceInterpolant 26 { 27 int value; 28 29 this()(int value) 30 { 31 this.value = value; 32 } 33 34 int opCall(uint derivative : 0, X)(int x0, int x1, X x) const 35 { 36 return value; 37 } 38 39 enum uint derivativeOrder = 0; 40 } 41 42 alias S = PieceInterpolant; 43 static immutable x = [0, 1, 2, 3]; // can be also an array of floating point numbers 44 static immutable y = [S(10), S(20), S(30)]; 45 46 auto interpolant = generic(x.rcslice, y.rcslice!(const S)); 47 48 assert(interpolant(-1) == 10); 49 assert(interpolant(0) == 10); 50 assert(interpolant(0.5) == 10); 51 52 assert(interpolant(1) == 20); 53 assert(interpolant(1.5) == 20); 54 55 assert(interpolant(2) == 30); 56 assert(interpolant(3) == 30); 57 assert(interpolant(3.4) == 30); 58 assert(interpolant(3) == 30); 59 assert(interpolant(4) == 30); 60 } 61 62 63 import core.lifetime: move; 64 import mir.internal.utility; 65 import mir.functional; 66 import mir.interpolate; 67 import mir.math.common: fmamath; 68 import mir.ndslice.slice; 69 import mir.primitives; 70 import mir.rc.array; 71 import mir.utility: min, max; 72 import std.meta: AliasSeq, staticMap; 73 import std.traits; 74 75 /// 76 public import mir.interpolate: atInterval; 77 78 /++ 79 Constructs multivariate generic interpolant with nodes on rectilinear grid. 80 81 Params: 82 grid = `x` values for interpolant 83 values = `f(x)` values for interpolant 84 85 Constraints: 86 `grid`, `values` must have the same length >= 1 87 88 Returns: $(LREF Generic) 89 +/ 90 Generic!(X, F) generic(X, F) 91 (Slice!(RCI!(immutable X)) grid, Slice!(RCI!(const F)) values) 92 { 93 return typeof(return)(forward!grid, values.move); 94 } 95 96 /++ 97 Multivariate generic interpolant with nodes on rectilinear grid. 98 +/ 99 struct Generic(X, F) 100 { 101 @fmamath: 102 103 /// Aligned buffer allocated with `mir.internal.memory`. $(RED For internal use.) 104 Slice!(RCI!(const F)) _data; 105 /// Grid iterators. $(RED For internal use.) 106 RCI!(immutable X) _grid; 107 108 bool opEquals()(auto ref scope const typeof(this) rhs) scope const @trusted pure nothrow @nogc 109 { 110 if (rhs._data != this._data) 111 return false; 112 static foreach (d; 0 .. 1) 113 if (gridScopeView!d != rhs.gridScopeView!d) 114 return false; 115 return true; 116 } 117 118 extern(D): 119 120 /++ 121 +/ 122 this(Slice!(RCI!(immutable X)) grid, Slice!(RCI!(const F)) data) @safe @nogc 123 { 124 import core.lifetime: move; 125 enum msg_min = "generic interpolant: minimal allowed length for the grid equals 2."; 126 enum msg_eq = "generic interpolant: X length and Y values length + 1 should be equal."; 127 version(D_Exceptions) 128 { 129 static immutable exc_min = new Exception(msg_min); 130 static immutable exc_eq = new Exception(msg_eq); 131 } 132 if (grid.length < 2) 133 { 134 version(D_Exceptions) { import mir.exception : toMutable; throw exc_min.toMutable; } 135 else assert(0, msg_min); 136 } 137 if (grid.length != data._lengths[0] + 1) 138 { 139 version(D_Exceptions) { import mir.exception : toMutable; throw exc_eq.toMutable; } 140 else assert(0, msg_eq); 141 } 142 _grid = move(grid._iterator); 143 _data = move(data); 144 } 145 146 @trusted: 147 148 /// 149 Generic lightConst()() const @property { return *cast(Generic*)&this; } 150 151 /// 152 Slice!(RCI!(immutable X)) grid(size_t dimension = 0)() return scope const @property 153 if (dimension == 0) 154 { 155 return _grid.lightConst.sliced(_data._lengths[0]); 156 } 157 158 /// 159 immutable(X)[] gridScopeView(size_t dimension = 0)() return scope const @property @trusted 160 if (dimension == 0) 161 { 162 return _grid._iterator[0 .. _data._lengths[0]]; 163 } 164 165 /++ 166 Returns: intervals count. 167 +/ 168 size_t intervalCount(size_t dimension = 0)() scope const @property 169 if (dimension == 0) 170 { 171 assert(_data._lengths[0] > 1); 172 return _data._lengths[0] - 0; 173 } 174 175 /// 176 size_t[1] gridShape()() scope const @property 177 { 178 return _data.shape; 179 } 180 181 /// 182 enum uint derivativeOrder = F.derivativeOrder; 183 184 /// 185 template opCall(uint derivative = 0) 186 if (derivative == 0) 187 { 188 /++ 189 `(x)` operator. 190 Complexity: 191 `O(log(grid.length))` 192 +/ 193 auto opCall(X)(in X x) const 194 { 195 return opCall!(X)(Tuple!(size_t, X)(this.findInterval(x), x)); 196 } 197 198 /// 199 auto opCall(X)(Tuple!(size_t, X) tuple) const 200 { 201 X x = tuple[1]; 202 size_t idx = tuple[0]; 203 return _data[idx].opCall!derivative(_grid[idx], _grid[idx + 1], x); 204 } 205 } 206 }