Skip to content
This repository was archived by the owner on Jul 1, 2025. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions include/glow/Importer/CommonOperatorLoader.h
Original file line number Diff line number Diff line change
Expand Up @@ -511,6 +511,26 @@ class CommonOperatorLoader : public ProtobufLoader {

bool broadcast;
ASSIGN_VALUE_OR_RETURN_ERR(broadcast, getBroadcast(dict));
// Check implicit broadcast
if (!broadcast && in0.dims().size() != in1.dims().size()) {
bool validBroadcast = true;
auto dimsA = in0.dims();
auto dimsB = in1.dims();
for (int i = dimsA.size() - 1, j = dimsB.size() - 1; i >= 0 && j >= 0;) {
auto a = dimsA[i];
auto b = dimsB[j];
if (!(a == b || a == 1 || b == 1)) {
validBroadcast = false;
break;
}
--i;
--j;
}
if (!validBroadcast) {
LOG(WARNING) << "Invalid broadcast rule for inputs of " << opName;
}
broadcast = validBroadcast;
}

int axis = -1;

Expand Down
6 changes: 3 additions & 3 deletions lib/Importer/Caffe2ModelLoader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -580,10 +580,10 @@ Error Caffe2ModelLoader::loadLayerNorm(const caffe2::OperatorDef &op,
LayerNormalizationNode *node =
G_->createLayerNormalization(opName, in, weight, bias, eps);

RETURN_ERR_IF_NOT(op.output_size() == 1,
"Supporting only one output from LayerNorm");
// We only support one output for LayoutNorm. Ignoring the
// rest of the outputs.
RETURN_IF_ERR(addNodeAsOutput(op, node, /* numOutputs */ 1));

RETURN_IF_ERR(addNodeAsOutput(op, node));
return Error::success();
}

Expand Down
35 changes: 35 additions & 0 deletions tests/models/caffe2Models/elementwise_linear_broadcast_net.pbtxt
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
name: "elementwise_linear"
op {
input: "X"
input: "w"
output: "i0"
name: ""
type: "Mul"
arg {
name: "axis"
i: 1
}
arg {
name: "broadcast"
i: 0
}
}
op {
input: "i0"
input: "b"
output: "el_result"
name: ""
type: "Add"
arg {
name: "axis"
i: 1
}
arg {
name: "broadcast"
i: 0
}
}
external_input: "X"
external_input: "w"
external_input: "b"
external_output: "el_result"
80 changes: 80 additions & 0 deletions tests/unittests/Caffe2ImporterTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2191,6 +2191,86 @@ TEST_F(Caffe2ImporterTest, elementwiseLinearUnspecifiedAxis) {
EXPECT_EQ(mod.getPlaceholders().size(), 4);
}

/// Test loading an ElementwiseLinear operator with implicit broadcast
TEST_F(Caffe2ImporterTest, elementwiseImplicitBroadcast) {
ExecutionEngine EE{};
auto &mod = EE.getModule();
Function *F = mod.createFunction("main");

std::string NetDescFilename(
GLOW_DATA_PATH
"tests/models/caffe2Models/elementwise_linear_broadcast_net.pbtxt");
std::string NetWeightFilename(
GLOW_DATA_PATH "tests/models/caffe2Models/empty_init_net.pbtxt");

PlaceholderBindings bindings;
Placeholder *output;

// Since the loader will assume that axis = 1, the 0th dim of the shapes of w
// and b must match the 1st dim of X.
Tensor X(ElemKind::FloatTy, {5, 10});
Tensor w(ElemKind::FloatTy, {10}), b(ElemKind::FloatTy, {10});

// Destroy the loader after the graph is loaded since the following execution
// will not depend on anything from the loader.
{
Caffe2ModelLoader caffe2LD(NetDescFilename, NetWeightFilename,
{"X", "w", "b"},
{&X.getType(), &w.getType(), &b.getType()}, *F);
output = EXIT_ON_ERR(caffe2LD.getSingleOutput());
}

// Check that the shape of the output matches that of the input.
std::vector<dim_t> expectedDims = {5, 10};
EXPECT_TRUE(output->dims().vec() == expectedDims);

// High level checks on the content of the graph.
// It should look like this:
//
// X w b
// | | |
// | v v
// | Reshape Reshape
// | | |
// | v v
// | Tile Tile
// | / /
// v v------ /
// Mul /
// | /---------------
// v v
// Add
// |
// v
// Save

EXPECT_EQ(F->getNodes().size(), 7);
auto *save = getSaveNodeFromDest(output);
auto *add = llvm::dyn_cast<AddNode>(save->getInput().getNode());
ASSERT_TRUE(add);
auto *mul = llvm::dyn_cast<MulNode>(add->getLHS().getNode());
ASSERT_TRUE(mul);
auto *bTile = llvm::dyn_cast<TileNode>(add->getRHS().getNode());
ASSERT_TRUE(bTile);
EXPECT_EQ(bTile->getAxis(), 0);
auto *XPH = llvm::dyn_cast<Placeholder>(mul->getLHS().getNode());
EXPECT_EQ(XPH, mod.getPlaceholderByName("X"));
auto *wTile = llvm::dyn_cast<TileNode>(mul->getRHS().getNode());
ASSERT_TRUE(wTile);
EXPECT_EQ(wTile->getAxis(), 0);
auto *bReshape = llvm::dyn_cast<ReshapeNode>(bTile->getInput().getNode());
ASSERT_TRUE(bReshape);
auto *wReshape = llvm::dyn_cast<ReshapeNode>(wTile->getInput().getNode());
ASSERT_TRUE(wReshape);
auto *wPH = llvm::dyn_cast<Placeholder>(wReshape->getInput().getNode());
EXPECT_EQ(wPH, mod.getPlaceholderByName("w"));
auto *bPH = llvm::dyn_cast<Placeholder>(bReshape->getInput().getNode());
EXPECT_EQ(bPH, mod.getPlaceholderByName("b"));

// We have three inputs and one output.
EXPECT_EQ(mod.getPlaceholders().size(), 4);
}

/// Test loading SparseLengthsWeightedSum8BitsRowwise. This is created as a
/// RowwiseQuantizedSparseLengthsWeightedSumNode. The following inputs/outputs
/// are used/expected for this test. Note that the DATA input is
Expand Down