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 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments 8 Authors: Ilya Yaroshenko 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 @optmath: 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: optmath; 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 public import mir.interpolate: atInterval; 75 76 /++ 77 Constructs multivariate generic interpolant with nodes on rectilinear grid. 78 79 Params: 80 grid = `x` values for interpolant 81 values = `f(x)` values for interpolant 82 83 Constraints: 84 `grid`, `values` must have the same length >= 1 85 86 Returns: $(LREF Generic) 87 +/ 88 Generic!(X, F) generic(X, F) 89 (Slice!(RCI!(immutable X)) grid, Slice!(RCI!(const F)) values) 90 { 91 return typeof(return)(forward!grid, values.move); 92 } 93 94 /++ 95 Multivariate generic interpolant with nodes on rectilinear grid. 96 +/ 97 struct Generic(X, F) 98 { 99 @optmath: 100 101 /// Aligned buffer allocated with `mir.internal.memory`. $(RED For internal use.) 102 Slice!(RCI!(const F)) _data; 103 /// Grid iterators. $(RED For internal use.) 104 RCI!(immutable X) _grid; 105 106 extern(D): 107 108 /++ 109 +/ 110 this(Slice!(RCI!(immutable X)) grid, Slice!(RCI!(const F)) data) @safe @nogc 111 { 112 import core.lifetime: move; 113 enum msg_min = "generic interpolant: minimal allowed length for the grid equals 2."; 114 enum msg_eq = "generic interpolant: X length and Y values length + 1 should be equal."; 115 version(D_Exceptions) 116 { 117 static immutable exc_min = new Exception(msg_min); 118 static immutable exc_eq = new Exception(msg_eq); 119 } 120 if (grid.length < 2) 121 { 122 version(D_Exceptions) throw exc_min; 123 else assert(0, msg_min); 124 } 125 if (grid.length != data._lengths[0] + 1) 126 { 127 version(D_Exceptions) throw exc_eq; 128 else assert(0, msg_eq); 129 } 130 _grid = move(grid._iterator); 131 _data = move(data); 132 } 133 134 @trusted: 135 136 /// 137 Generic lightConst()() const @property { return *cast(Generic*)&this; } 138 139 /// 140 Slice!(RCI!(immutable X)) grid(size_t dimension = 0)() scope return const @property 141 if (dimension == 0) 142 { 143 return _grid.lightConst.sliced(_data._lengths[0]); 144 } 145 146 /// 147 immutable(X)[] gridScopeView(size_t dimension = 0)() scope return const @property @trusted 148 if (dimension == 0) 149 { 150 return _grid._iterator[0 .. _data._lengths[0]]; 151 } 152 153 /++ 154 Returns: intervals count. 155 +/ 156 size_t intervalCount(size_t dimension = 0)() scope const @property 157 if (dimension == 0) 158 { 159 assert(_data._lengths[0] > 1); 160 return _data._lengths[0] - 0; 161 } 162 163 /// 164 size_t[1] gridShape()() scope const @property 165 { 166 return _data.shape; 167 } 168 169 /// 170 enum uint derivativeOrder = F.derivativeOrder; 171 172 /// 173 template opCall(uint derivative = 0) 174 if (derivative == 0) 175 { 176 /++ 177 `(x)` operator. 178 Complexity: 179 `O(log(grid.length))` 180 +/ 181 auto opCall(X)(in X x) const 182 { 183 return opCall!(X)(RefTuple!(size_t, X)(this.findInterval(x), x)); 184 } 185 186 /// 187 auto opCall(X)(RefTuple!(size_t, X) tuple) const 188 { 189 X x = tuple[1]; 190 size_t idx = tuple[0]; 191 return _data[idx].opCall!derivative(_grid[idx], _grid[idx + 1], x); 192 } 193 } 194 }