A binary operator is called trivial when exactly one of its operands is a constant and is such that the binary operation is equivalent to the identity operation on its other input. For example, an Add operator is trivial if one of its operands is constant 0, a Mul operator is trivial if one of its operands is constant 1, etc.
| 47 | // one of its operands is constant 0, a Mul operator is trivial |
| 48 | // if one of its operands is constant 1, etc. |
| 49 | ::tensorflow::Status RemoveTrivialBinaryOperator::Run(Model* model, |
| 50 | std::size_t op_index, |
| 51 | bool* modified) { |
| 52 | *modified = false; |
| 53 | const auto binary_it = model->operators.begin() + op_index; |
| 54 | auto* binary_op = binary_it->get(); |
| 55 | if (binary_op->type != OperatorType::kAdd && |
| 56 | binary_op->type != OperatorType::kMul && |
| 57 | binary_op->type != OperatorType::kSub && |
| 58 | binary_op->type != OperatorType::kDiv) { |
| 59 | return ::tensorflow::Status::OK(); |
| 60 | } |
| 61 | |
| 62 | CHECK_EQ(binary_op->inputs.size(), 2); |
| 63 | |
| 64 | // This graph transformation is only concerned with the case |
| 65 | // when one input is constant and the other is not constant. |
| 66 | const bool is_input_constant[2] = { |
| 67 | IsConstantParameterArray(*model, binary_op->inputs[0]), |
| 68 | IsConstantParameterArray(*model, binary_op->inputs[1]), |
| 69 | }; |
| 70 | if (!is_input_constant[0] && !is_input_constant[1]) { |
| 71 | // Neither input is constant, so nothing we can resolve here. |
| 72 | return ::tensorflow::Status::OK(); |
| 73 | } |
| 74 | if (is_input_constant[0] && is_input_constant[1]) { |
| 75 | // Both inputs are constants. That's a job for constants |
| 76 | // propagation, not for us to handle here. |
| 77 | return ::tensorflow::Status::OK(); |
| 78 | } |
| 79 | const int index_of_constant_input = is_input_constant[0] ? 0 : 1; |
| 80 | const int index_of_variable_input = is_input_constant[0] ? 1 : 0; |
| 81 | CHECK(is_input_constant[index_of_constant_input]); |
| 82 | CHECK(!is_input_constant[index_of_variable_input]); |
| 83 | |
| 84 | // If this was a broadcasting op we can't remove it as we need the broadcast. |
| 85 | // It's possible we could replace it with a cheaper op, though. |
| 86 | const auto& input_array_0 = model->GetArray(binary_op->inputs[0]); |
| 87 | const auto& input_array_1 = model->GetArray(binary_op->inputs[1]); |
| 88 | if (!input_array_0.has_shape() || !input_array_1.has_shape()) { |
| 89 | // Both input shapes must be known. |
| 90 | return ::tensorflow::Status::OK(); |
| 91 | } |
| 92 | if (input_array_0.shape().dimensions_count() == |
| 93 | input_array_1.shape().dimensions_count() && |
| 94 | input_array_0.shape() != input_array_1.shape()) { |
| 95 | AddMessageF( |
| 96 | "Preserving %s even though it's trivial as we need to broadcast " |
| 97 | "(lhs %s, rhs %s)", |
| 98 | LogName(*binary_op), ShapeToString(input_array_0.shape()), |
| 99 | ShapeToString(input_array_1.shape())); |
| 100 | return ::tensorflow::Status::OK(); |
| 101 | } |
| 102 | |
| 103 | // Now check if the constant operand makes this binary |
| 104 | // operator trivial. |
| 105 | const auto& constant_input_array = |
| 106 | model->GetArray(binary_op->inputs[index_of_constant_input]); |
nothing calls this directly
no test coverage detected