<commit_before>/* Copyright 2016 The TensorFlow Authors. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/

#include "tensorflow/compiler/xla/service/gpu/fusion_merger.h"

#include <algorithm>

#include "tensorflow/compiler/xla/service/hlo_cost_analysis.h"
#include "tensorflow/compiler/xla/shape_util.h"
#include "tensorflow/compiler/xla/util.h"
#include "tensorflow/core/lib/core/errors.h"
#include "tensorflow/core/lib/strings/str_util.h"

namespace xla {
namespace gpu {

namespace {

// Traverses users of tuple shape, adding leaf instructions to 'instructions'.
void MaybeResolveTupleElements(HloInstruction* instruction,
                               std::vector<HloInstruction*>* instructions) {
  if (ShapeUtil::IsTuple(instruction->shape())) {
    for (auto tuple_user : instruction->users()) {
      MaybeResolveTupleElements(tuple_user, instructions);
    }
  } else {
    instructions->push_back(instruction);
  }
}

// Returns the bytes read by fusion parameter 'param', by returning the byte
// size of 'param' shape (or the cumulative byte sizes of all leaf tuple
// elements if 'param' is tuple-shaped).
// In the special case where all users of 'param' (or all users of a leaf
// tuple element if 'param' is tuple-shaped) are Slice instructions, the size
// of each slice instruction is accumulated instead, to give a more accurate
// value for bytes read.
double CalculateBytesReadByFusionParameter(HloInstruction* param) {
  CHECK_EQ(HloOpcode::kParameter, param->opcode());

  // Adds all leaf tuple elements to 'instructions' if 'param' is tuple-shaped.
  // Adds 'param' to 'instructions' otherwise.
  std::vector<HloInstruction*> instructions;
  MaybeResolveTupleElements(param, &instructions);

  // Iterate through 'instructions' accumulating byte sizes of each instruction
  // shape. For each 'instruction' in 'instructions', if all users of
  // 'instruction' are Slice instructions, accumuates the byte sizes of each
  // Slice for a more accurate estimate of bytes read.
  double bytes = 0.0;
  for (auto& instruction : instructions) {
    if (std::all_of(instruction->users().begin(), instruction->users().end(),
                    [](const HloInstruction* instruction) {
                      return instruction->opcode() == HloOpcode::kSlice ||
                             instruction->opcode() == HloOpcode::kDynamicSlice;
                    })) {
      // All users are slice: accumulate bytes of all user slice instructions.
      for (auto& user : instruction->users()) {
        bytes += ShapeUtil::ByteSizeOf(user->shape());
      }
    } else {
      // Some users are not slice: accumulate full size of 'instruction'.
      bytes += ShapeUtil::ByteSizeOf(instruction->shape());
    }
  }
  return bytes;
}

// Returns the bytes read by all fusion parameters of instruction 'fusion'.
double CalculateBytesReadByFusionInstruction(HloInstruction* fusion) {
  double bytes = 0.0;
  for (const auto& fused_instruction : fusion->fused_instructions()) {
    if (fused_instruction->opcode() != HloOpcode::kParameter) {
      continue;
    }
    bytes += CalculateBytesReadByFusionParameter(fused_instruction.get());
  }
  return bytes;
}

// Returns the flops to bytes transferred ratio of instruction 'fusion'.
double CalculateFlopsToBytesRatio(HloInstruction* fusion) {
  CHECK_EQ(HloOpcode::kFusion, fusion->opcode());
  // Calculate total bytes transferred in/out.
  double bytes = CalculateBytesReadByFusionInstruction(fusion);
  // Add bytes written to root instructions buffer.
  bytes += ShapeUtil::ByteSizeOf(fusion->fused_expression_root()->shape());
  // Calculate flops for all fused instructions.
  HloCostAnalysis analysis;
  TF_CHECK_OK(fusion->fused_expression_root()->Accept(&analysis));
  // Return flops / bytes.
  return bytes > 0.0 ? analysis.flop_count() / bytes : analysis.flop_count();
}

// Returns bytes transferred by instruction 'fusion', including the bytes
// that would be read by all users.
double GetCurrentBytesTransferred(HloInstruction* fusion) {
  CHECK_EQ(HloOpcode::kFusion, fusion->opcode());
  const double bytes_read = CalculateBytesReadByFusionInstruction(fusion);
  const double bytes_written =
      ShapeUtil::ByteSizeOf(fusion->fused_expression_root()->shape());
  // Current bytes transferred (ignoring non 'fusion' user operands) is bytes
  // read and written by 'fusion', plus reads of size 'bytes_written' for each
  // user.
  return bytes_read + bytes_written * (fusion->user_count() + 1);
}

// Returns bytes transferred if 'fusion' were to be merged into its users.
double GetMergedBytesTransferred(HloInstruction* fusion) {
  CHECK_EQ(HloOpcode::kFusion, fusion->opcode());
  return CalculateBytesReadByFusionInstruction(fusion) * fusion->user_count();
}

}  // anonymous namespace

// FusionInstructionMerger visits all fusion instructions in 'computation'
// in post order, attempting to merge each into all of its users.
// Accumulates and reports stats on successful/failed merge attempts.
class FusionInstructionMerger {
 public:
  explicit FusionInstructionMerger(HloComputation* computation)
      : computation_(computation) {}

  Status Run();

  bool changed() const { return changed_; }

 private:
  Status HandleFusion(HloInstruction* fusion);

  HloComputation* computation_;
  bool changed_ = false;

  // Fusion instruction merge stats.
  int total_visited_ = 0;
  int total_merged_ = 0;
  int num_fail_no_users_ = 0;
  int num_fail_dot_root_ = 0;
  int num_fail_merge_all_users_ = 0;
  int num_fail_flops_to_byte_ratio_ = 0;
  int num_fail_net_bytes_transferred_ratio_ = 0;

  TF_DISALLOW_COPY_AND_ASSIGN(FusionInstructionMerger);
};

Status FusionInstructionMerger::Run() {
  for (auto* instruction : computation_->MakeInstructionPostOrder()) {
    if (instruction->opcode() == HloOpcode::kFusion) {
      TF_RETURN_IF_ERROR(HandleFusion(instruction));
    }
  }

  VLOG(1) << "FusionInstructionMerger EXIT"
          << " computation: " << computation_->name()
          << " total_visited: " << total_visited_
          << " total_merged: " << total_merged_ << " merge failures { "
          << " no_users: " << num_fail_no_users_
          << " dot_root: " << num_fail_dot_root_
          << " merge_all_users: " << num_fail_merge_all_users_
          << " flops_to_byte_ratio: " << num_fail_flops_to_byte_ratio_
          << " net_bytes_transferred: " << num_fail_net_bytes_transferred_ratio_
          << " }";
  return Status::OK();
}

Status FusionInstructionMerger::HandleFusion(HloInstruction* fusion) {
  VLOG(3) << "FusionInstructionMerger ENTRY fusion: " << fusion->name()
          << " flops_to_bytes_ratio: " << CalculateFlopsToBytesRatio(fusion);
  ++total_visited_;
  // Skip 'fusion' instruction if there are no users into which we can merge.
  if (fusion->users().empty()) {
    ++num_fail_no_users_;
    return Status::OK();
  }
  // Skip 'fusion' instruction if its fused root is kDot, as IrEmitterUnnested
  // expects a specific structure (kParameter operands) for this case.
  if (fusion->fused_expression_root()->opcode() == HloOpcode::kDot) {
    ++num_fail_dot_root_;
    return Status::OK();
  }
  // Skip 'fusion' instruction if we cannot merge into all of its users.
  // Merging into all users enables the removal of 'fusion' from the
  // computation.
  if (!std::all_of(fusion->users().begin(), fusion->users().end(),
                   [](const HloInstruction* instruction) {
                     return instruction->opcode() == HloOpcode::kFusion &&
                            instruction->fused_expression_root()->opcode() !=
                                HloOpcode::kDot;
                   })) {
    ++num_fail_merge_all_users_;
    return Status::OK();
  }
  // Skip 'fusion' instruction if its flops to bytes transferred ratio
  // exceeds the threshold value.
  if (CalculateFlopsToBytesRatio(fusion) >
      FusionMerger::GetThresholdFlopsToBytesRatio()) {
    ++num_fail_flops_to_byte_ratio_;
    return Status::OK();
  }
  // Skip 'fusion' instruction if merging it into all users would result in a
  // net increase in bytes transferred (currently allowing the net bytes
  // transferred to be exceeded up to ~10% in exhange for eliminating the
  // overhead from a GPU kernel launch).
  const double current_bytes_transferred = GetCurrentBytesTransferred(fusion);
  const double merged_bytes_transferred = GetMergedBytesTransferred(fusion);
  const double merged_to_current_bytes_ratio =
      merged_bytes_transferred / std::max(1.0, current_bytes_transferred);
  if (merged_to_current_bytes_ratio > 1.10) {
    ++num_fail_net_bytes_transferred_ratio_;
    return Status::OK();
  }
  // Merge fused instructions from 'fusion' into each user.
  std::set<HloInstruction*> users = fusion->users();
  for (HloInstruction* user : users) {
    user->MergeFusionInstruction(fusion);
    changed_ = true;
  }
  ++total_merged_;
  VLOG(2) << "Merged fusion instruction: " << fusion->name()
          << " flops_to_bytes_ratio: " << CalculateFlopsToBytesRatio(fusion)
          << " merged_to_current_bytes_ratio: " << merged_to_current_bytes_ratio
          << " into users { "
          << tensorflow::str_util::Join(users, ", ",
                                        [](string* out, HloInstruction* user) {
                                          tensorflow::strings::StrAppend(
                                              out, user->name());
                                        })
          << " }";
  // Remove 'fusion' instruction.
  CHECK_EQ(0, fusion->user_count());
  computation_->RemoveInstruction(fusion);
  return Status::OK();
}

StatusOr<bool> FusionMerger::Run(HloModule* module) {
  bool changed = false;
  VLOG(2) << "FusionMerger for module: " << module->name();
  for (auto& computation : module->computations()) {
    VLOG(1) << "Before running FusionInstructionMerger for computation: "
            << computation->name();
    XLA_VLOG_LINES(3, computation->ToString());

    FusionInstructionMerger fusion_merger(computation.get());
    TF_RETURN_IF_ERROR(fusion_merger.Run());
    changed |= fusion_merger.changed();

    VLOG(1) << "After running FusionInstructionMerger for computation: "
            << computation->name() << " changed: " << changed;
    XLA_VLOG_LINES(3, computation->ToString());
  }
  return changed;
}

}  // namespace gpu
}  // namespace xla
<commit_msg>[XLA:GPU] Do not merge fusions that are not loop fusions.<commit_after>/* Copyright 2016 The TensorFlow Authors. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/

#include "tensorflow/compiler/xla/service/gpu/fusion_merger.h"

#include <algorithm>

#include "tensorflow/compiler/xla/service/hlo_cost_analysis.h"
#include "tensorflow/compiler/xla/shape_util.h"
#include "tensorflow/compiler/xla/util.h"
#include "tensorflow/core/lib/core/errors.h"
#include "tensorflow/core/lib/strings/str_util.h"

namespace xla {
namespace gpu {

namespace {

// Traverses users of tuple shape, adding leaf instructions to 'instructions'.
void MaybeResolveTupleElements(HloInstruction* instruction,
                               std::vector<HloInstruction*>* instructions) {
  if (ShapeUtil::IsTuple(instruction->shape())) {
    for (auto tuple_user : instruction->users()) {
      MaybeResolveTupleElements(tuple_user, instructions);
    }
  } else {
    instructions->push_back(instruction);
  }
}

// Returns the bytes read by fusion parameter 'param', by returning the byte
// size of 'param' shape (or the cumulative byte sizes of all leaf tuple
// elements if 'param' is tuple-shaped).
// In the special case where all users of 'param' (or all users of a leaf
// tuple element if 'param' is tuple-shaped) are Slice instructions, the size
// of each slice instruction is accumulated instead, to give a more accurate
// value for bytes read.
double CalculateBytesReadByFusionParameter(HloInstruction* param) {
  CHECK_EQ(HloOpcode::kParameter, param->opcode());

  // Adds all leaf tuple elements to 'instructions' if 'param' is tuple-shaped.
  // Adds 'param' to 'instructions' otherwise.
  std::vector<HloInstruction*> instructions;
  MaybeResolveTupleElements(param, &instructions);

  // Iterate through 'instructions' accumulating byte sizes of each instruction
  // shape. For each 'instruction' in 'instructions', if all users of
  // 'instruction' are Slice instructions, accumuates the byte sizes of each
  // Slice for a more accurate estimate of bytes read.
  double bytes = 0.0;
  for (auto& instruction : instructions) {
    if (std::all_of(instruction->users().begin(), instruction->users().end(),
                    [](const HloInstruction* instruction) {
                      return instruction->opcode() == HloOpcode::kSlice ||
                             instruction->opcode() == HloOpcode::kDynamicSlice;
                    })) {
      // All users are slice: accumulate bytes of all user slice instructions.
      for (auto& user : instruction->users()) {
        bytes += ShapeUtil::ByteSizeOf(user->shape());
      }
    } else {
      // Some users are not slice: accumulate full size of 'instruction'.
      bytes += ShapeUtil::ByteSizeOf(instruction->shape());
    }
  }
  return bytes;
}

// Returns the bytes read by all fusion parameters of instruction 'fusion'.
double CalculateBytesReadByFusionInstruction(HloInstruction* fusion) {
  double bytes = 0.0;
  for (const auto& fused_instruction : fusion->fused_instructions()) {
    if (fused_instruction->opcode() != HloOpcode::kParameter) {
      continue;
    }
    bytes += CalculateBytesReadByFusionParameter(fused_instruction.get());
  }
  return bytes;
}

// Returns the flops to bytes transferred ratio of instruction 'fusion'.
double CalculateFlopsToBytesRatio(HloInstruction* fusion) {
  CHECK_EQ(HloOpcode::kFusion, fusion->opcode());
  // Calculate total bytes transferred in/out.
  double bytes = CalculateBytesReadByFusionInstruction(fusion);
  // Add bytes written to root instructions buffer.
  bytes += ShapeUtil::ByteSizeOf(fusion->fused_expression_root()->shape());
  // Calculate flops for all fused instructions.
  HloCostAnalysis analysis;
  TF_CHECK_OK(fusion->fused_expression_root()->Accept(&analysis));
  // Return flops / bytes.
  return bytes > 0.0 ? analysis.flop_count() / bytes : analysis.flop_count();
}

// Returns bytes transferred by instruction 'fusion', including the bytes
// that would be read by all users.
double GetCurrentBytesTransferred(HloInstruction* fusion) {
  CHECK_EQ(HloOpcode::kFusion, fusion->opcode());
  const double bytes_read = CalculateBytesReadByFusionInstruction(fusion);
  const double bytes_written =
      ShapeUtil::ByteSizeOf(fusion->fused_expression_root()->shape());
  // Current bytes transferred (ignoring non 'fusion' user operands) is bytes
  // read and written by 'fusion', plus reads of size 'bytes_written' for each
  // user.
  return bytes_read + bytes_written * (fusion->user_count() + 1);
}

// Returns bytes transferred if 'fusion' were to be merged into its users.
double GetMergedBytesTransferred(HloInstruction* fusion) {
  CHECK_EQ(HloOpcode::kFusion, fusion->opcode());
  return CalculateBytesReadByFusionInstruction(fusion) * fusion->user_count();
}

}  // anonymous namespace

// FusionInstructionMerger visits all fusion instructions in 'computation'
// in post order, attempting to merge each into all of its users.
// Accumulates and reports stats on successful/failed merge attempts.
class FusionInstructionMerger {
 public:
  explicit FusionInstructionMerger(HloComputation* computation)
      : computation_(computation) {}

  Status Run();

  bool changed() const { return changed_; }

 private:
  Status HandleFusion(HloInstruction* fusion);

  HloComputation* computation_;
  bool changed_ = false;

  // Fusion instruction merge stats.
  int total_visited_ = 0;
  int total_merged_ = 0;
  int num_fail_no_users_ = 0;
  int num_fail_not_loop_fusion_ = 0;
  int num_fail_merge_all_users_ = 0;
  int num_fail_flops_to_byte_ratio_ = 0;
  int num_fail_net_bytes_transferred_ratio_ = 0;

  TF_DISALLOW_COPY_AND_ASSIGN(FusionInstructionMerger);
};

Status FusionInstructionMerger::Run() {
  for (auto* instruction : computation_->MakeInstructionPostOrder()) {
    if (instruction->opcode() == HloOpcode::kFusion) {
      TF_RETURN_IF_ERROR(HandleFusion(instruction));
    }
  }

  VLOG(1) << "FusionInstructionMerger EXIT"
          << " computation: " << computation_->name()
          << " total_visited: " << total_visited_
          << " total_merged: " << total_merged_ << " merge failures { "
          << " no_users: " << num_fail_no_users_
          << " not_loop_fusion: " << num_fail_not_loop_fusion_
          << " merge_all_users: " << num_fail_merge_all_users_
          << " flops_to_byte_ratio: " << num_fail_flops_to_byte_ratio_
          << " net_bytes_transferred: " << num_fail_net_bytes_transferred_ratio_
          << " }";
  return Status::OK();
}

Status FusionInstructionMerger::HandleFusion(HloInstruction* fusion) {
  VLOG(3) << "FusionInstructionMerger ENTRY fusion: " << fusion->name()
          << " flops_to_bytes_ratio: " << CalculateFlopsToBytesRatio(fusion);
  ++total_visited_;
  // Skip 'fusion' instruction if there are no users into which we can merge.
  if (fusion->users().empty()) {
    ++num_fail_no_users_;
    return Status::OK();
  }

  // Skip 'fusion' instruction if it is not a loop fusion. Library fusion
  // instructions match specific patterns, so they shouldn't be further fused.
  // Input fusion instructions need to be rooted at a particular HLO (e.g.
  // kReduce), so they shouldn't be further fused either.
  if (fusion->fusion_kind() != HloInstruction::FusionKind::kLoop) {
    ++num_fail_not_loop_fusion_;
    return Status::OK();
  }
  // Skip 'fusion' instruction if we cannot merge into all of its users.
  // Merging into all users enables the removal of 'fusion' from the
  // computation.
  if (!std::all_of(fusion->users().begin(), fusion->users().end(),
                   [](const HloInstruction* instruction) {
                     return instruction->opcode() == HloOpcode::kFusion &&
                            instruction->fusion_kind() ==
                                HloInstruction::FusionKind::kLoop;
                   })) {
    ++num_fail_merge_all_users_;
    return Status::OK();
  }
  // Skip 'fusion' instruction if its flops to bytes transferred ratio
  // exceeds the threshold value.
  if (CalculateFlopsToBytesRatio(fusion) >
      FusionMerger::GetThresholdFlopsToBytesRatio()) {
    ++num_fail_flops_to_byte_ratio_;
    return Status::OK();
  }
  // Skip 'fusion' instruction if merging it into all users would result in a
  // net increase in bytes transferred (currently allowing the net bytes
  // transferred to be exceeded up to ~10% in exhange for eliminating the
  // overhead from a GPU kernel launch).
  const double current_bytes_transferred = GetCurrentBytesTransferred(fusion);
  const double merged_bytes_transferred = GetMergedBytesTransferred(fusion);
  const double merged_to_current_bytes_ratio =
      merged_bytes_transferred / std::max(1.0, current_bytes_transferred);
  if (merged_to_current_bytes_ratio > 1.10) {
    ++num_fail_net_bytes_transferred_ratio_;
    return Status::OK();
  }
  // Merge fused instructions from 'fusion' into each user.
  std::set<HloInstruction*> users = fusion->users();
  for (HloInstruction* user : users) {
    user->MergeFusionInstruction(fusion);
    changed_ = true;
  }
  ++total_merged_;
  VLOG(2) << "Merged fusion instruction: " << fusion->name()
          << " flops_to_bytes_ratio: " << CalculateFlopsToBytesRatio(fusion)
          << " merged_to_current_bytes_ratio: " << merged_to_current_bytes_ratio
          << " into users { "
          << tensorflow::str_util::Join(users, ", ",
                                        [](string* out, HloInstruction* user) {
                                          tensorflow::strings::StrAppend(
                                              out, user->name());
                                        })
          << " }";
  // Remove 'fusion' instruction.
  CHECK_EQ(0, fusion->user_count());
  computation_->RemoveInstruction(fusion);
  return Status::OK();
}

StatusOr<bool> FusionMerger::Run(HloModule* module) {
  bool changed = false;
  VLOG(2) << "FusionMerger for module: " << module->name();
  for (auto& computation : module->computations()) {
    VLOG(1) << "Before running FusionInstructionMerger for computation: "
            << computation->name();
    XLA_VLOG_LINES(3, computation->ToString());

    FusionInstructionMerger fusion_merger(computation.get());
    TF_RETURN_IF_ERROR(fusion_merger.Run());
    changed |= fusion_merger.changed();

    VLOG(1) << "After running FusionInstructionMerger for computation: "
            << computation->name() << " changed: " << changed;
    XLA_VLOG_LINES(3, computation->ToString());
  }
  return changed;
}

}  // namespace gpu
}  // namespace xla
<|endoftext|>


<commit_before>/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/

#include <stdint.h>

#include "tensorflow/lite/micro/all_ops_resolver.h"
#include "tensorflow/lite/micro/benchmarks/keyword_scrambled_model_data.h"
#include "tensorflow/lite/micro/recording_micro_allocator.h"
#include "tensorflow/lite/micro/recording_micro_interpreter.h"
#include "tensorflow/lite/micro/testing/micro_test.h"
#include "tensorflow/lite/micro/testing/test_conv_model.h"

/**
 * Tests to ensure arena memory allocation does not regress by more than 3%.
 */

