forked from pytorch/pytorch
-
Notifications
You must be signed in to change notification settings - Fork 0
/
boolean_unmask_ops.cc
156 lines (123 loc) · 4.22 KB
/
boolean_unmask_ops.cc
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)
}