tensorflow/lite/toco/graph_transformations/merge_reshape_into_preceding_transpose.cc
// When a transpose is fed into a reshape, it is possible for the two operators
// to be merged if the reshape does not affect memory ordering and does not
// affects the number of dimensions. This only occurs when only unary dimensions
// are shifting position.
When a transpose is fed into a reshape, it is possible for the two operators
to be merged if the reshape does not affect memory ordering and does not
affects the number of dimensions. This only occurs when only unary dimensions
are shifting position.
When a transpose is fed into a reshape it is possible for the two operators to be merged if the reshape does not affect memory ordering and does not affects the number of dimensions . This only occurs when only unary dimensions are shifting position .
::tensorflow::Status MergeReshapeIntoPrecedingTranspose::Run(
    Model* model, std::size_t op_index, bool* modified) {
  *modified = false;
  auto it = model->operators.begin() + op_index;
  auto* reshape_op = ConvertOperator<TensorFlowReshapeOperator*>(
      it->get(), OperatorType::kReshape);
  if (reshape_op == nullptr) {
    return ::tensorflow::Status::OK();
  }
  if (!OperatorReady(*model, reshape_op) || reshape_op->shape.empty()) {
    return ::tensorflow::Status::OK();
  }
  const std::string intermediate_name = reshape_op->inputs[0];
  const std::string output_name = reshape_op->outputs[0];
  if (CountOpsWithInput(*model, intermediate_name) != 1) {
    return ::tensorflow::Status::OK();
  }
  const auto& transpose_it = FindOpWithOutput(*model, intermediate_name);
  if (transpose_it == model->operators.end()) {
    return ::tensorflow::Status::OK();
  }
  TransposeOperator* transpose_op = ConvertOperator<TransposeOperator*>(
      transpose_it->get(), OperatorType::kTranspose);
  if (transpose_op == nullptr) {
    return ::tensorflow::Status::OK();
  }
  if (!OperatorReady(*model, transpose_op) || transpose_op->perm.empty()) {
    return ::tensorflow::Status::OK();
  }
  if (!ReshapeIsEquivalentToTranspose(*model, reshape_op,
                                      false )) {
    return ::tensorflow::Status::OK();
  }
  if (!IsDiscardableArray(*model, intermediate_name)) {
    AddMessageF(
        "Cannot fuse %s and %s as it would invalidate the transpose "
        "output array.",
        LogName(*transpose_op), LogName(*reshape_op));
    return ::tensorflow::Status::OK();
  }
  AddMessageF("Merging operations %s and %s", LogName(*transpose_op),
              LogName(*reshape_op));
  auto merged_perm = ReshapeToTranspose(*model, reshape_op);
  const auto& transpose_perm = transpose_op->perm;
  for (size_t i = 0; i < merged_perm.size(); i++) {
    merged_perm[i] = transpose_perm[merged_perm[i]];
  }
  if (!RemoveTrivialPassthroughOp(this, model, op_index)) {
    return ::tensorflow::Status::OK();
  }
  model->GetArray(transpose_op->inputs[1])
      .GetMutableBuffer<ArrayDataType::kInt32>()
      .data = merged_perm;
  transpose_op->perm = merged_perm;
  model->GetArray(transpose_op->outputs[0]).clear_shape();
  *modified = true;
  return ::tensorflow::Status::OK();
}
:: tensorflow :: Status MergeReshapeIntoPrecedingTranspose :: Run ( Model * model , std :: size_t op_index , bool * modified ) { * modified = false ; auto it = model -> operators . begin ( ) + op_index ; auto * reshape_op = ConvertOperator < TensorFlowReshapeOperator * > ( it -> get ( ) , OperatorType :: kReshape ) ; if ( reshape_op == nullptr ) { return :: tensorflow :: Status :: OK ( ) ; } if ( ! OperatorReady ( * model , reshape_op ) || reshape_op -> shape . empty ( ) ) { return :: tensorflow :: Status :: OK ( ) ; } const std :: string intermediate_name = reshape_op -> inputs [ 0 ] ; const std :: string output_name = reshape_op -> outputs [ 0 ] ; if ( CountOpsWithInput ( * model , intermediate_name ) != 1 ) { return :: tensorflow :: Status :: OK ( ) ; } const auto & transpose_it = FindOpWithOutput ( * model , intermediate_name ) ; if ( transpose_it == model -> operators . end ( ) ) { return :: tensorflow :: Status :: OK ( ) ; } TransposeOperator * transpose_op = ConvertOperator < TransposeOperator * > ( transpose_it -> get ( ) , OperatorType :: kTranspose ) ; if ( transpose_op == nullptr ) { return :: tensorflow :: Status :: OK ( ) ; } if ( ! OperatorReady ( * model , transpose_op ) || transpose_op -> perm . empty ( ) ) { return :: tensorflow :: Status :: OK ( ) ; } if ( ! ReshapeIsEquivalentToTranspose ( * model , reshape_op , false ) ) { return :: tensorflow :: Status :: OK ( ) ; } if ( ! IsDiscardableArray ( * model , intermediate_name ) ) { AddMessageF ( " " " " , LogName ( * transpose_op ) , LogName ( * reshape_op ) ) ; return :: tensorflow :: Status :: OK ( ) ; } AddMessageF ( " " , LogName ( * transpose_op ) , LogName ( * reshape_op ) ) ; auto merged_perm = ReshapeToTranspose ( * model , reshape_op ) ; const auto & transpose_perm = transpose_op -> perm ; for ( size_t i = 0 ; i < merged_perm . size ( ) ; i ++ ) { merged_perm [ i ] = transpose_perm [ merged_perm [ i ] ] ; } if ( ! RemoveTrivialPassthroughOp ( this , model , op_index ) ) { return :: tensorflow :: Status :: OK ( ) ; } model -> GetArray ( transpose_op -> inputs [ 1 ] ) . GetMutableBuffer < ArrayDataType :: kInt32 > ( ) . data = merged_perm ; transpose_op -> perm = merged_perm ; model -> GetArray ( transpose_op -> outputs [ 0 ] ) . clear_shape ( ) ; * modified = true ; return :: tensorflow :: Status :: OK ( ) ; }
When a transpose is fed into a reshape, it is possible for the two operators
to be merged if the reshape does not affect memory ordering and does not
affects the number of dimensions.
When a transpose is fed into a reshape it is possible for the two operators to be merged if the reshape does not affect memory ordering and does not affects the number of dimensions .
// Guarantee the input is only consume by the reshape. // Check for the parent operator. // Find the parent operator and guarantee it is a transpose. /*allow_extra_unary_dimensions*/ // Check that the intermediate is not an output array. // const auto& intermediate_array = model->GetArray(intermediate_name); // const auto& output_array = model->GetArray(output_name); // Combine the permutations. // Remove the reshape as passthrough operation. // Update transpose_op's constant buffer to contain the new permutation. // transpose_ops's shape will likely has changed.