namespace {

// Ensure memory doesn't expand more that 3%:
constexpr float kAllocationThreshold = 0.03;

// TODO(b/160617245): Record persistent allocations to provide a more accurate
// number here.
constexpr float kAllocationTailMiscCeiling = 2 * 1024;

const bool kIs64BitSystem = (sizeof(void*) == 8);

constexpr int kKeywordModelTensorArenaSize = 22 * 1024;
uint8_t keyword_model_tensor_arena[kKeywordModelTensorArenaSize];

constexpr int kKeywordModelTensorCount = 54;
constexpr int kKeywordModelNodeAndRegistrationCount = 15;

// NOTE: These values are measured on x86-64:
// TODO(b/158651472): Consider auditing these values on non-64 bit systems.
//
// Run this test with '--copt=-DTF_LITE_STATIC_MEMORY' to get optimized memory
// runtime values:
#ifdef TF_LITE_STATIC_MEMORY
constexpr int kKeywordModelTotalSize = 18192;
constexpr int kKeywordModelTailSize = 17520;
#else
constexpr int kKeywordModelTotalSize = 21152;
constexpr int kKeywordModelTailSize = 20480;
#endif
constexpr int kKeywordModelHeadSize = 672;
constexpr int kKeywordModelTfLiteTensorVariableBufferDataSize = 10240;
constexpr int kKeywordModelTfLiteTensorQuantizationDataSize = 1728;
constexpr int kKeywordModelOpRuntimeDataSize = 148;

constexpr int kTestConvModelArenaSize = 12 * 1024;
uint8_t test_conv_tensor_arena[kTestConvModelArenaSize];

constexpr int kTestConvModelTensorCount = 15;
constexpr int kTestConvModelNodeAndRegistrationCount = 7;

// NOTE: These values are measured on x86-64:
// TODO(b/158651472): Consider auditing these values on non-64 bit systems.
#ifdef TF_LITE_STATIC_MEMORY
constexpr int kTestConvModelTotalSize = 10816;
constexpr int kTestConvModelTailSize = 3072;
#else
constexpr int kTestConvModelTotalSize = 11712;
constexpr int kTestConvModelTailSize = 3968;
#endif
constexpr int kTestConvModelHeadSize = 7744;
constexpr int kTestConvModelTfLiteTensorQuantizationDataSize = 768;
constexpr int kTestConvModelOpRuntimeDataSize = 136;

struct ModelAllocationThresholds {
  size_t tensor_count = 0;
  size_t node_and_registration_count = 0;
  size_t total_alloc_size = 0;
  size_t head_alloc_size = 0;
  size_t tail_alloc_size = 0;
  size_t tensor_variable_buffer_data_size = 0;
  size_t tensor_quantization_data_size = 0;
  size_t op_runtime_data_size = 0;
};

void EnsureAllocatedSizeThreshold(const char* allocation_type, size_t actual,
                                  size_t expected) {
  // TODO(b/158651472): Better auditing of non-64 bit systems:
  if (kIs64BitSystem) {
    // 64-bit systems should check floor and ceiling to catch memory savings:
    TF_LITE_MICRO_EXPECT_NEAR(actual, expected, kAllocationThreshold);
    if (actual != expected) {
      TF_LITE_REPORT_ERROR(micro_test::reporter,
                           "%s threshold failed: %d != %d", allocation_type,
                           actual, expected);
    }
  } else {
    // Non-64 bit systems should just expect allocation does not exceed the
    // ceiling:
    TF_LITE_MICRO_EXPECT_LE(actual, expected + expected * kAllocationThreshold);
  }
}

void ValidateModelAllocationThresholds(
    const tflite::RecordingMicroAllocator& allocator,
    const ModelAllocationThresholds& thresholds) {
  allocator.PrintAllocations();

  EnsureAllocatedSizeThreshold(
      "Total", allocator.GetSimpleMemoryAllocator()->GetUsedBytes(),
      thresholds.total_alloc_size);
  EnsureAllocatedSizeThreshold(
      "Head", allocator.GetSimpleMemoryAllocator()->GetHeadUsedBytes(),
      thresholds.head_alloc_size);
  EnsureAllocatedSizeThreshold(
      "Tail", allocator.GetSimpleMemoryAllocator()->GetTailUsedBytes(),
      thresholds.tail_alloc_size);
  EnsureAllocatedSizeThreshold(
      "TfLiteTensor",
      allocator
          .GetRecordedAllocation(
              tflite::RecordedAllocationType::kTfLiteTensorArray)
          .used_bytes,
      sizeof(TfLiteTensor) * thresholds.tensor_count);
  EnsureAllocatedSizeThreshold(
      "VariableBufferData",
      allocator
          .GetRecordedAllocation(
              tflite::RecordedAllocationType::kTfLiteTensorVariableBufferData)
          .used_bytes,
      thresholds.tensor_variable_buffer_data_size);
  EnsureAllocatedSizeThreshold(
      "QuantizationData",
      allocator
          .GetRecordedAllocation(tflite::RecordedAllocationType::
                                     kTfLiteTensorArrayQuantizationData)
          .used_bytes,
      thresholds.tensor_quantization_data_size);
  EnsureAllocatedSizeThreshold(
      "NodeAndRegistration",
      allocator
          .GetRecordedAllocation(
              tflite::RecordedAllocationType::kNodeAndRegistrationArray)
          .used_bytes,
      sizeof(tflite::NodeAndRegistration) *
          thresholds.node_and_registration_count);
  EnsureAllocatedSizeThreshold(
      "OpData",
      allocator.GetRecordedAllocation(tflite::RecordedAllocationType::kOpData)
          .used_bytes,
      thresholds.op_runtime_data_size);

  // Ensure tail allocation recording is not missing any large chunks:
  size_t tail_est_length = sizeof(TfLiteTensor) * thresholds.tensor_count +
                           thresholds.tensor_quantization_data_size +
                           thresholds.tensor_variable_buffer_data_size +
                           sizeof(tflite::NodeAndRegistration) *
                               thresholds.node_and_registration_count +
                           thresholds.op_runtime_data_size;
  TF_LITE_MICRO_EXPECT_LE(thresholds.tail_alloc_size - tail_est_length,
                          kAllocationTailMiscCeiling);
}

}  // namespace

