| 573 | } |
| 574 | |
| 575 | TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) { |
| 576 | const auto* params = reinterpret_cast<TfLiteBidirectionalSequenceRNNParams*>( |
| 577 | node->builtin_data); |
| 578 | |
| 579 | const TfLiteTensor* input = GetInput(context, node, kInputTensor); |
| 580 | const TfLiteTensor* fw_input_weights = |
| 581 | GetInput(context, node, kFwWeightsTensor); |
| 582 | const TfLiteTensor* fw_recurrent_weights = |
| 583 | GetInput(context, node, kFwRecurrentWeightsTensor); |
| 584 | const TfLiteTensor* fw_bias = GetInput(context, node, kFwBiasTensor); |
| 585 | const TfLiteTensor* bw_input_weights = |
| 586 | GetInput(context, node, kBwWeightsTensor); |
| 587 | const TfLiteTensor* bw_recurrent_weights = |
| 588 | GetInput(context, node, kBwRecurrentWeightsTensor); |
| 589 | const TfLiteTensor* bw_bias = GetInput(context, node, kBwBiasTensor); |
| 590 | |
| 591 | // Get auxiliary inputs. |
| 592 | const TfLiteTensor* aux_input = |
| 593 | GetOptionalInputTensor(context, node, kAuxInputTensor); |
| 594 | const TfLiteTensor* fw_aux_input_weights = |
| 595 | GetOptionalInputTensor(context, node, kFwAuxWeightsTensor); |
| 596 | const TfLiteTensor* bw_aux_input_weights = |
| 597 | GetOptionalInputTensor(context, node, kBwAuxWeightsTensor); |
| 598 | |
| 599 | TfLiteTensor* fw_hidden_state = |
| 600 | GetVariableInput(context, node, kFwHiddenStateTensor); |
| 601 | TfLiteTensor* bw_hidden_state = |
| 602 | GetVariableInput(context, node, kBwHiddenStateTensor); |
| 603 | |
| 604 | TfLiteTensor* fw_output = GetOutput(context, node, kFwOutputTensor); |
| 605 | TfLiteTensor* bw_output = params->merge_outputs |
| 606 | ? nullptr |
| 607 | : GetOutput(context, node, kBwOutputTensor); |
| 608 | |
| 609 | const bool has_previous_bw_output = (aux_input != nullptr); |
| 610 | const bool use_aux_input = (fw_aux_input_weights != nullptr); |
| 611 | |
| 612 | // We want to cover the following cases: |
| 613 | // |
| 614 | // If not stacking (not connected after other bidi lstms): |
| 615 | // both fw & bw will just use `input`; aux_input will be null. |
| 616 | // |
| 617 | // If stacking with cross_links, TensorFlow equivalent |
| 618 | // (tf.contrib.rnn.stack_bidirectional_rnn): |
| 619 | // both fw & bw will use `input`, but aux_input will be none null. |
| 620 | // Note, this time, whether connected after other bidi lstms both works. |
| 621 | // |
| 622 | // If stacking without cross_links, but connected after other bidi lstms, |
| 623 | // TensorFlow equivalent (tf.nn.static_bidirectional_rnn): |
| 624 | // fw will use `input`, bw will use aux_input, and the `real aux_input` |
| 625 | // will be null. |
| 626 | |
| 627 | const bool non_stacking_mode = !use_aux_input && has_previous_bw_output; |
| 628 | const TfLiteTensor* bw_input = non_stacking_mode ? aux_input : input; |
| 629 | const TfLiteTensor* real_aux_input = non_stacking_mode ? nullptr : aux_input; |
| 630 | |
| 631 | switch (fw_input_weights->type) { |
| 632 | case kTfLiteFloat32: |
nothing calls this directly
no test coverage detected