1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156
|
#include "caffe2/operators/boolean_unmask_ops.h"
#include "caffe2/core/operator.h"
#include "caffe2/core/tensor.h"
namespace caffe2 {
template <>
bool BooleanUnmaskOp<CPUContext>::RunOnDevice() {
int maskSize = Input(0).numel();
int numMasks = InputSize() / 2;
auto& valueMeta = Input(1).dtype();
auto* valuesOut = Output(0);
valuesOut->Resize(maskSize);
auto* valuesOutPtr = (char*)valuesOut->raw_mutable_data(valueMeta);
std::vector<int> nextValueIndices(numMasks, 0);
for (int maskOffset = 0; maskOffset < maskSize; ++maskOffset) {
bool maskFound = false;
for (int maskIndex = 0; maskIndex < numMasks; ++maskIndex) {
auto& mask = Input(maskIndex * 2);
CAFFE_ENFORCE_EQ(mask.dim(), 1);
CAFFE_ENFORCE_EQ(mask.numel(), maskSize);
const auto* maskPtr = mask.template data<bool>();
auto& values = Input(maskIndex * 2 + 1);
CAFFE_ENFORCE_EQ(values.dim(), 1);
const auto* valuesPtr = (char*)values.raw_data();
if (maskPtr[maskOffset]) {
auto& valueIndex = nextValueIndices[maskIndex];
CAFFE_ENFORCE_LT(valueIndex, values.numel());
auto* src = valuesPtr + (valueIndex++) * valueMeta.itemsize();
auto* dst = valuesOutPtr + maskOffset * valueMeta.itemsize();
std::copy(src, src + valueMeta.itemsize(), dst);
maskFound = true;
break;
}
}
CAFFE_ENFORCE(
maskFound, "All masks have False at position ", maskOffset, ".");
}
// check all indices match value length
for (int i = 0; i < numMasks; ++i) {
auto& values = Input(i * 2 + 1);
CAFFE_ENFORCE_EQ(
values.numel(),
nextValueIndices[i],
"The number of true at mask ",
i,
" does not match the corresponding value size.");
}
return true;
}
REGISTER_CPU_OPERATOR(BooleanUnmask, BooleanUnmaskOp<CPUContext>);
OPERATOR_SCHEMA(BooleanUnmask)
.NumInputs([](int n) { return n > 0 && n % 2 == 0; })
.NumOutputs(1)
.SetDoc(R"DOC(
Given a series of masks and values, reconstruct values together according to masks. A comprehensive example:
```
mask1 = True, False, True, False, False
values1 = 1.0, 3.0
mask2 = False, True, False, False, False
values2 = 2.0
mask3 = False, False, False, True, True
values3 = 4.0, 5.0
```
Reconstruct by:
```
output = net.BooleanUnmask([mask1, values1, mask2, values2, mask3, values3], ["output"])
output = 1.0, 2.0, 3.0, 4.0, 5.0
```
Note that for all mask positions, there must be at least one True. This is not allowed:
```
mask1 = True, False
values1 = 1.0
mask2 = False, False
values2 =
output = net.BooleanUnmask([mask1, values1, mask2, values2], ["output"])
```
If there are multiple True values for a field, we accept the first value, and no longer expect a value for that location:
```
mask1 = True, False
values1 = 1.0
mask2 = True, True
values2 = 2.0
output = net.BooleanUnmask([mask1, values1, mask2, values2], ["output"])
output = 1.0, 2.0
```
*** Note that we alternate `data` and `mask` inputs
Github Links:
- https://github.com/pytorch/pytorch/blob/master/caffe2/operators/boolean_unmask_ops.cc
<details>
<summary> <b>Example</b> </summary>
**Code**
```
workspace.ResetWorkspace()
op = core.CreateOperator(
"BooleanUnmask",
["mask1", "data1", "mask2", "data2"],
["unmasked_data"]
)
workspace.FeedBlob("mask1", np.array([True,False,False,True,True,False]))
workspace.FeedBlob("data1", np.array([1,4,5]))
workspace.FeedBlob("mask2", np.array([False,True,True,False,False,True]))
workspace.FeedBlob("data2", np.array([2,3,6]))
print("data1:", workspace.FetchBlob("data1"))
print("mask1:", workspace.FetchBlob("mask1"))
print("data2:", workspace.FetchBlob("data2"))
print("mask2:", workspace.FetchBlob("mask2"))
workspace.RunOperatorOnce(op)
print("unmasked_data:", workspace.FetchBlob("unmasked_data"))
```
**Result**
```
data1: [1 4 5]
mask1: [ True False False True True False]
data2: [2 3 6]
mask2: [False True True False False True]
unmasked_data: [1 2 3 4 5 6]
```
</details>
)DOC")
.Input(0,"data","(*Tensor*): 1D input tensor(s)")
.Input(1,"mask","(*Tensor`<bool>`*): 1D boolean mask tensor(s)")
.Output(0, "unmasked_data", "(*Tensor*): 1D tensor of same type as `data` input that contains the unmasked input tensor");
NO_GRADIENT(BooleanUnmask)
}
|