TF_LITE_MICRO_TESTS_BEGIN

TF_LITE_MICRO_TEST(TestKeywordModelMemoryThreshold) {
  tflite::AllOpsResolver all_ops_resolver;
  tflite::RecordingMicroInterpreter interpreter(
      tflite::GetModel(g_keyword_scrambled_model_data), all_ops_resolver,
      keyword_model_tensor_arena, kKeywordModelTensorArenaSize,
      micro_test::reporter);

  interpreter.AllocateTensors();

  ModelAllocationThresholds thresholds;
  thresholds.tensor_count = kKeywordModelTensorCount;
  thresholds.node_and_registration_count =
      kKeywordModelNodeAndRegistrationCount;
  thresholds.total_alloc_size = kKeywordModelTotalSize;
  thresholds.head_alloc_size = kKeywordModelHeadSize;
  thresholds.tail_alloc_size = kKeywordModelTailSize;
  thresholds.tensor_variable_buffer_data_size =
      kKeywordModelTfLiteTensorVariableBufferDataSize;
  thresholds.tensor_quantization_data_size =
      kKeywordModelTfLiteTensorQuantizationDataSize;
  thresholds.op_runtime_data_size = kKeywordModelOpRuntimeDataSize;

  ValidateModelAllocationThresholds(interpreter.GetMicroAllocator(),
                                    thresholds);
}

TF_LITE_MICRO_TEST(TestConvModelMemoryThreshold) {
  tflite::AllOpsResolver all_ops_resolver;
  tflite::RecordingMicroInterpreter interpreter(
      tflite::GetModel(kTestConvModelData), all_ops_resolver,
      test_conv_tensor_arena, kTestConvModelArenaSize, micro_test::reporter);

  interpreter.AllocateTensors();

  ModelAllocationThresholds thresholds;
  thresholds.tensor_count = kTestConvModelTensorCount;
  thresholds.node_and_registration_count =
      kTestConvModelNodeAndRegistrationCount;
  thresholds.total_alloc_size = kTestConvModelTotalSize;
  thresholds.head_alloc_size = kTestConvModelHeadSize;
  thresholds.tail_alloc_size = kTestConvModelTailSize;
  thresholds.tensor_quantization_data_size =
      kTestConvModelTfLiteTensorQuantizationDataSize;
  thresholds.op_runtime_data_size = kTestConvModelOpRuntimeDataSize;

  ValidateModelAllocationThresholds(interpreter.GetMicroAllocator(),
                                    thresholds);
}

TF_LITE_MICRO_TESTS_END
<commit_msg>Fix memory "near" tests for thresholds.<commit_after>/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/

#include <stdint.h>

#include "tensorflow/lite/micro/all_ops_resolver.h"
#include "tensorflow/lite/micro/benchmarks/keyword_scrambled_model_data.h"
#include "tensorflow/lite/micro/recording_micro_allocator.h"
#include "tensorflow/lite/micro/recording_micro_interpreter.h"
#include "tensorflow/lite/micro/testing/micro_test.h"
#include "tensorflow/lite/micro/testing/test_conv_model.h"

/**
 * Tests to ensure arena memory allocation does not regress by more than 3%.
 */

