Skip to content

Commit b231e60

Browse files
authored
add OrthogonalReduceField (#144)
1 parent 79534a4 commit b231e60

File tree

2 files changed

+130
-2
lines changed

2 files changed

+130
-2
lines changed

source/mir/ndslice/field.d

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ $(T2 LinspaceField, $(SUBREF topology, linspace))
1313
$(T2 MagicField, $(SUBREF topology, magic))
1414
$(T2 MapField, $(SUBREF topology, map))
1515
$(T2 ndIotaField, $(SUBREF topology, ndiota))
16+
$(T2 OrthogonalReduceField, $(SUBREF topology, orthogonalReduceField))
1617
$(T2 RepeatField, $(SUBREF topology, repeat))
1718
)
1819
@@ -302,6 +303,47 @@ unittest
302303
assert(packed[2] == 5);
303304
}
304305

306+
///
307+
struct OrthogonalReduceField(FieldsIterator, alias fun)
308+
{
309+
import mir.ndslice.slice: Slice;
310+
311+
@optmath:
312+
/// non empty slice
313+
314+
Slice!FieldsIterator _fields;
315+
316+
///
317+
auto lightConst()() const @property
318+
{
319+
auto fields = _fields.lightConst;
320+
return OrthogonalReduceField!(fields.Iterator, fun)(fields);
321+
}
322+
323+
///
324+
auto lightImmutable()() immutable @property
325+
{
326+
auto fields = _fields.lightImmutable;
327+
return OrthogonalReduceField!(fields.Iterator, fun)(fields);
328+
}
329+
330+
/// `r = fun(r, fields[i][index]);` reduction by `i`
331+
auto opIndex()(size_t index) const
332+
{
333+
assert(_fields.length);
334+
auto fields = _fields.lightConst;
335+
Unqual!(typeof(fun(fields.front[index], fields.front[index]))) r = fields.front[index];
336+
for(;;)
337+
{
338+
fields.popFront;
339+
if (fields.empty)
340+
break;
341+
r = fun(r, fields.front[index]);
342+
}
343+
return r;
344+
}
345+
}
346+
305347
/++
306348
`ndIotaField` is used by $(SUBREF topology, ndiota).
307349
+/
@@ -324,6 +366,7 @@ struct ndIotaField(size_t N)
324366
return ndIotaField!N(_lengths);
325367
}
326368

