| #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) | 
 | } |