namespace {

// Ensure memory doesn't expand more that 3%:
constexpr float kAllocationThreshold = 0.03;

// TODO(b/160617245): Record persistent allocations to provide a more accurate
// number here.
constexpr float kAllocationTailMiscCeiling = 2 * 1024;

const bool kIs64BitSystem = (sizeof(void*) == 8);

constexpr int kKeywordModelTensorArenaSize = 22 * 1024;
uint8_t keyword_model_tensor_arena[kKeywordModelTensorArenaSize];

constexpr int kKeywordModelTensorCount = 54;
constexpr int kKeywordModelNodeAndRegistrationCount = 15;

// NOTE: These values are measured on x86-64:
// TODO(b/158651472): Consider auditing these values on non-64 bit systems.
//
// Run this test with '--copt=-DTF_LITE_STATIC_MEMORY' to get optimized memory
// runtime values:
#ifdef TF_LITE_STATIC_MEMORY
constexpr int kKeywordModelTotalSize = 18192;
constexpr int kKeywordModelTailSize = 17520;
#else
constexpr int kKeywordModelTotalSize = 21152;
constexpr int kKeywordModelTailSize = 20480;
#endif
constexpr int kKeywordModelHeadSize = 672;
constexpr int kKeywordModelTfLiteTensorVariableBufferDataSize = 10240;
constexpr int kKeywordModelTfLiteTensorQuantizationDataSize = 1728;
constexpr int kKeywordModelOpRuntimeDataSize = 148;

constexpr int kTestConvModelArenaSize = 12 * 1024;
uint8_t test_conv_tensor_arena[kTestConvModelArenaSize];

constexpr int kTestConvModelTensorCount = 15;
constexpr int kTestConvModelNodeAndRegistrationCount = 7;

// NOTE: These values are measured on x86-64:
// TODO(b/158651472): Consider auditing these values on non-64 bit systems.
#ifdef TF_LITE_STATIC_MEMORY
constexpr int kTestConvModelTotalSize = 10816;
constexpr int kTestConvModelTailSize = 3072;
#else
constexpr int kTestConvModelTotalSize = 11712;
constexpr int kTestConvModelTailSize = 3968;
#endif
constexpr int kTestConvModelHeadSize = 7744;
constexpr int kTestConvModelTfLiteTensorQuantizationDataSize = 768;
constexpr int kTestConvModelOpRuntimeDataSize = 136;

struct ModelAllocationThresholds {
  size_t tensor_count = 0;
  size_t node_and_registration_count = 0;
  size_t total_alloc_size = 0;
  size_t head_alloc_size = 0;
  size_t tail_alloc_size = 0;
  size_t tensor_variable_buffer_data_size = 0;
  size_t tensor_quantization_data_size = 0;
  size_t op_runtime_data_size = 0;
};

void EnsureAllocatedSizeThreshold(const char* allocation_type, size_t actual,
                                  size_t expected) {
  // TODO(b/158651472): Better auditing of non-64 bit systems:
  if (kIs64BitSystem) {
    // 64-bit systems should check floor and ceiling to catch memory savings:
    TF_LITE_MICRO_EXPECT_NEAR(actual, expected,
                              expected * kAllocationThreshold);
    if (actual != expected) {
      TF_LITE_REPORT_ERROR(micro_test::reporter,
                           "%s threshold failed: %d != %d", allocation_type,
                           actual, expected);
    }
  } else {
    // Non-64 bit systems should just expect allocation does not exceed the
    // ceiling:
    TF_LITE_MICRO_EXPECT_LE(actual, expected + expected * kAllocationThreshold);
  }
}

void ValidateModelAllocationThresholds(
    const tflite::RecordingMicroAllocator& allocator,
    const ModelAllocationThresholds& thresholds) {
  allocator.PrintAllocations();

  EnsureAllocatedSizeThreshold(
      "Total", allocator.GetSimpleMemoryAllocator()->GetUsedBytes(),
      thresholds.total_alloc_size);
  EnsureAllocatedSizeThreshold(
      "Head", allocator.GetSimpleMemoryAllocator()->GetHeadUsedBytes(),
      thresholds.head_alloc_size);
  EnsureAllocatedSizeThreshold(
      "Tail", allocator.GetSimpleMemoryAllocator()->GetTailUsedBytes(),
      thresholds.tail_alloc_size);
  EnsureAllocatedSizeThreshold(
      "TfLiteTensor",
      allocator
          .GetRecordedAllocation(
              tflite::RecordedAllocationType::kTfLiteTensorArray)
          .used_bytes,
      sizeof(TfLiteTensor) * thresholds.tensor_count);
  EnsureAllocatedSizeThreshold(
      "VariableBufferData",
      allocator
          .GetRecordedAllocation(
              tflite::RecordedAllocationType::kTfLiteTensorVariableBufferData)
          .used_bytes,
      thresholds.tensor_variable_buffer_data_size);
  EnsureAllocatedSizeThreshold(
      "QuantizationData",
      allocator
          .GetRecordedAllocation(tflite::RecordedAllocationType::
                                     kTfLiteTensorArrayQuantizationData)
          .used_bytes,
      thresholds.tensor_quantization_data_size);
  EnsureAllocatedSizeThreshold(
      "NodeAndRegistration",
      allocator
          .GetRecordedAllocation(
              tflite::RecordedAllocationType::kNodeAndRegistrationArray)
          .used_bytes,
      sizeof(tflite::NodeAndRegistration) *
          thresholds.node_and_registration_count);
  EnsureAllocatedSizeThreshold(
      "OpData",
      allocator.GetRecordedAllocation(tflite::RecordedAllocationType::kOpData)
          .used_bytes,
      thresholds.op_runtime_data_size);

  // Ensure tail allocation recording is not missing any large chunks:
  size_t tail_est_length = sizeof(TfLiteTensor) * thresholds.tensor_count +
                           thresholds.tensor_quantization_data_size +
                           thresholds.tensor_variable_buffer_data_size +
                           sizeof(tflite::NodeAndRegistration) *
                               thresholds.node_and_registration_count +
                           thresholds.op_runtime_data_size;
  TF_LITE_MICRO_EXPECT_LE(thresholds.tail_alloc_size - tail_est_length,
                          kAllocationTailMiscCeiling);
}

}  // namespace

TF_LITE_MICRO_TESTS_BEGIN

TF_LITE_MICRO_TEST(TestKeywordModelMemoryThreshold) {
  tflite::AllOpsResolver all_ops_resolver;
  tflite::RecordingMicroInterpreter interpreter(
      tflite::GetModel(g_keyword_scrambled_model_data), all_ops_resolver,
      keyword_model_tensor_arena, kKeywordModelTensorArenaSize,
      micro_test::reporter);

  interpreter.AllocateTensors();

  ModelAllocationThresholds thresholds;
  thresholds.tensor_count = kKeywordModelTensorCount;
  thresholds.node_and_registration_count =
      kKeywordModelNodeAndRegistrationCount;
  thresholds.total_alloc_size = kKeywordModelTotalSize;
  thresholds.head_alloc_size = kKeywordModelHeadSize;
  thresholds.tail_alloc_size = kKeywordModelTailSize;
  thresholds.tensor_variable_buffer_data_size =
      kKeywordModelTfLiteTensorVariableBufferDataSize;
  thresholds.tensor_quantization_data_size =
      kKeywordModelTfLiteTensorQuantizationDataSize;
  thresholds.op_runtime_data_size = kKeywordModelOpRuntimeDataSize;

  ValidateModelAllocationThresholds(interpreter.GetMicroAllocator(),
                                    thresholds);
}

TF_LITE_MICRO_TEST(TestConvModelMemoryThreshold) {
  tflite::AllOpsResolver all_ops_resolver;
  tflite::RecordingMicroInterpreter interpreter(
      tflite::GetModel(kTestConvModelData), all_ops_resolver,
      test_conv_tensor_arena, kTestConvModelArenaSize, micro_test::reporter);

  interpreter.AllocateTensors();

  ModelAllocationThresholds thresholds;
  thresholds.tensor_count = kTestConvModelTensorCount;
  thresholds.node_and_registration_count =
      kTestConvModelNodeAndRegistrationCount;
  thresholds.total_alloc_size = kTestConvModelTotalSize;
  thresholds.head_alloc_size = kTestConvModelHeadSize;
  thresholds.tail_alloc_size = kTestConvModelTailSize;
  thresholds.tensor_quantization_data_size =
      kTestConvModelTfLiteTensorQuantizationDataSize;
  thresholds.op_runtime_data_size = kTestConvModelOpRuntimeDataSize;

  ValidateModelAllocationThresholds(interpreter.GetMicroAllocator(),
                                    thresholds);
}

TF_LITE_MICRO_TESTS_END
<|endoftext|>


<commit_before><commit_msg>Added `#include` guard.<commit_after><|endoftext|>


<commit_before>// The libMesh Finite Element Library.
// Copyright (C) 2002-2014 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner

// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.

// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.

// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

#include "libmesh/diff_system.h"
#include "libmesh/euler2_solver.h"

namespace libMesh
{

Euler2Solver::Euler2Solver (sys_type& s)
  : UnsteadySolver(s), theta(1.)
{
}

Euler2Solver::~Euler2Solver ()
{
}

Real Euler2Solver::error_order() const
{
  if (theta == 0.5)
    return 2.;
  return 1.;
}

bool Euler2Solver::element_residual (bool request_jacobian,
                                     DiffContext &context)
{
  return this->_general_residual(request_jacobian,
                                 context,
                                 &DifferentiablePhysics::mass_residual,
                                 &DifferentiablePhysics::_eulerian_time_deriv,
                                 &DifferentiablePhysics::element_constraint,
                                 &DiffContext::elem_reinit);
}

bool Euler2Solver::side_residual (bool request_jacobian,
                                  DiffContext &context)
{
  return this->_general_residual(request_jacobian,
                                 context,
                                 &DifferentiablePhysics::side_mass_residual,
                                 &DifferentiablePhysics::side_time_derivative,
                                 &DifferentiablePhysics::side_constraint,
                                 &DiffContext::elem_side_reinit);
}

bool Euler2Solver::nonlocal_residual (bool request_jacobian,
                                      DiffContext &context)
{
  return this->_general_residual(request_jacobian,
                                 context,
                                 &DifferentiablePhysics::nonlocal_mass_residual,
                                 &DifferentiablePhysics::nonlocal_time_derivative,
                                 &DifferentiablePhysics::nonlocal_constraint,
                                 &DiffContext::nonlocal_reinit);
}

bool Euler2Solver::_general_residual (bool request_jacobian,
                                      DiffContext &context,
                                      ResFuncType mass,
                                      ResFuncType time_deriv,
                                      ResFuncType constraint,
                                      ReinitFuncType reinit_func)
{
  unsigned int n_dofs = context.get_elem_solution().size();

  // Local nonlinear solution at old timestep
  DenseVector<Number> old_elem_solution(n_dofs);
  for (unsigned int i=0; i != n_dofs; ++i)
    old_elem_solution(i) =
      old_nonlinear_solution(context.get_dof_indices()[i]);

  // Local time derivative of solution
  context.get_elem_solution_rate() = context.get_elem_solution();
  context.get_elem_solution_rate() -= old_elem_solution;
  context.elem_solution_rate_derivative = 1 / _system.deltat;
  context.get_elem_solution_rate() *=
    context.elem_solution_rate_derivative;

  // Our first evaluations are at the final elem_solution
  context.elem_solution_derivative = 1.0;

  // If a fixed solution is requested, we'll use the elem_solution
  // at the new timestep
  // FIXME - should this be the theta solution instead?
  if (_system.use_fixed_solution)
    context.get_elem_fixed_solution() = context.get_elem_solution();

  context.fixed_solution_derivative = 1.0;

  // We might need to save the old jacobian in case one of our physics
  // terms later is unable to update it analytically.
  DenseMatrix<Number> old_elem_jacobian(n_dofs, n_dofs);
  if (request_jacobian)
    old_elem_jacobian.swap(context.get_elem_jacobian());

  // Local time derivative of solution
  context.get_elem_solution_rate() = context.get_elem_solution();
  context.get_elem_solution_rate() -= old_elem_solution;
  context.elem_solution_rate_derivative = 1 / _system.deltat;
  context.get_elem_solution_rate() *=
    context.elem_solution_rate_derivative;

  // First, evaluate time derivative at the new timestep.
  // The element should already be in the proper place
  // even for a moving mesh problem.
  bool jacobian_computed =
    (_system.*time_deriv)(request_jacobian, context);

  // Next, evaluate the mass residual at the new timestep

  jacobian_computed = (_system.*mass)(jacobian_computed, context) &&
    jacobian_computed;

  // Add the constraint term
  jacobian_computed = (_system.*constraint)(jacobian_computed, context) &&
    jacobian_computed;

  // Add the time-dependent term for the old solution

  // Make sure elem_solution is set up for elem_reinit to use
  // Move elem_->old_, old_->elem_
  context.get_elem_solution().swap(old_elem_solution);
  context.elem_solution_derivative = 0.0;

  // Move the mesh into place first if necessary
  (context.*reinit_func)(0.);

  jacobian_computed =
    (_system.*time_deriv)(jacobian_computed, context) &&
    jacobian_computed;

  // Add the mass residual term for the old solution

  // Evaluating the mass residual at both old and new timesteps will be
  // redundant in most problems but may be necessary for time accuracy
  // or stability in moving mesh problems or problems with user-overridden
  // mass_residual functions

  jacobian_computed =
    (_system.*mass)(jacobian_computed, context) &&
    jacobian_computed;

  // Restore the elem_solution
  // Move elem_->elem_, old_->old_
  context.get_elem_solution().swap(old_elem_solution);
  context.elem_solution_derivative = 1;

  // Restore the elem position if necessary
  (context.*reinit_func)(1.);

  // Add back (or restore) the old jacobian
  if (request_jacobian)
    {
      if (jacobian_computed)
        context.get_elem_jacobian() += old_elem_jacobian;
      else
        context.get_elem_jacobian().swap(old_elem_jacobian);
    }

  return jacobian_computed;
}

} // namespace libMesh
<commit_msg>Fix new Euler2Solver evaluations<commit_after>// The libMesh Finite Element Library.
// Copyright (C) 2002-2014 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner

// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.

// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.

// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

#include "libmesh/diff_system.h"
#include "libmesh/euler2_solver.h"