369+
///
327370
size_t[N] opIndex()(size_t index) const
328371
{
329372
size_t[N] indexes;

source/mir/ndslice/topology.d

Lines changed: 87 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ $(T2 as, Convenience function that creates a lazy view,
3838
where each element of the original slice is converted to a type `T`.)
3939
$(T2 bitpack, Bitpack slice over an unsigned integral slice.)
4040
$(T2 bitwise, Bitwise slice over an unsigned integral slice.)
41+
$(T2 bitwiseField, Bitwise field over an unsigned integral field.)
4142
$(T2 bytegroup, Groups existing slice into fixed length chunks and uses them as data store for destination type.)
4243
$(T2 cached, Random access cache. It is usefull in combiation with $(LREF map) and $(LREF vmap).)
4344
$(T2 cachedGC, Random access cache auto-allocated in GC heap. It is usefull in combiation with $(LREF map) and $(LREF vmap).)
@@ -46,6 +47,7 @@ $(T2 flattened, Contiguous 1-dimensional slice of all elements of a slice.)
4647
$(T2 map, Multidimensional functional map.)
4748
$(T2 mapSubSlices, Maps indexes pairs to subslices.)
4849
$(T2 member, Field (element's member) projection.)
50+
$(T2 orthogonalReduceField, Functional deep-element wise reduce of a slice composed of fields or iterators.)
4951
$(T2 pairwise, Pairwise map for vectors.)
5052
$(T2 pairwiseMapSubSlices, Maps pairwise indexes pairs to subslices.)
5153
$(T2 retro, Reverses order of iteration for all dimensions.)
@@ -2087,6 +2089,18 @@ version(mir_test) unittest
20872089
assert(bits[100] == false);
20882090
}
20892091

2092+
/++
2093+
Bitwise field over an integral field.
2094+
Params:
2095+
field = an integral field.
2096+
Returns: A bitwise field.
2097+
+/
2098+
auto bitwiseField(Field)(Field field)
2099+
if (isIntegral!(typeof(Field.init[size_t.init])))
2100+
{
2101+
return BitwiseField!Field(field);
2102+
}
2103+
20902104
/++
20912105
Bitpack slice over an integral slice.
20922106
@@ -2275,7 +2289,6 @@ template map(fun...)
22752289
auto map(Iterator, size_t N, SliceKind kind)
22762290
(Slice!(Iterator, N, kind) slice)
22772291
{
2278-
import mir.ndslice.iterator: mapIterator;
22792292
auto iterator = mapIterator!f(slice._iterator);
22802293
return Slice!(typeof(iterator), N, kind)(slice._lengths, slice._strides, iterator);
22812294
}
@@ -2448,7 +2461,6 @@ See_Also:
24482461
@optmath auto vmap(Iterator, size_t N, SliceKind kind, Callable)
24492462
(Slice!(Iterator, N, kind) slice, auto ref Callable callable)
24502463
{
2451-
import mir.ndslice.iterator: VmapIterator;
24522464
alias It = VmapIterator!(Iterator, Callable);
24532465
return Slice!(It, N, kind)(slice._lengths, slice._strides, It(slice._iterator, callable));
24542466
}
@@ -4093,3 +4105,76 @@ template member(string name)
40934105
matrix.member!"y"[] = matrix.member!"f";
40944106
assert(matrix.member!"y" == [2, 3].iota * 2);
40954107
}
4108+
4109+
4110+
version(D_Exceptions)
4111+
private immutable orthogonalReduceFieldException = new Exception("orthogonalReduceField: Slice composed of fields must not be empty");
4112+
4113+
/++
4114+
Functional deep-element wise reduce of a slice composed of fields or iterators.
4115+
+/
4116+
template orthogonalReduceField(alias fun)
4117+
{
4118+
import mir.functional: naryFun;
4119+
static if (__traits(isSame, naryFun!fun, fun))
4120+
{
4121+
@optmath:
4122+
/++
4123+
Params:
4124+
slice = Non empty input slice composed of fields or iterators.
4125+
Returns:
4126+
a lazy field with each element of which is reduced value of element of the same index of all iterators.
4127+
+/
4128+
OrthogonalReduceField!(Iterator, fun) orthogonalReduceField(Iterator)(Slice!Iterator slice)
4129+
{
4130+
if (_expect(slice.empty, false))
4131+
{
4132+
version(D_Exceptions)
4133+
return orthogonalReduceFieldException;
4134+
else
4135+
assert(0);
4136+
}
4137+
return typeof(return)(slice);
4138+
}
4139+
4140+
/// ditto
4141+
auto orthogonalReduceField(T)(T[] array)
4142+
{
4143+
return orthogonalReduceField(array.sliced);
4144+
}
4145+
4146+
/// ditto
4147+
auto orthogonalReduceField(T)(auto ref T withAsSlice)
4148+
if (hasAsSlice!T)
4149+
{
4150+
return orthogonalReduceField(withAsSlice.asSlice);
4151+
}
4152+
}
4153+
else alias orthogonalReduceField = .orthogonalReduceField!(naryFun!fun);
4154+
}
4155+
4156+
/// bit array operations
4157+
unittest
4158+
{
4159+
import mir.ndslice.allocation: bitSlice;
4160+
import mir.ndslice.dynamic: strided;
4161+
import mir.ndslice.topology: iota, orthogonalReduceField;
4162+
auto len = 100;
4163+
auto a = len.bitSlice;
4164+
auto b = len.bitSlice;
4165+
auto c = len.bitSlice;
4166+
a[len.iota.strided!0(7)][] = true;
4167+
b[len.iota.strided!0(11)][] = true;
4168+
c[len.iota.strided!0(13)][] = true;
4169+
4170+
// this is valid since bitslices above are oroginal slices of allocated memory.
4171+
auto and = [
4172+
a.iterator._field._field, // get raw data pointers
4173+
b.iterator._field._field,
4174+
c.iterator._field._field]
4175+
.orthogonalReduceField!"a & b" // operation on size_t
4176+
.bitwiseField
4177+
.slicedField(len);
4178+
4179+
assert(and == (a & b & c));
4180+
}

0 commit comments

Comments
 (0)