namespace libMesh
{

Euler2Solver::Euler2Solver (sys_type& s)
  : UnsteadySolver(s), theta(1.)
{
}

Euler2Solver::~Euler2Solver ()
{
}

Real Euler2Solver::error_order() const
{
  if (theta == 0.5)
    return 2.;
  return 1.;
}

bool Euler2Solver::element_residual (bool request_jacobian,
                                     DiffContext &context)
{
  return this->_general_residual(request_jacobian,
                                 context,
                                 &DifferentiablePhysics::mass_residual,
                                 &DifferentiablePhysics::_eulerian_time_deriv,
                                 &DifferentiablePhysics::element_constraint,
                                 &DiffContext::elem_reinit);
}

bool Euler2Solver::side_residual (bool request_jacobian,
                                  DiffContext &context)
{
  return this->_general_residual(request_jacobian,
                                 context,
                                 &DifferentiablePhysics::side_mass_residual,
                                 &DifferentiablePhysics::side_time_derivative,
                                 &DifferentiablePhysics::side_constraint,
                                 &DiffContext::elem_side_reinit);
}

bool Euler2Solver::nonlocal_residual (bool request_jacobian,
                                      DiffContext &context)
{
  return this->_general_residual(request_jacobian,
                                 context,
                                 &DifferentiablePhysics::nonlocal_mass_residual,
                                 &DifferentiablePhysics::nonlocal_time_derivative,
                                 &DifferentiablePhysics::nonlocal_constraint,
                                 &DiffContext::nonlocal_reinit);
}

bool Euler2Solver::_general_residual (bool request_jacobian,
                                      DiffContext &context,
                                      ResFuncType mass,
                                      ResFuncType time_deriv,
                                      ResFuncType constraint,
                                      ReinitFuncType reinit_func)
{
  unsigned int n_dofs = context.get_elem_solution().size();

  // Local nonlinear solution at old timestep
  DenseVector<Number> old_elem_solution(n_dofs);
  for (unsigned int i=0; i != n_dofs; ++i)
    old_elem_solution(i) =
      old_nonlinear_solution(context.get_dof_indices()[i]);

  // Local time derivative of solution
  context.get_elem_solution_rate() = context.get_elem_solution();
  context.get_elem_solution_rate() -= old_elem_solution;
  context.elem_solution_rate_derivative = 1 / _system.deltat;
  context.get_elem_solution_rate() *=
    context.elem_solution_rate_derivative;

  // Our first evaluations are at the final elem_solution
  context.elem_solution_derivative = 1.0;

  // If a fixed solution is requested, we'll use the elem_solution
  // at the new timestep
  // FIXME - should this be the theta solution instead?
  if (_system.use_fixed_solution)
    context.get_elem_fixed_solution() = context.get_elem_solution();

  context.fixed_solution_derivative = 1.0;

  // We need to save the old jacobian and old residual since we'll be
  // multiplying some of the new contributions by theta or 1-theta
  DenseMatrix<Number> old_elem_jacobian(n_dofs, n_dofs);
  DenseVector<Number> old_elem_residual(n_dofs);
  old_elem_residual.swap(context.get_elem_residual());
  if (request_jacobian)
    old_elem_jacobian.swap(context.get_elem_jacobian());

  // Local time derivative of solution
  context.get_elem_solution_rate() = context.get_elem_solution();
  context.get_elem_solution_rate() -= old_elem_solution;
  context.elem_solution_rate_derivative = 1 / _system.deltat;
  context.get_elem_solution_rate() *=
    context.elem_solution_rate_derivative;

  // First, evaluate time derivative at the new timestep.
  // The element should already be in the proper place
  // even for a moving mesh problem.
  bool jacobian_computed =
    (_system.*time_deriv)(request_jacobian, context);

  // Next, evaluate the mass residual at the new timestep

  jacobian_computed = (_system.*mass)(jacobian_computed, context) &&
    jacobian_computed;

  // Add the constraint term
  jacobian_computed = (_system.*constraint)(jacobian_computed, context) &&
    jacobian_computed;

  // The new solution's contribution is scaled by theta
  context.get_elem_residual() *= theta;
  context.get_elem_jacobian() *= theta;

  // Save the new solution's term
  DenseMatrix<Number> elem_jacobian_newterm(n_dofs, n_dofs);
  DenseVector<Number> elem_residual_newterm(n_dofs);
  elem_residual_newterm.swap(context.get_elem_residual());
  if (request_jacobian)
    elem_jacobian_newterm.swap(context.get_elem_jacobian());

  // Add the time-dependent term for the old solution

  // Make sure elem_solution is set up for elem_reinit to use
  // Move elem_->old_, old_->elem_
  context.get_elem_solution().swap(old_elem_solution);
  context.elem_solution_derivative = 0.0;

  // Move the mesh into place first if necessary
  (context.*reinit_func)(0.);

  jacobian_computed =
    (_system.*time_deriv)(jacobian_computed, context) &&
    jacobian_computed;

  // Add the mass residual term for the old solution

  // Evaluating the mass residual at both old and new timesteps will be
  // redundant in most problems but may be necessary for time accuracy
  // or stability in moving mesh problems or problems with user-overridden
  // mass_residual functions

  jacobian_computed =
    (_system.*mass)(jacobian_computed, context) &&
    jacobian_computed;

  // The old solution's contribution is scaled by (1-theta)
  context.get_elem_residual() *= (1-theta);
  context.get_elem_jacobian() *= (1-theta);

  // Restore the elem_solution
  // Move elem_->elem_, old_->old_
  context.get_elem_solution().swap(old_elem_solution);
  context.elem_solution_derivative = 1;

  // Restore the elem position if necessary
  (context.*reinit_func)(1.);

  // Add back (or restore) the old residual/jacobian
  context.get_elem_residual() += old_elem_residual;
  if (request_jacobian)
    {
      if (jacobian_computed)
        context.get_elem_jacobian() += old_elem_jacobian;
      else
        context.get_elem_jacobian().swap(old_elem_jacobian);
    }

  // Add the saved new-solution terms
  context.get_elem_residual() += elem_residual_newterm;
  if (jacobian_computed)
    context.get_elem_jacobian() += elem_jacobian_newterm;

  return jacobian_computed;
}

} // namespace libMesh
<|endoftext|>


<commit_before>
#include "diff_system.h"
#include "libmesh_logging.h"
#include "linear_solver.h"
#include "newton_solver.h"
#include "numeric_vector.h"
#include "sparse_matrix.h"
#include "dof_map.h"

NewtonSolver::NewtonSolver (sys_type& s)
  : Parent(s),
    require_residual_reduction(false),
    minsteplength(0.0),
    linear_solver(LinearSolver<Number>::build())
{
}

NewtonSolver::~NewtonSolver ()
{
}

void NewtonSolver::reinit()
{
  Parent::reinit();

  linear_solver->clear();
}

void NewtonSolver::solve()
{
  START_LOG("solve()", "NewtonSolver");

// Amount by which nonlinear residual should exceed linear solver
// tolerance
const Real relative_tolerance = 1.e-3;

  NumericVector<Number> &solution = *(_system.solution);
  NumericVector<Number> &newton_iterate =
    _system.get_vector("_nonlinear_solution");
  newton_iterate.close();
  solution.close();

  NumericVector<Number> &rhs = *(_system.rhs);

  SparseMatrix<Number> &matrix = *(_system.matrix);

  // Prepare to take incomplete steps
  Real last_residual=0.;

  // Set starting linear tolerance
  Real current_linear_tolerance = initial_linear_tolerance;

  // Now we begin the nonlinear loop
  for (unsigned int l=0; l<max_nonlinear_iterations; ++l)
    {
      PAUSE_LOG("solve()", "NewtonSolver");
      _system.assembly(true, true);
      RESTART_LOG("solve()", "NewtonSolver");
      rhs.close();
      Real current_residual = rhs.l2_norm();
      last_residual = current_residual;

      max_residual_norm = std::max (current_residual,
                                    max_residual_norm);

      // Compute the l2 norm of the whole solution
      Real norm_total = newton_iterate.l2_norm();

      max_solution_norm = std::max(max_solution_norm, norm_total);

      if (!quiet)
        std::cout << "Nonlinear Residual: "
                  << current_residual << std::endl;

      // Make sure our linear tolerance is low enough
      if (current_linear_tolerance > current_residual * relative_tolerance)
        {
          current_linear_tolerance = current_residual * relative_tolerance;
        }

      // But don't let it be zero
      if (current_linear_tolerance == 0.)
        {
          current_linear_tolerance = TOLERANCE * TOLERANCE;
        }

      // At this point newton_iterate is the current guess, and
      // solution is now about to become the NEGATIVE of the next
      // Newton step.

      // Our best initial guess for the solution is zero!
      solution.zero();

      if (!quiet)
        std::cout << "Linear solve starting" << std::endl;

      PAUSE_LOG("solve()", "NewtonSolver");
      // Solve the linear system.  Two cases:
      const std::pair<unsigned int, Real> rval =
        (_system.have_matrix("Preconditioner")) ?
      // 1.) User-supplied preconditioner
        linear_solver->solve (matrix, _system.get_matrix("Preconditioner"),
                              solution, rhs, current_linear_tolerance,
                              max_linear_iterations) :
      // 2.) Use system matrix for the preconditioner
        linear_solver->solve (matrix, solution, rhs,
                              current_linear_tolerance,
                              max_linear_iterations);
      // We may need to localize a parallel solution
      _system.update ();
      RESTART_LOG("solve()", "NewtonSolver");
      // The linear solver may not have fit our constraints exactly
      _system.get_dof_map().enforce_constraints_exactly(_system);

      if (!quiet)
        std::cout << "Linear solve finished, step " << rval.first
                  << ", residual " << rval.second
                  << ", tolerance " << current_linear_tolerance
                  << std::endl;

      // Compute the l2 norm of the nonlinear update
      Real norm_delta = solution.l2_norm();

      if (!quiet)
        std::cout << "Trying full Newton step" << std::endl;
      // Take a full Newton step
      newton_iterate.add (-1., solution);
      newton_iterate.close();

      // Check residual with full Newton step
      Real steplength = 1.;
      PAUSE_LOG("solve()", "NewtonSolver");
      _system.assembly(true, false);
      RESTART_LOG("solve()", "NewtonSolver");

      rhs.close();
      current_residual = rhs.l2_norm();

      // backtrack if necessary
      if (require_residual_reduction)
        {
          // but don't fiddle around if we've already converged
          if (test_convergence(current_residual, norm_delta))
            {
              if (!quiet)
		print_convergence(l, current_residual, norm_delta);
              break;
            }

          while (current_residual > last_residual)
            {
              // Reduce step size to 1/2, 1/4, etc.
              steplength /= 2.;
              norm_delta /= 2.;
              if (!quiet)
                std::cout << "Shrinking Newton step to "
                          << steplength << std::endl;
              newton_iterate.add (steplength, solution);
              newton_iterate.close();

              // Check residual with fractional Newton step
              PAUSE_LOG("solve()", "NewtonSolver");
              _system.assembly (true, false);
              RESTART_LOG("solve()", "NewtonSolver");
              current_residual = rhs.l2_norm();
              if (!quiet)
                std::cout << "Current Residual: "
                          << current_residual << std::endl;

              if (steplength/2. < minsteplength &&
                  current_residual > last_residual)
                {
                  if (!quiet)
                    std::cout << "Inexact Newton step FAILED at step "
                              << l << std::endl;

                  error();
                }
            }
        }

      // Compute the l2 norm of the whole solution
      norm_total = newton_iterate.l2_norm();

      max_solution_norm = std::max(max_solution_norm, norm_total);

      // Print out information for the
      // nonlinear iterations.
      if (!quiet)
        std::cout << "  Nonlinear step: |du|/|u| = "
                  << norm_delta / norm_total
                  << ", |du| = " << norm_delta
                  << std::endl;

      // Terminate the solution iteration if the difference between
      // this iteration and the last is sufficiently small.
      if (!quiet)
        print_convergence(l, current_residual,
                          norm_delta / steplength);
      if (test_convergence(current_residual, norm_delta / steplength))
        {
          break;
        }
      if (l >= max_nonlinear_iterations - 1)
        {
          std::cout << "  Nonlinear solver DIVERGED at step " << l
                    << " with norm " << norm_total
                    << std::endl;
          error();
          continue;
        }
    } // end nonlinear loop

  // Copy the final nonlinear iterate into the current_solution,
  // for other libMesh functions that expect it

  solution = newton_iterate;
  solution.close();

  // We may need to localize a parallel solution
  _system.update ();

  STOP_LOG("solve()", "NewtonSolver");
}

bool NewtonSolver::test_convergence(Real current_residual,
                                    Real step_norm)
{
  // We haven't converged unless we pass a convergence test
  bool has_converged = false;

  // Is our absolute residual low enough?
  if (current_residual < absolute_residual_tolerance)
    has_converged = true;

  // Is our relative residual low enough?
  if ((current_residual / max_residual_norm) <
      relative_residual_tolerance)
    has_converged = true;

  // Is our absolute Newton step size small enough?
  if (step_norm < absolute_step_tolerance)
    has_converged = true;

  // Is our relative Newton step size small enough?
  if (step_norm / max_solution_norm <
      relative_step_tolerance)
    has_converged = true;

  return has_converged;
}

void NewtonSolver::print_convergence(unsigned int step_num,
                                     Real current_residual,
                                     Real step_norm)
{
  // Is our absolute residual low enough?
  if (current_residual < absolute_residual_tolerance)
    {
      std::cout << "  Nonlinear solver converged, step " << step_num
                << ", residual " << current_residual
                << std::endl;
    }
  else if (absolute_residual_tolerance)
    {
      std::cout << "  Nonlinear solver current_residual "
                << current_residual << " > "
                << (absolute_residual_tolerance) << std::endl;
    }

  // Is our relative residual low enough?
  if ((current_residual / max_residual_norm) <
      relative_residual_tolerance)
    {
      std::cout << "  Nonlinear solver converged, step " << step_num
                << ", residual reduction "
                << current_residual / max_residual_norm
                << " < " << relative_residual_tolerance
                << std::endl;
    }
  else if (relative_residual_tolerance)
    {
      if (!quiet)
        std::cout << "  Nonlinear solver relative residual "
                  << (current_residual / max_residual_norm)
                  << " > " << relative_residual_tolerance
                  << std::endl;
    }

  // Is our absolute Newton step size small enough?
  if (step_norm < absolute_step_tolerance)
    {
      std::cout << "  Nonlinear solver converged, step " << step_num
                << ", absolute step size "
                << step_norm
                << " < " << absolute_step_tolerance
                << std::endl;
    }
  else if (absolute_step_tolerance)
    {
      std::cout << "  Nonlinear solver absolute step size "
                << step_norm
                << " > " << absolute_step_tolerance
                << std::endl;
    }

  // Is our relative Newton step size small enough?
  if (step_norm / max_solution_norm <
      relative_step_tolerance)
    {
      std::cout << "  Nonlinear solver converged, step " << step_num
                << ", relative step size "
                << (step_norm / max_solution_norm)
                << " < " << relative_step_tolerance
                << std::endl;
    }
  else if (relative_step_tolerance)
    {
      std::cout << "  Nonlinear solver relative step size "
                << (step_norm / max_solution_norm)
                << " > " << relative_step_tolerance
                << std::endl;
    }
}
<commit_msg>More verbosity<commit_after>
#include "diff_system.h"
#include "libmesh_logging.h"
#include "linear_solver.h"
#include "newton_solver.h"
#include "numeric_vector.h"
#include "sparse_matrix.h"
#include "dof_map.h"

NewtonSolver::NewtonSolver (sys_type& s)
  : Parent(s),
    require_residual_reduction(false),
    minsteplength(0.0),
    linear_solver(LinearSolver<Number>::build())
{
}

NewtonSolver::~NewtonSolver ()
{
}

void NewtonSolver::reinit()
{
  Parent::reinit();

  linear_solver->clear();
}

void NewtonSolver::solve()
{
  START_LOG("solve()", "NewtonSolver");

// Amount by which nonlinear residual should exceed linear solver
// tolerance
const Real relative_tolerance = 1.e-3;

  NumericVector<Number> &solution = *(_system.solution);
  NumericVector<Number> &newton_iterate =
    _system.get_vector("_nonlinear_solution");
  newton_iterate.close();
  solution.close();

  NumericVector<Number> &rhs = *(_system.rhs);

  SparseMatrix<Number> &matrix = *(_system.matrix);

  // Prepare to take incomplete steps
  Real last_residual=0.;

  // Set starting linear tolerance
  Real current_linear_tolerance = initial_linear_tolerance;

  // Now we begin the nonlinear loop
  for (unsigned int l=0; l<max_nonlinear_iterations; ++l)
    {
      if (!quiet)
        std::cout << "Assembling System" << std::endl;

      PAUSE_LOG("solve()", "NewtonSolver");
      _system.assembly(true, true);
      RESTART_LOG("solve()", "NewtonSolver");
      rhs.close();
      Real current_residual = rhs.l2_norm();
      last_residual = current_residual;

      max_residual_norm = std::max (current_residual,
                                    max_residual_norm);

      // Compute the l2 norm of the whole solution
      Real norm_total = newton_iterate.l2_norm();

      max_solution_norm = std::max(max_solution_norm, norm_total);

      if (!quiet)
        std::cout << "Nonlinear Residual: "
                  << current_residual << std::endl;

      // Make sure our linear tolerance is low enough
      if (current_linear_tolerance > current_residual * relative_tolerance)
        {
          current_linear_tolerance = current_residual * relative_tolerance;
        }

      // But don't let it be zero
      if (current_linear_tolerance == 0.)
        {
          current_linear_tolerance = TOLERANCE * TOLERANCE;
        }

      // At this point newton_iterate is the current guess, and
      // solution is now about to become the NEGATIVE of the next
      // Newton step.

      // Our best initial guess for the solution is zero!
      solution.zero();

      if (!quiet)
        std::cout << "Linear solve starting" << std::endl;

      PAUSE_LOG("solve()", "NewtonSolver");
      // Solve the linear system.  Two cases:
      const std::pair<unsigned int, Real> rval =
        (_system.have_matrix("Preconditioner")) ?
      // 1.) User-supplied preconditioner
        linear_solver->solve (matrix, _system.get_matrix("Preconditioner"),
                              solution, rhs, current_linear_tolerance,
                              max_linear_iterations) :
      // 2.) Use system matrix for the preconditioner
        linear_solver->solve (matrix, solution, rhs,
                              current_linear_tolerance,
                              max_linear_iterations);
      // We may need to localize a parallel solution
      _system.update ();
      RESTART_LOG("solve()", "NewtonSolver");
      // The linear solver may not have fit our constraints exactly
      _system.get_dof_map().enforce_constraints_exactly(_system);

      if (!quiet)
        std::cout << "Linear solve finished, step " << rval.first
                  << ", residual " << rval.second
                  << ", tolerance " << current_linear_tolerance
                  << std::endl;

      // Compute the l2 norm of the nonlinear update
      Real norm_delta = solution.l2_norm();

      if (!quiet)
        std::cout << "Trying full Newton step" << std::endl;
      // Take a full Newton step
      newton_iterate.add (-1., solution);
      newton_iterate.close();

      // Check residual with full Newton step
      Real steplength = 1.;
      PAUSE_LOG("solve()", "NewtonSolver");
      _system.assembly(true, false);
      RESTART_LOG("solve()", "NewtonSolver");

      rhs.close();
      current_residual = rhs.l2_norm();

      // backtrack if necessary
      if (require_residual_reduction)
        {
          // but don't fiddle around if we've already converged
          if (test_convergence(current_residual, norm_delta))
            {
              if (!quiet)
		print_convergence(l, current_residual, norm_delta);
              break;
            }

          while (current_residual > last_residual)
            {
              // Reduce step size to 1/2, 1/4, etc.
              steplength /= 2.;
              norm_delta /= 2.;
              if (!quiet)
                std::cout << "Shrinking Newton step to "
                          << steplength << std::endl;
              newton_iterate.add (steplength, solution);
              newton_iterate.close();

              // Check residual with fractional Newton step
              PAUSE_LOG("solve()", "NewtonSolver");
              _system.assembly (true, false);
              RESTART_LOG("solve()", "NewtonSolver");
              current_residual = rhs.l2_norm();
              if (!quiet)
                std::cout << "Current Residual: "
                          << current_residual << std::endl;

              if (steplength/2. < minsteplength &&
                  current_residual > last_residual)
                {
                  if (!quiet)
                    std::cout << "Inexact Newton step FAILED at step "
                              << l << std::endl;

                  error();
                }
            }
        }

      // Compute the l2 norm of the whole solution
      norm_total = newton_iterate.l2_norm();

      max_solution_norm = std::max(max_solution_norm, norm_total);

      // Print out information for the
      // nonlinear iterations.
      if (!quiet)
        std::cout << "  Nonlinear step: |du|/|u| = "
                  << norm_delta / norm_total
                  << ", |du| = " << norm_delta
                  << std::endl;

      // Terminate the solution iteration if the difference between
      // this iteration and the last is sufficiently small.
      if (!quiet)
        print_convergence(l, current_residual,
                          norm_delta / steplength);
      if (test_convergence(current_residual, norm_delta / steplength))
        {
          break;
        }
      if (l >= max_nonlinear_iterations - 1)
        {
          std::cout << "  Nonlinear solver DIVERGED at step " << l
                    << " with norm " << norm_total
                    << std::endl;
          error();
          continue;
        }
    } // end nonlinear loop

  // Copy the final nonlinear iterate into the current_solution,
  // for other libMesh functions that expect it

  solution = newton_iterate;
  solution.close();

  // We may need to localize a parallel solution
  _system.update ();

  STOP_LOG("solve()", "NewtonSolver");
}

bool NewtonSolver::test_convergence(Real current_residual,
                                    Real step_norm)
{
  // We haven't converged unless we pass a convergence test
  bool has_converged = false;

  // Is our absolute residual low enough?
  if (current_residual < absolute_residual_tolerance)
    has_converged = true;

  // Is our relative residual low enough?
  if ((current_residual / max_residual_norm) <
      relative_residual_tolerance)
    has_converged = true;

  // Is our absolute Newton step size small enough?
  if (step_norm < absolute_step_tolerance)
    has_converged = true;

  // Is our relative Newton step size small enough?
  if (step_norm / max_solution_norm <
      relative_step_tolerance)
    has_converged = true;

  return has_converged;
}

void NewtonSolver::print_convergence(unsigned int step_num,
                                     Real current_residual,
                                     Real step_norm)
{
  // Is our absolute residual low enough?
  if (current_residual < absolute_residual_tolerance)
    {
      std::cout << "  Nonlinear solver converged, step " << step_num
                << ", residual " << current_residual
                << std::endl;
    }
  else if (absolute_residual_tolerance)
    {
      std::cout << "  Nonlinear solver current_residual "
                << current_residual << " > "
                << (absolute_residual_tolerance) << std::endl;
    }

  // Is our relative residual low enough?
  if ((current_residual / max_residual_norm) <
      relative_residual_tolerance)
    {
      std::cout << "  Nonlinear solver converged, step " << step_num
                << ", residual reduction "
                << current_residual / max_residual_norm
                << " < " << relative_residual_tolerance
                << std::endl;
    }
  else if (relative_residual_tolerance)
    {
      if (!quiet)
        std::cout << "  Nonlinear solver relative residual "
                  << (current_residual / max_residual_norm)
                  << " > " << relative_residual_tolerance
                  << std::endl;
    }

  // Is our absolute Newton step size small enough?
  if (step_norm < absolute_step_tolerance)
    {
      std::cout << "  Nonlinear solver converged, step " << step_num
                << ", absolute step size "
                << step_norm
                << " < " << absolute_step_tolerance
                << std::endl;
    }
  else if (absolute_step_tolerance)
    {
      std::cout << "  Nonlinear solver absolute step size "
                << step_norm
                << " > " << absolute_step_tolerance
                << std::endl;
    }

  // Is our relative Newton step size small enough?
  if (step_norm / max_solution_norm <
      relative_step_tolerance)
    {
      std::cout << "  Nonlinear solver converged, step " << step_num
                << ", relative step size "
                << (step_norm / max_solution_norm)
                << " < " << relative_step_tolerance
                << std::endl;
    }
  else if (relative_step_tolerance)
    {
      std::cout << "  Nonlinear solver relative step size "
                << (step_norm / max_solution_norm)
                << " > " << relative_step_tolerance
                << std::endl;
    }
}
<|endoftext|>


<commit_before>#include "paddle.hpp"

void PaddleBase::draw() const
{
	arduboy.fillRect(x, y, PADDLE_WIDTH, PADDLE_HEIGHT, WHITE);
}

void PaddleBase::move(int16_t distance)
{
	int16_t newY = y + distance;
	if (newY <= 0)
	{
		y = 1;
	}
	else if (newY > HEIGHT - PADDLE_HEIGHT - 1)
	{
		y = HEIGHT - PADDLE_HEIGHT - 1;
	}
	else
	{
		y = newY;
	}
}

void Player::move_impl()
{
	if (arduboy.pressed(UP_BUTTON))
	{
		PaddleBase::move(-1);
	}
	if (arduboy.pressed(DOWN_BUTTON))
	{
		PaddleBase::move(1);
	}
}

void Computer::move_impl()
{
	// bother the paddle if the ball is close or a random time
	if (ball.x > 90 || !random(15))
	{
		// move up if the ball is higher
		if (ball.y <= y)
		{
			PaddleBase::move(-1);
		}
		// move down if the ball is lower
		if (ball.y + BALL_SIZE >= y + PADDLE_HEIGHT)
		{
			PaddleBase::move(1);
		}
	}
}
<commit_msg>Tweak AI<commit_after>#include "paddle.hpp"

void PaddleBase::draw() const
{
	arduboy.fillRect(x, y, PADDLE_WIDTH, PADDLE_HEIGHT, WHITE);
}

void PaddleBase::move(int16_t distance)
{
	int16_t newY = y + distance;
	if (newY <= 0)
	{
		y = 1;
	}
	else if (newY > HEIGHT - PADDLE_HEIGHT - 1)
	{
		y = HEIGHT - PADDLE_HEIGHT - 1;
	}
	else
	{
		y = newY;
	}
}

void Player::move_impl()
{
	if (arduboy.pressed(UP_BUTTON))
	{
		PaddleBase::move(-1);
	}
	if (arduboy.pressed(DOWN_BUTTON))
	{
		PaddleBase::move(1);
	}
}

void Computer::move_impl()
{
	// bother the paddle if the ball is close or a random time
	if (ball.x > 80 || !random(10))
	{
		// move up if the ball is higher
		if (ball.y <= y)
		{
			PaddleBase::move(-1);
		}
		// move down if the ball is lower
		if (ball.y + BALL_SIZE >= y + PADDLE_HEIGHT)
		{
			PaddleBase::move(1);
		}
	}
}
<|endoftext|>


<commit_before>/** \file rna_main.cpp
 *
 * Defines the "vg rna" subcommand, which projects rna transcripts to graph paths and/or GBWT haplotypes.
 */

#include <unistd.h>
#include <getopt.h>
#include <chrono>

#include "subcommand.hpp"

#include "../transcriptome.hpp"
#include "../stream/vpkg.hpp"
#include "../stream/stream.hpp"

using namespace std;
using namespace vg;
using namespace vg::subcommand;

void help_rna(char** argv) {
    cerr << "\nusage: " << argv[0] << " rna [options] <graph.vg> > splice_graph.vg" << endl
         << "options:" << endl
         << "    -n, --transcripts FILE     transcript file(s) in gtf/gff format; may repeat (required)" << endl
         << "    -s, --transcript-tag NAME  use this attribute tag in the gtf/gff file(s) as id [transcript_id]" << endl
         << "    -l, --haplotypes FILE      project transcripts onto haplotypes in GBWT index file" << endl
         << "    -e, --use-embedded-paths   project transcripts onto embedded graph paths" << endl
         << "    -r, --filter-reference     filter reference transcripts" << endl
         << "    -c, --do-not-collapse      do not collapse identical transcripts across haplotypes" << endl
         << "    -a, --add-paths            add transcripts as embedded paths in the graph" << endl
         << "    -b, --write-gbwt FILE      write transcripts as path to GBWT index file" << endl
         << "    -g, --write-gam FILE       write transcripts as alignments to GAM file" << endl
         << "    -f, --write-fasta FILE     write transcripts as sequences to fasta file" << endl
         << "    -t, --threads INT          number of compute threads to use [1]" << endl
         << "    -p, --progress             show progress" << endl
         << "    -h, --help                 print help message" << endl
         << endl;
}

int32_t main_rna(int32_t argc, char** argv) {

    if (argc == 2) {
        help_rna(argv);
        return 1;
    }

    vector<string> transcript_filenames;
    string transcript_tag = "transcript_id";
    string haplotypes_filename;
    bool use_embedded_paths = false;
    bool filter_reference_transcript_paths = false;
    bool collapse_transcript_paths = true;
    bool add_transcript_paths = false;
    string gbwt_out_filename = "";
    string gam_out_filename = "";
    string fasta_out_filename = "";
    int32_t num_threads = 1;
    bool show_progress = false;

    int32_t c;
    optind = 2; // force optind past command positional argument

    while (true) {
        static struct option long_options[] =
            {
                {"transcripts",  no_argument, 0, 'n'},
                {"transcript-tag",  no_argument, 0, 's'},
                {"haplotypes",  no_argument, 0, 'l'},
                {"use-embeded-paths",  no_argument, 0, 'e'},
                {"filter-reference",  no_argument, 0, 'r'},
                {"do-not-collapse",  no_argument, 0, 'c'},
                {"add-paths",  no_argument, 0, 'a'},
                {"write-gbwt",  no_argument, 0, 'b'},
                {"write-gam",  no_argument, 0, 'g'},
                {"write-fasta",  no_argument, 0, 'f'},
                {"threads",  no_argument, 0, 't'},
                {"progress",  no_argument, 0, 'p'},
                {"help", no_argument, 0, 'h'},
                {0, 0, 0, 0}
            };

        int32_t option_index = 0;
        c = getopt_long(argc, argv, "n:s:l:ercab:g:f:t:ph?", long_options, &option_index);

        /* Detect the end of the options. */
        if (c == -1)
            break;

        switch (c)
        {

        case 'n':
            transcript_filenames.push_back(optarg);
            break;

        case 's':
            transcript_tag = optarg;
            break;

        case 'l':
            haplotypes_filename = optarg;
            break;

        case 'e':
            use_embedded_paths = true;
            break;

        case 'r':
            filter_reference_transcript_paths = true;
            break;

        case 'c':
            collapse_transcript_paths = false;
            break;

        case 'a':
            add_transcript_paths = true;
            break;

        case 'b':
            gbwt_out_filename = optarg;
            break;

        case 'g':
            gam_out_filename = optarg;
            break;

        case 'f':
            fasta_out_filename = optarg;
            break;

        case 't':
            num_threads = stoi(optarg);
            break;

        case 'p':
            show_progress = true;
            break;

        case 'h':
        case '?':
            /* getopt_long already printed an error message. */
            help_rna(argv);
            exit(1);
            break;

        default:
            abort();
        }
    }

    if (argc < optind + 1) {
        help_rna(argv);
        return 1;
    }

    if (transcript_filenames.empty()) {

        cerr << "[vg rna] ERROR: No transcripts were given. Use --transcripts FILE." << endl;
        return 1;
    }

    if (haplotypes_filename.empty() && !use_embedded_paths) {

        cerr << "[vg rna] ERROR: No haplotypes or paths were given for transcript projection. Use --haplotypes FILE and/or --use-embeded-paths." << endl;
        return 1;
    }

    if (show_progress) { cerr << "[vg rna] Parsing graph file ..." << endl; }

    // Load the graph
    VG* graph = nullptr;
    get_input_file(optind, argc, argv, [&](istream& in) {
        graph = new VG(in, show_progress);
    });

    if (!graph) {
        cerr << "[vg rna] ERROR: Could not load graph." << endl;
        return 1;
    }

    gbwt::GBWT haplotype_index;

    if (!haplotypes_filename.empty()) {

        if (show_progress) { cerr << "[vg rna] Parsing haplotype GBWT index file ..." << endl; }

        sdsl::load_from_file(haplotype_index, haplotypes_filename);
    }

    Transcriptome transcriptome;

    transcriptome.num_threads = num_threads;
    transcriptome.transcript_tag = transcript_tag;
    transcriptome.use_embedded_paths = use_embedded_paths;
    transcriptome.collapse_transcript_paths = collapse_transcript_paths;
    transcriptome.filter_reference_transcript_paths = filter_reference_transcript_paths;

    auto time1 = std::chrono::system_clock::now();

    if (show_progress) { cerr << "[vg rna] Parsing and projecting transcripts ..." << endl; }

    for (auto & filename: transcript_filenames) {

        get_input_file(filename, [&](istream& transcript_stream) {
                   transcriptome.add_transcripts(transcript_stream, *graph, haplotype_index);
        });
    }

    auto time2 = std::chrono::system_clock::now();
    cerr << "Time (s): " << std::chrono::duration_cast<std::chrono::seconds>(time2 - time1).count() << endl;

    if (show_progress) { cerr << "[vg rna] Adding splice-junctions " << (add_transcript_paths ? "and transcript paths " : "") << "to graph ..." << endl; }

    transcriptome.edit_graph(graph, add_transcript_paths);

    auto time3 = std::chrono::system_clock::now();
    cerr << "Time (s): " << std::chrono::duration_cast<std::chrono::seconds>(time3 - time2).count() << endl;

    if (!gbwt_out_filename.empty()) {

        if (show_progress) { cerr << "[vg rna] Writing " << transcriptome.size() << " transcripts as paths to GBWT index file ..." << endl; }

        gbwt::Verbosity::set(gbwt::Verbosity::SILENT);
        gbwt::GBWTBuilder gbwt_builder(gbwt::bit_length(gbwt::Node::encode(graph->max_node_id(), true)));

        transcriptome.construct_gbwt(&gbwt_builder);
        gbwt_builder.finish();

        gbwt_builder.index.addMetadata();
        gbwt_builder.index.metadata.setHaplotypes(transcriptome.size());
        // gbwt_builder.index.metadata.setSamples();
        // gbwt_builder.index.metadata.setContigs();

        sdsl::store_to_file(gbwt_builder.index, gbwt_out_filename);
    }

    auto time4 = std::chrono::system_clock::now();
    cerr << "Time (s): " << std::chrono::duration_cast<std::chrono::seconds>(time4 - time3).count() << endl;

    if (!gam_out_filename.empty()) {

        if (show_progress) { cerr << "[vg rna] Writing " << transcriptome.size() << " transcripts as alignments to GAM file ..." << endl; }

        ofstream gam_ostream;
        gam_ostream.open(gam_out_filename);
        transcriptome.write_gam_alignments(&gam_ostream);
        gam_ostream.close();
    }

    auto time5 = std::chrono::system_clock::now();
    cerr << "Time (s): " << std::chrono::duration_cast<std::chrono::seconds>(time5 - time4).count() << endl;

    if (!fasta_out_filename.empty()) {

        if (show_progress) { cerr << "[vg rna] Writing " << transcriptome.size() << " transcripts as sequences to fasta file ..." << endl; }

        ofstream fasta_ostream;
        fasta_ostream.open(fasta_out_filename);
        transcriptome.write_fasta_sequences(&fasta_ostream, *graph);
        fasta_ostream.close();
    }

    auto time6 = std::chrono::system_clock::now();
    cerr << "Time (s): " << std::chrono::duration_cast<std::chrono::seconds>(time6 - time5).count() << endl;

    if (show_progress) { cerr << "[vg rna] Writing graph to stdout ..." << endl; }

    graph->serialize_to_ostream(std::cout);
    delete graph;

    return 0;
}

// Register subcommand
static Subcommand vg_rna("rna", "project rna transcripts to graph paths and/or GBWT haplotypes", main_rna);

<commit_msg>fixed VPKG compatibility<commit_after>/** \file rna_main.cpp
 *
 * Defines the "vg rna" subcommand, which projects rna transcripts to graph paths and/or GBWT haplotypes.
 */

#include <unistd.h>
#include <getopt.h>
#include <chrono>

#include "subcommand.hpp"

#include "../transcriptome.hpp"
#include "../stream/vpkg.hpp"
#include "../stream/stream.hpp"

using namespace std;
using namespace vg;
using namespace vg::subcommand;

void help_rna(char** argv) {
    cerr << "\nusage: " << argv[0] << " rna [options] <graph.vg> > splice_graph.vg" << endl
         << "options:" << endl
         << "    -n, --transcripts FILE     transcript file(s) in gtf/gff format; may repeat (required)" << endl
         << "    -s, --transcript-tag NAME  use this attribute tag in the gtf/gff file(s) as id [transcript_id]" << endl
         << "    -l, --haplotypes FILE      project transcripts onto haplotypes in GBWT index file" << endl
         << "    -e, --use-embedded-paths   project transcripts onto embedded graph paths" << endl
         << "    -r, --filter-reference     filter reference transcripts" << endl
         << "    -c, --do-not-collapse      do not collapse identical transcripts across haplotypes" << endl
         << "    -a, --add-paths            add transcripts as embedded paths in the graph" << endl
         << "    -b, --write-gbwt FILE      write transcripts as path to GBWT index file" << endl
         << "    -g, --write-gam FILE       write transcripts as alignments to GAM file" << endl
         << "    -f, --write-fasta FILE     write transcripts as sequences to fasta file" << endl
         << "    -t, --threads INT          number of compute threads to use [1]" << endl
         << "    -p, --progress             show progress" << endl
         << "    -h, --help                 print help message" << endl
         << endl;
}

int32_t main_rna(int32_t argc, char** argv) {

    if (argc == 2) {
        help_rna(argv);
        return 1;
    }

    vector<string> transcript_filenames;
    string transcript_tag = "transcript_id";
    string haplotypes_filename;
    bool use_embedded_paths = false;
    bool filter_reference_transcript_paths = false;
    bool collapse_transcript_paths = true;
    bool add_transcript_paths = false;
    string gbwt_out_filename = "";
    string gam_out_filename = "";
    string fasta_out_filename = "";
    int32_t num_threads = 1;
    bool show_progress = false;

    int32_t c;
    optind = 2; // force optind past command positional argument

    while (true) {
        static struct option long_options[] =
            {
                {"transcripts",  no_argument, 0, 'n'},
                {"transcript-tag",  no_argument, 0, 's'},
                {"haplotypes",  no_argument, 0, 'l'},
                {"use-embeded-paths",  no_argument, 0, 'e'},
                {"filter-reference",  no_argument, 0, 'r'},
                {"do-not-collapse",  no_argument, 0, 'c'},
                {"add-paths",  no_argument, 0, 'a'},
                {"write-gbwt",  no_argument, 0, 'b'},
                {"write-gam",  no_argument, 0, 'g'},
                {"write-fasta",  no_argument, 0, 'f'},
                {"threads",  no_argument, 0, 't'},
                {"progress",  no_argument, 0, 'p'},
                {"help", no_argument, 0, 'h'},
                {0, 0, 0, 0}
            };

        int32_t option_index = 0;
        c = getopt_long(argc, argv, "n:s:l:ercab:g:f:t:ph?", long_options, &option_index);

        /* Detect the end of the options. */
        if (c == -1)
            break;

        switch (c)
        {

        case 'n':
            transcript_filenames.push_back(optarg);
            break;

        case 's':
            transcript_tag = optarg;
            break;

        case 'l':
            haplotypes_filename = optarg;
            break;

        case 'e':
            use_embedded_paths = true;
            break;

        case 'r':
            filter_reference_transcript_paths = true;
            break;

        case 'c':
            collapse_transcript_paths = false;
            break;

        case 'a':
            add_transcript_paths = true;
            break;

        case 'b':
            gbwt_out_filename = optarg;
            break;

        case 'g':
            gam_out_filename = optarg;
            break;

        case 'f':
            fasta_out_filename = optarg;
            break;

        case 't':
            num_threads = stoi(optarg);
            break;

        case 'p':
            show_progress = true;
            break;

        case 'h':
        case '?':
            /* getopt_long already printed an error message. */
            help_rna(argv);
            exit(1);
            break;

        default:
            abort();
        }
    }

    if (argc < optind + 1) {
        help_rna(argv);
        return 1;
    }

    if (transcript_filenames.empty()) {

        cerr << "[vg rna] ERROR: No transcripts were given. Use --transcripts FILE." << endl;
        return 1;
    }

    if (haplotypes_filename.empty() && !use_embedded_paths) {

        cerr << "[vg rna] ERROR: No haplotypes or paths were given for transcript projection. Use --haplotypes FILE and/or --use-embeded-paths." << endl;
        return 1;
    }

    if (show_progress) { cerr << "[vg rna] Parsing graph file ..." << endl; }

    // Load the graph
    VG* graph = nullptr;
    get_input_file(optind, argc, argv, [&](istream& in) {
        graph = new VG(in, show_progress);
    });

    if (!graph) {
        cerr << "[vg rna] ERROR: Could not load graph." << endl;
        return 1;
    }

    unique_ptr<gbwt::GBWT> haplotype_index;

    if (!haplotypes_filename.empty()) {

        if (show_progress) { cerr << "[vg rna] Parsing haplotype GBWT index file ..." << endl; }
        haplotype_index = stream::VPKG::load_one<gbwt::GBWT>(haplotypes_filename);

    } else {

        haplotype_index = unique_ptr<gbwt::GBWT>(new gbwt::GBWT());
    }

    Transcriptome transcriptome;

    transcriptome.num_threads = num_threads;
    transcriptome.transcript_tag = transcript_tag;
    transcriptome.use_embedded_paths = use_embedded_paths;
    transcriptome.collapse_transcript_paths = collapse_transcript_paths;
    transcriptome.filter_reference_transcript_paths = filter_reference_transcript_paths;

    auto time1 = std::chrono::system_clock::now();

    if (show_progress) { cerr << "[vg rna] Parsing and projecting transcripts ..." << endl; }

    for (auto & filename: transcript_filenames) {

        get_input_file(filename, [&](istream& transcript_stream) {
                   transcriptome.add_transcripts(transcript_stream, *graph, *haplotype_index);
        });
    }

    haplotype_index.reset();

    auto time2 = std::chrono::system_clock::now();
    cerr << "Time (s): " << std::chrono::duration_cast<std::chrono::seconds>(time2 - time1).count() << endl;

    if (show_progress) { cerr << "[vg rna] Adding splice-junctions " << (add_transcript_paths ? "and transcript paths " : "") << "to graph ..." << endl; }

    transcriptome.edit_graph(graph, add_transcript_paths);

    auto time3 = std::chrono::system_clock::now();
    cerr << "Time (s): " << std::chrono::duration_cast<std::chrono::seconds>(time3 - time2).count() << endl;

    if (!gbwt_out_filename.empty()) {

        if (show_progress) { cerr << "[vg rna] Writing " << transcriptome.size() << " transcripts as paths to GBWT index file ..." << endl; }

        gbwt::Verbosity::set(gbwt::Verbosity::SILENT);
        gbwt::GBWTBuilder gbwt_builder(gbwt::bit_length(gbwt::Node::encode(graph->max_node_id(), true)));

        transcriptome.construct_gbwt(&gbwt_builder);
        gbwt_builder.finish();

        gbwt_builder.index.addMetadata();
        gbwt_builder.index.metadata.setHaplotypes(transcriptome.size());
        // gbwt_builder.index.metadata.setSamples();
        // gbwt_builder.index.metadata.setContigs();

        stream::VPKG::save(gbwt_builder.index, gbwt_out_filename);
    }

    auto time4 = std::chrono::system_clock::now();
    cerr << "Time (s): " << std::chrono::duration_cast<std::chrono::seconds>(time4 - time3).count() << endl;

    if (!gam_out_filename.empty()) {

        if (show_progress) { cerr << "[vg rna] Writing " << transcriptome.size() << " transcripts as alignments to GAM file ..." << endl; }

        ofstream gam_ostream;
        gam_ostream.open(gam_out_filename);
        transcriptome.write_gam_alignments(&gam_ostream);
        gam_ostream.close();
    }

    auto time5 = std::chrono::system_clock::now();
    cerr << "Time (s): " << std::chrono::duration_cast<std::chrono::seconds>(time5 - time4).count() << endl;

    if (!fasta_out_filename.empty()) {

        if (show_progress) { cerr << "[vg rna] Writing " << transcriptome.size() << " transcripts as sequences to fasta file ..." << endl; }

        ofstream fasta_ostream;
        fasta_ostream.open(fasta_out_filename);
        transcriptome.write_fasta_sequences(&fasta_ostream, *graph);
        fasta_ostream.close();
    }

    auto time6 = std::chrono::system_clock::now();
    cerr << "Time (s): " << std::chrono::duration_cast<std::chrono::seconds>(time6 - time5).count() << endl;

    if (show_progress) { cerr << "[vg rna] Writing graph to stdout ..." << endl; }

    graph->serialize_to_ostream(std::cout);
    delete graph;

    return 0;
}

// Register subcommand
static Subcommand vg_rna("rna", "project rna transcripts to graph paths and/or GBWT haplotypes", main_rna);

<|endoftext|>