<commit_before>/* Copyright 2017 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/instruction_fusion.h"

#include "tensorflow/compiler/xla/service/hlo_matchers.h"
#include "tensorflow/compiler/xla/tests/hlo_test_base.h"
#include "tensorflow/compiler/xla/tools/parser/hlo_parser.h"

namespace xla {

namespace op = xla::testing::opcode_matchers;

using InstructionFusionTest = HloTestBase;

TEST_F(InstructionFusionTest, PotentialBitcastReshapeOfParameterUnfused) {
  HloComputation::Builder builder(TestName());
  auto param0 = builder.AddInstruction(
      HloInstruction::CreateParameter(0, ShapeUtil::MakeShape(S32, {}), "0"));
  auto reshape1 = builder.AddInstruction(
      HloInstruction::CreateReshape(ShapeUtil::MakeShape(S32, {1, 1}), param0));

  auto module = CreateNewModule();
  auto computation = module->AddEntryComputation(builder.Build());
  EXPECT_EQ(reshape1, computation->root_instruction());
  EXPECT_FALSE(
      InstructionFusion(InstructionFusion::IsExpensive, /*may_duplicate=*/true)
          .Run(module.get())
          .ValueOrDie());
}

TEST_F(InstructionFusionTest, PotentialBitcastSimpleReshapeOfParameterUnfused) {
  HloComputation::Builder builder(TestName());
  auto param0 = builder.AddInstruction(
      HloInstruction::CreateParameter(0, ShapeUtil::MakeShape(S32, {}), "0"));
  auto reshape1 = builder.AddInstruction(
      HloInstruction::CreateReshape(ShapeUtil::MakeShape(S32, {1, 1}), param0));

  auto module = CreateNewModule();
  auto computation = module->AddEntryComputation(builder.Build());
  EXPECT_EQ(reshape1, computation->root_instruction());
  EXPECT_FALSE(
      InstructionFusion(InstructionFusion::IsExpensive, /*may_duplicate=*/true)
          .Run(module.get())
          .ValueOrDie());
}

TEST_F(InstructionFusionTest, PotentialBitcastTransposeOfParameterUnfused) {
  HloComputation::Builder builder(TestName());
  auto param0 = builder.AddInstruction(
      HloInstruction::CreateParameter(0, ShapeUtil::MakeShape(S32, {}), "0"));
  auto transpose1 = builder.AddInstruction(HloInstruction::CreateTranspose(
      ShapeUtil::MakeShape(S32, {}), param0, {}));

  auto module = CreateNewModule();
  auto computation = module->AddEntryComputation(builder.Build());
  EXPECT_EQ(transpose1, computation->root_instruction());
  EXPECT_FALSE(
      InstructionFusion(InstructionFusion::IsExpensive, /*may_duplicate=*/true)
          .Run(module.get())
          .ValueOrDie());
}

TEST_F(InstructionFusionTest, AvoidDuplicationIfNotAllFusable) {
  HloComputation::Builder builder(TestName());
  auto shape = ShapeUtil::MakeShape(F32, {16, 16});
  auto param0 =
      builder.AddInstruction(HloInstruction::CreateParameter(0, shape, "0"));
  auto param1 =
      builder.AddInstruction(HloInstruction::CreateParameter(1, shape, "1"));
  HloInstruction* binary1 = builder.AddInstruction(
      HloInstruction::CreateBinary(shape, HloOpcode::kAdd, param0, param1));
  builder.AddInstruction(HloInstruction::CreateSend(binary1, 0));
  HloInstruction* unary = builder.AddInstruction(
      HloInstruction::CreateUnary(shape, HloOpcode::kAbs, binary1));

  auto module = CreateNewModule();
  auto computation = module->AddEntryComputation(builder.Build());
  EXPECT_EQ(unary, computation->root_instruction());
  EXPECT_FALSE(
      InstructionFusion(InstructionFusion::IsExpensive, /*may_duplicate=*/true)
          .Run(module.get())
          .ValueOrDie());
}

// Counts the number of HLO ops with a given op code in the specified module.
static int Count(const HloModule& module, HloOpcode op) {
  int count = 0;
  for (const auto* computation : module.computations()) {
    for (const auto* instruction : computation->instructions()) {
      if (instruction->opcode() == op) {
        ++count;
      }
    }
  }
  return count;
}

TEST_F(InstructionFusionTest, FuseCheapNonDuplicatableOps) {
  auto module = tools::Parse(R"(
  HloModule test_module
  ENTRY OutputFusion {
    p0 = f32[4,3]{1,0} parameter(0)
    add = f32[4,3]{1,0} add(p0, p0)
    ROOT root = f32[4,3]{1,0} subtract(add, add)
  })")
                    .ValueOrDie();
  // Expect the add and subtraction to be fused.
  EXPECT_TRUE(
      InstructionFusion(InstructionFusion::IsExpensive, /*may_duplicate=*/true)
          .Run(module.get())
          .ValueOrDie())
      << module->ToString();
  EXPECT_EQ(Count(*module, HloOpcode::kFusion), 1) << module->ToString();

  // Make sure the add hasn't been duplicated.
  EXPECT_EQ(Count(*module, HloOpcode::kFusion), 1) << module->ToString();
}

TEST_F(InstructionFusionTest, AvoidDuplicationIfNotAllFusableRecursively) {
  // Make sure we do not duplicate the add, as we cannot fuse through the rng.
  //
  // p0 -> add -------------------------> sub
  //           \-> abs1 -> rng -> abs2 -/
  auto module = tools::Parse(R"(
  HloModule test_module
  ENTRY OutputFusion {
    p0 = f32[4,3]{1,0} parameter(0)
    add = f32[4,3]{1,0} add(p0, p0)
    abs1 = f32[4,3]{1,0} abs(add)
    rng = f32[4,3]{1,0} rng(abs1), distribution=rng_uniform
    abs2 = f32[4,3]{1,0} abs(rng)
    ROOT root = f32[4,3]{1,0} subtract(abs2, add)
  })")
                    .ValueOrDie();
  // We expect abs2 to be fused into root.
  EXPECT_TRUE(
      InstructionFusion(InstructionFusion::IsExpensive, /*may_duplicate=*/true)
          .Run(module.get())
          .ValueOrDie())
      << module->ToString();
  EXPECT_EQ(Count(*module, HloOpcode::kFusion), 1) << module->ToString();

  // Make sure the add hasn't been duplicated.
  EXPECT_EQ(Count(*module, HloOpcode::kAdd), 1) << module->ToString();

  // Use a log node with a second consumer to break the fusion.
  //
  // p0 -> add -------------------------> sub
  //           \-> abs1 -> log -> abs2 -/
  //                           \-> send
  module = tools::Parse(R"(
  HloModule test_module
  ENTRY OutputFusion {
    p0 = f32[4,3]{1,0} parameter(0)
    add = f32[4,3]{1,0} add(p0, p0)
    abs1 = f32[4,3]{1,0} abs(add)
    log = f32[4,3]{1,0} log(abs1)
    send = f32[4,3]{1,0} send(log), channel_id=0
    abs2 = f32[4,3]{1,0} abs(log)
    ROOT root = f32[4,3]{1,0} subtract(abs2, add)
  })")
               .ValueOrDie();

  // We expect abs2 to be fused into root and abs1 to be fused into log.
  EXPECT_TRUE(
      InstructionFusion(InstructionFusion::IsExpensive, /*may_duplicate=*/true)
          .Run(module.get())
          .ValueOrDie())
      << module->ToString();
  EXPECT_EQ(Count(*module, HloOpcode::kFusion), 2) << module->ToString();

  // Make sure the add hasn't been duplicated.
  EXPECT_EQ(Count(*module, HloOpcode::kAdd), 1) << module->ToString();

  // Make sure we still fuse ops where one operand in the chain to the producer
  // can't be fused.
  //
  // p0 ---> add1 -----------> sub
  //    \         \-> add2 -/
  //     \-> log -/
  //             \-> send
  module = tools::Parse(R"(
  HloModule test_module
  ENTRY OutputFusion {
    p0 = f32[4,3]{1,0} parameter(0)
    add1 = f32[4,3]{1,0} add(p0, p0)
    log = f32[4,3]{1,0} log(p0)
    send = f32[4,3]{1,0} send(log), channel_id=0
    add2 = f32[4,3]{1,0} add(log, add1)
    ROOT root = f32[4,3]{1,0} subtract(add1, add2)
  })")
               .ValueOrDie();

  // Expect the add1 and add2 to be fused into root.
  EXPECT_TRUE(
      InstructionFusion(InstructionFusion::IsExpensive, /*may_duplicate=*/true)
          .Run(module.get())
          .ValueOrDie())
      << module->ToString();
  EXPECT_EQ(Count(*module, HloOpcode::kFusion), 1) << module->ToString();

  // Make sure we didn't duplicate any adds.
  EXPECT_EQ(Count(*module, HloOpcode::kAdd), 2) << module->ToString();

  // A variant of the above that allows the algorithm to put add2 into the set
  // of unfusable ops to short-circuit the decision whether add1 should be fused
  // into sub2.
  //
  //             /---------------\
  // p0 ---> add1 ---> add2 ------> sub2
  //                       \------> sub1
  //                        log -/
  //                            \-> send
  module = tools::Parse(R"(
  HloModule test_module
  ENTRY OutputFusion {
    p0 = f32[4,3]{1,0} parameter(0)
    add1 = f32[4,3]{1,0} add(p0, p0)
    add2 = f32[4,3]{1,0} add(add1, add1)
    log = f32[4,3]{1,0} log(add2)
    send = f32[4,3]{1,0} send(log), channel_id=0
    sub1 = f32[4,3]{1,0} subtract(log, add2)
    sub2 = f32[4,3]{1,0} subtract(add2, add1)
    ROOT root = (f32[4,3]{1,0}, f32[4,3]{1,0}) tuple(sub1, sub2)
  })")
               .ValueOrDie();

  // Expect sub1 and sub2 to be fused into root.
  EXPECT_TRUE(
      InstructionFusion(InstructionFusion::IsExpensive, /*may_duplicate=*/true)
          .Run(module.get())
          .ValueOrDie())
      << module->ToString();
  EXPECT_EQ(Count(*module, HloOpcode::kFusion), 1) << module->ToString();

  // Make sure we didn't duplicate any adds.
  EXPECT_EQ(Count(*module, HloOpcode::kAdd), 2) << module->ToString();
}

TEST_F(InstructionFusionTest, AllowUnaryDuplication) {
  HloComputation::Builder builder(TestName());
  auto shape = ShapeUtil::MakeShape(F32, {16, 16});
  auto param0 =
      builder.AddInstruction(HloInstruction::CreateParameter(0, shape, "0"));
  HloInstruction* unary1 = builder.AddInstruction(
      HloInstruction::CreateUnary(shape, HloOpcode::kFloor, param0));
  builder.AddInstruction(HloInstruction::CreateSend(unary1, 0));
  HloInstruction* unary2 = builder.AddInstruction(
      HloInstruction::CreateUnary(shape, HloOpcode::kAbs, unary1));

  auto module = CreateNewModule();
  auto computation = module->AddEntryComputation(builder.Build());
  EXPECT_EQ(unary2, computation->root_instruction());
  EXPECT_TRUE(
      InstructionFusion(InstructionFusion::IsExpensive, /*may_duplicate=*/true)
          .Run(module.get())
          .ValueOrDie());
}

TEST_F(InstructionFusionTest, AllowEffectiveUnaryDuplication) {
  auto shape = ShapeUtil::MakeShape(F32, {16, 16});
  auto small_shape = ShapeUtil::MakeShape(F32, {16});
  HloComputation::Builder builder(TestName());
  auto param0 = builder.AddInstruction(
      HloInstruction::CreateParameter(0, small_shape, "0"));
  auto param1 =
      builder.AddInstruction(HloInstruction::CreateParameter(1, shape, "1"));
  HloInstruction* binary1 = builder.AddInstruction(
      HloInstruction::CreateBinary(shape, HloOpcode::kAdd, param0, param1));
  builder.AddInstruction(HloInstruction::CreateSend(binary1, 0));
  HloInstruction* unary = builder.AddInstruction(
      HloInstruction::CreateUnary(shape, HloOpcode::kAbs, binary1));

  auto module = CreateNewModule();
  auto computation = module->AddEntryComputation(builder.Build());
  EXPECT_EQ(unary, computation->root_instruction());
  EXPECT_TRUE(
      InstructionFusion(InstructionFusion::IsExpensive, /*may_duplicate=*/true)
          .Run(module.get())
          .ValueOrDie());
}

TEST_F(InstructionFusionTest,
       WideningConvertsAreAlwaysDuplicableIntoConsumers) {
  auto module = tools::Parse(R"(
  HloModule test_module
  ENTRY Test {
    p0 = f16[100] parameter(0)
    c = f32[100] convert(p0)
    add = f32[100] add(c, c)
    ROOT mul = f32[100] multiply(c, c)
  })")
                    .ValueOrDie();

  // The convert should be fused into the add and mul, even though may_duplicate
  // is false, because it's always beneficial to fuse/duplicate widening
  // converts into consumers.
  EXPECT_TRUE(
      InstructionFusion(InstructionFusion::IsExpensive, /*may_duplicate=*/false)
          .Run(module.get())
          .ValueOrDie())
      << module->ToString();

  HloInstruction* root = module->entry_computation()->root_instruction();
  EXPECT_THAT(root, op::Fusion(op::Parameter()));
}

}  // namespace xla
<commit_msg>Fix a test expectation.<commit_after>/* Copyright 2017 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/instruction_fusion.h"

#include "tensorflow/compiler/xla/service/hlo_matchers.h"
#include "tensorflow/compiler/xla/tests/hlo_test_base.h"
#include "tensorflow/compiler/xla/tools/parser/hlo_parser.h"

namespace xla {

namespace op = xla::testing::opcode_matchers;

using InstructionFusionTest = HloTestBase;

TEST_F(InstructionFusionTest, PotentialBitcastReshapeOfParameterUnfused) {
  HloComputation::Builder builder(TestName());
  auto param0 = builder.AddInstruction(
      HloInstruction::CreateParameter(0, ShapeUtil::MakeShape(S32, {}), "0"));
  auto reshape1 = builder.AddInstruction(
      HloInstruction::CreateReshape(ShapeUtil::MakeShape(S32, {1, 1}), param0));

  auto module = CreateNewModule();
  auto computation = module->AddEntryComputation(builder.Build());
  EXPECT_EQ(reshape1, computation->root_instruction());
  EXPECT_FALSE(
      InstructionFusion(InstructionFusion::IsExpensive, /*may_duplicate=*/true)
          .Run(module.get())
          .ValueOrDie());
}

TEST_F(InstructionFusionTest, PotentialBitcastSimpleReshapeOfParameterUnfused) {
  HloComputation::Builder builder(TestName());
  auto param0 = builder.AddInstruction(
      HloInstruction::CreateParameter(0, ShapeUtil::MakeShape(S32, {}), "0"));
  auto reshape1 = builder.AddInstruction(
      HloInstruction::CreateReshape(ShapeUtil::MakeShape(S32, {1, 1}), param0));

  auto module = CreateNewModule();
  auto computation = module->AddEntryComputation(builder.Build());
  EXPECT_EQ(reshape1, computation->root_instruction());
  EXPECT_FALSE(
      InstructionFusion(InstructionFusion::IsExpensive, /*may_duplicate=*/true)
          .Run(module.get())
          .ValueOrDie());
}

TEST_F(InstructionFusionTest, PotentialBitcastTransposeOfParameterUnfused) {
  HloComputation::Builder builder(TestName());
  auto param0 = builder.AddInstruction(
      HloInstruction::CreateParameter(0, ShapeUtil::MakeShape(S32, {}), "0"));
  auto transpose1 = builder.AddInstruction(HloInstruction::CreateTranspose(
      ShapeUtil::MakeShape(S32, {}), param0, {}));

  auto module = CreateNewModule();
  auto computation = module->AddEntryComputation(builder.Build());
  EXPECT_EQ(transpose1, computation->root_instruction());
  EXPECT_FALSE(
      InstructionFusion(InstructionFusion::IsExpensive, /*may_duplicate=*/true)
          .Run(module.get())
          .ValueOrDie());
}

TEST_F(InstructionFusionTest, AvoidDuplicationIfNotAllFusable) {
  HloComputation::Builder builder(TestName());
  auto shape = ShapeUtil::MakeShape(F32, {16, 16});
  auto param0 =
      builder.AddInstruction(HloInstruction::CreateParameter(0, shape, "0"));
  auto param1 =
      builder.AddInstruction(HloInstruction::CreateParameter(1, shape, "1"));
  HloInstruction* binary1 = builder.AddInstruction(
      HloInstruction::CreateBinary(shape, HloOpcode::kAdd, param0, param1));
  builder.AddInstruction(HloInstruction::CreateSend(binary1, 0));
  HloInstruction* unary = builder.AddInstruction(
      HloInstruction::CreateUnary(shape, HloOpcode::kAbs, binary1));

  auto module = CreateNewModule();
  auto computation = module->AddEntryComputation(builder.Build());
  EXPECT_EQ(unary, computation->root_instruction());
  EXPECT_FALSE(
      InstructionFusion(InstructionFusion::IsExpensive, /*may_duplicate=*/true)
          .Run(module.get())
          .ValueOrDie());
}

// Counts the number of HLO ops with a given op code in the specified module.
static int Count(const HloModule& module, HloOpcode op) {
  int count = 0;
  for (const auto* computation : module.computations()) {
    for (const auto* instruction : computation->instructions()) {
      if (instruction->opcode() == op) {
        ++count;
      }
    }
  }
  return count;
}

TEST_F(InstructionFusionTest, FuseCheapNonDuplicatableOps) {
  auto module = tools::Parse(R"(
  HloModule test_module
  ENTRY OutputFusion {
    p0 = f32[4,3]{1,0} parameter(0)
    add = f32[4,3]{1,0} add(p0, p0)
    ROOT root = f32[4,3]{1,0} subtract(add, add)
  })")
                    .ValueOrDie();
  // Expect the add and subtraction to be fused.
  EXPECT_TRUE(
      InstructionFusion(InstructionFusion::IsExpensive, /*may_duplicate=*/true)
          .Run(module.get())
          .ValueOrDie())
      << module->ToString();
  EXPECT_EQ(Count(*module, HloOpcode::kFusion), 1) << module->ToString();

  // Make sure the add hasn't been duplicated.
  EXPECT_EQ(Count(*module, HloOpcode::kAdd), 1) << module->ToString();
}

TEST_F(InstructionFusionTest, AvoidDuplicationIfNotAllFusableRecursively) {
  // Make sure we do not duplicate the add, as we cannot fuse through the rng.
  //
  // p0 -> add -------------------------> sub
  //           \-> abs1 -> rng -> abs2 -/
  auto module = tools::Parse(R"(
  HloModule test_module
  ENTRY OutputFusion {
    p0 = f32[4,3]{1,0} parameter(0)
    add = f32[4,3]{1,0} add(p0, p0)
    abs1 = f32[4,3]{1,0} abs(add)
    rng = f32[4,3]{1,0} rng(abs1), distribution=rng_uniform
    abs2 = f32[4,3]{1,0} abs(rng)
    ROOT root = f32[4,3]{1,0} subtract(abs2, add)
  })")
                    .ValueOrDie();
  // We expect abs2 to be fused into root.
  EXPECT_TRUE(
      InstructionFusion(InstructionFusion::IsExpensive, /*may_duplicate=*/true)
          .Run(module.get())
          .ValueOrDie())
      << module->ToString();
  EXPECT_EQ(Count(*module, HloOpcode::kFusion), 1) << module->ToString();

  // Make sure the add hasn't been duplicated.
  EXPECT_EQ(Count(*module, HloOpcode::kAdd), 1) << module->ToString();

  // Use a log node with a second consumer to break the fusion.
  //
  // p0 -> add -------------------------> sub
  //           \-> abs1 -> log -> abs2 -/
  //                           \-> send
  module = tools::Parse(R"(
  HloModule test_module
  ENTRY OutputFusion {
    p0 = f32[4,3]{1,0} parameter(0)
    add = f32[4,3]{1,0} add(p0, p0)
    abs1 = f32[4,3]{1,0} abs(add)
    log = f32[4,3]{1,0} log(abs1)
    send = f32[4,3]{1,0} send(log), channel_id=0
    abs2 = f32[4,3]{1,0} abs(log)
    ROOT root = f32[4,3]{1,0} subtract(abs2, add)
  })")
               .ValueOrDie();

  // We expect abs2 to be fused into root and abs1 to be fused into log.
  EXPECT_TRUE(
      InstructionFusion(InstructionFusion::IsExpensive, /*may_duplicate=*/true)
          .Run(module.get())
          .ValueOrDie())
      << module->ToString();
  EXPECT_EQ(Count(*module, HloOpcode::kFusion), 2) << module->ToString();

  // Make sure the add hasn't been duplicated.
  EXPECT_EQ(Count(*module, HloOpcode::kAdd), 1) << module->ToString();

  // Make sure we still fuse ops where one operand in the chain to the producer
  // can't be fused.
  //
  // p0 ---> add1 -----------> sub
  //    \         \-> add2 -/
  //     \-> log -/
  //             \-> send
  module = tools::Parse(R"(
  HloModule test_module
  ENTRY OutputFusion {
    p0 = f32[4,3]{1,0} parameter(0)
    add1 = f32[4,3]{1,0} add(p0, p0)
    log = f32[4,3]{1,0} log(p0)
    send = f32[4,3]{1,0} send(log), channel_id=0
    add2 = f32[4,3]{1,0} add(log, add1)
    ROOT root = f32[4,3]{1,0} subtract(add1, add2)
  })")
               .ValueOrDie();

  // Expect the add1 and add2 to be fused into root.
  EXPECT_TRUE(
      InstructionFusion(InstructionFusion::IsExpensive, /*may_duplicate=*/true)
          .Run(module.get())
          .ValueOrDie())
      << module->ToString();
  EXPECT_EQ(Count(*module, HloOpcode::kFusion), 1) << module->ToString();

  // Make sure we didn't duplicate any adds.
  EXPECT_EQ(Count(*module, HloOpcode::kAdd), 2) << module->ToString();

  // A variant of the above that allows the algorithm to put add2 into the set
  // of unfusable ops to short-circuit the decision whether add1 should be fused
  // into sub2.
  //
  //             /---------------\
  // p0 ---> add1 ---> add2 ------> sub2
  //                       \------> sub1
  //                        log -/
  //                            \-> send
  module = tools::Parse(R"(
  HloModule test_module
  ENTRY OutputFusion {
    p0 = f32[4,3]{1,0} parameter(0)
    add1 = f32[4,3]{1,0} add(p0, p0)
    add2 = f32[4,3]{1,0} add(add1, add1)
    log = f32[4,3]{1,0} log(add2)
    send = f32[4,3]{1,0} send(log), channel_id=0
    sub1 = f32[4,3]{1,0} subtract(log, add2)
    sub2 = f32[4,3]{1,0} subtract(add2, add1)
    ROOT root = (f32[4,3]{1,0}, f32[4,3]{1,0}) tuple(sub1, sub2)
  })")
               .ValueOrDie();

  // Expect sub1 and sub2 to be fused into root.
  EXPECT_TRUE(
      InstructionFusion(InstructionFusion::IsExpensive, /*may_duplicate=*/true)
          .Run(module.get())
          .ValueOrDie())
      << module->ToString();
  EXPECT_EQ(Count(*module, HloOpcode::kFusion), 1) << module->ToString();

  // Make sure we didn't duplicate any adds.
  EXPECT_EQ(Count(*module, HloOpcode::kAdd), 2) << module->ToString();
}

TEST_F(InstructionFusionTest, AllowUnaryDuplication) {
  HloComputation::Builder builder(TestName());
  auto shape = ShapeUtil::MakeShape(F32, {16, 16});
  auto param0 =
      builder.AddInstruction(HloInstruction::CreateParameter(0, shape, "0"));
  HloInstruction* unary1 = builder.AddInstruction(
      HloInstruction::CreateUnary(shape, HloOpcode::kFloor, param0));
  builder.AddInstruction(HloInstruction::CreateSend(unary1, 0));
  HloInstruction* unary2 = builder.AddInstruction(
      HloInstruction::CreateUnary(shape, HloOpcode::kAbs, unary1));

  auto module = CreateNewModule();
  auto computation = module->AddEntryComputation(builder.Build());
  EXPECT_EQ(unary2, computation->root_instruction());
  EXPECT_TRUE(
      InstructionFusion(InstructionFusion::IsExpensive, /*may_duplicate=*/true)
          .Run(module.get())
          .ValueOrDie());
}

TEST_F(InstructionFusionTest, AllowEffectiveUnaryDuplication) {
  auto shape = ShapeUtil::MakeShape(F32, {16, 16});
  auto small_shape = ShapeUtil::MakeShape(F32, {16});
  HloComputation::Builder builder(TestName());
  auto param0 = builder.AddInstruction(
      HloInstruction::CreateParameter(0, small_shape, "0"));
  auto param1 =
      builder.AddInstruction(HloInstruction::CreateParameter(1, shape, "1"));
  HloInstruction* binary1 = builder.AddInstruction(
      HloInstruction::CreateBinary(shape, HloOpcode::kAdd, param0, param1));
  builder.AddInstruction(HloInstruction::CreateSend(binary1, 0));
  HloInstruction* unary = builder.AddInstruction(
      HloInstruction::CreateUnary(shape, HloOpcode::kAbs, binary1));

  auto module = CreateNewModule();
  auto computation = module->AddEntryComputation(builder.Build());
  EXPECT_EQ(unary, computation->root_instruction());
  EXPECT_TRUE(
      InstructionFusion(InstructionFusion::IsExpensive, /*may_duplicate=*/true)
          .Run(module.get())
          .ValueOrDie());
}

TEST_F(InstructionFusionTest,
       WideningConvertsAreAlwaysDuplicableIntoConsumers) {
  auto module = tools::Parse(R"(
  HloModule test_module
  ENTRY Test {
    p0 = f16[100] parameter(0)
    c = f32[100] convert(p0)
    add = f32[100] add(c, c)
    ROOT mul = f32[100] multiply(c, c)
  })")
                    .ValueOrDie();

  // The convert should be fused into the add and mul, even though may_duplicate
  // is false, because it's always beneficial to fuse/duplicate widening
  // converts into consumers.
  EXPECT_TRUE(
      InstructionFusion(InstructionFusion::IsExpensive, /*may_duplicate=*/false)
          .Run(module.get())
          .ValueOrDie())
      << module->ToString();

  HloInstruction* root = module->entry_computation()->root_instruction();
  EXPECT_THAT(root, op::Fusion(op::Parameter()));
}

}  // namespace xla
<|endoftext|>


<commit_before>/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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 <deque>
#include <string>

#include <process/defer.hpp>
#include <process/dispatch.hpp>
#include <process/future.hpp>
#include <process/help.hpp>
#include <process/http.hpp>
#include <process/id.hpp>
#include <process/owned.hpp>
#include <process/process.hpp>

#include <process/metrics/gauge.hpp>
#include <process/metrics/metrics.hpp>
#include <process/metrics/timer.hpp>

#include <stout/lambda.hpp>
#include <stout/none.hpp>
#include <stout/nothing.hpp>
#include <stout/option.hpp>
#include <stout/protobuf.hpp>

#include "common/type_utils.hpp"

#include "master/registrar.hpp"
#include "master/registry.hpp"

#include "state/protobuf.hpp"

using mesos::internal::state::protobuf::State;
using mesos::internal::state::protobuf::Variable;

using process::dispatch;
using process::spawn;
using process::terminate;
using process::wait; // Necessary on some OS's to disambiguate.

using process::DESCRIPTION;
using process::Failure;
using process::Future;
using process::HELP;
using process::Owned;
using process::Process;
using process::Promise;
using process::TLDR;
using process::USAGE;

using process::http::OK;

using process::metrics::Gauge;
using process::metrics::Timer;

using std::deque;
using std::string;

namespace mesos {
namespace internal {
namespace master {

using process::http::Response;
using process::http::Request;

class RegistrarProcess : public Process<RegistrarProcess>
{
public:
  RegistrarProcess(const Flags& _flags, State* _state)
    : ProcessBase(process::ID::generate("registrar")),
      metrics(*this),
      updating(false),
      flags(_flags),
      state(_state) {}

  virtual ~RegistrarProcess() {}

  // Registrar implementation.
  Future<Registry> recover(const MasterInfo& info);
  Future<bool> apply(Owned<Operation> operation);

protected:
  virtual void initialize()
  {
    route("/registry", registryHelp(), &RegistrarProcess::registry);
  }

private:
  // HTTP handlers.
  // /registrar(N)/registry
  Future<Response> registry(const Request& request);
  static string registryHelp();

  // The 'Recover' operation adds the latest MasterInfo.
  class Recover : public Operation
  {
  public:
    explicit Recover(const MasterInfo& _info) : info(_info) {}

  protected:
    virtual Try<bool> perform(
        Registry* registry,
        hashset<SlaveID>* slaveIDs,
        bool strict)
    {
      registry->mutable_master()->mutable_info()->CopyFrom(info);
      return true; // Mutation.
    }

  private:
    const MasterInfo info;
  };

  // Metrics.
  struct Metrics
  {
    explicit Metrics(const RegistrarProcess& process)
      : queued_operations(
          "registrar/queued_operations",
          defer(process, &RegistrarProcess::_queued_operations)),
        state_fetch("registrar/state_fetch"),
        state_store("registrar/state_store", Days(1))
    {
      process::metrics::add(queued_operations);

      process::metrics::add(state_fetch);
      process::metrics::add(state_store);
    }

    ~Metrics()
    {
      process::metrics::remove(queued_operations);

      process::metrics::remove(state_fetch);
      process::metrics::remove(state_store);
    }

    Gauge queued_operations;

    Timer state_fetch;
    Timer state_store;
  } metrics;

  // Gauge handlers
  double _queued_operations()
  {
    return operations.size();
  }

  // Continuations.
  void _recover(
      const MasterInfo& info,
      const Future<Variable<Registry> >& recovery);
  void __recover(const Future<bool>& recover);
  Future<bool> _apply(Owned<Operation> operation);

  // Helper for updating state (performing store).
  void update();
  void _update(
      const Future<Option<Variable<Registry> > >& store,
      deque<Owned<Operation> > operations);

  Option<Variable<Registry> > variable;
  deque<Owned<Operation> > operations;
  bool updating; // Used to signify fetching (recovering) or storing.

  const Flags flags;
  State* state;

  // Used to compose our operations with recovery.
  Option<Owned<Promise<Registry> > > recovered;
};

// Helper for treating State operations that timeout as failures.
template <typename T>
Future<T> timeout(
    const string& operation,
    const Duration& duration,
    Future<T> future)
{
  future.discard();

  return Failure(
      "Failed to perform " + operation + " within " + stringify(duration));
}

Future<Response> RegistrarProcess::registry(const Request& request)
{
  JSON::Object result;

  if (variable.isSome()) {
    result = JSON::Protobuf(variable.get().get());
  }

  return OK(result, request.query.get("jsonp"));
}

string RegistrarProcess::registryHelp()
{
  return HELP(
      TLDR(
          "Returns the current contents of the Registry in JSON."),
      USAGE(
          "/registrar(1)/registry"),
      DESCRIPTION(
          "Example:"
          "",
          "```",
          "{",
          "  \"master\":",
          "  {",
          "    \"info\":",
          "    {",
          "      \"hostname\": \"localhost\",",
          "      \"id\": \"20140325-235542-1740121354-5050-33357\",",
          "      \"ip\": 2130706433,",
          "      \"pid\": \"master@127.0.0.1:5050\",",
          "      \"port\": 5050",
          "    }",
          "  },",
          "",
          "  \"slaves\":",
          "  {",
          "    \"slaves\":",
          "    [",
          "      {",
          "        \"info\":",
          "        {",
          "          \"checkpoint\": true,",
          "          \"hostname\": \"localhost\",",
          "          \"id\":",
          "          { ",
          "            \"value\": \"20140325-234618-1740121354-5050-29065-0\"",
          "          },",
          "          \"port\": 5051,",
          "          \"resources\":",
          "          [",
          "            {",
          "              \"name\": \"cpus\",",
          "              \"role\": \"*\",",
          "              \"scalar\": { \"value\": 24 },",
          "              \"type\": \"SCALAR\"",
          "            }",
          "          ],",
          "          \"webui_hostname\": \"localhost\"",
          "        }",
          "      }",
          "    ]",
          "  }",
          "}",
          "```"));
}

Future<Registry> RegistrarProcess::recover(const MasterInfo& info)
{
  LOG(INFO) << "Recovering registrar";

  if (recovered.isNone()) {
    metrics.state_fetch.time(state->fetch<Registry>("registry"))
      .after(flags.registry_fetch_timeout,
             lambda::bind(
                 &timeout<Variable<Registry> >,
                 "fetch",
                 flags.registry_fetch_timeout,
                 lambda::_1))
      .onAny(defer(self(), &Self::_recover, info, lambda::_1));
    updating = true;
    recovered = Owned<Promise<Registry> >(new Promise<Registry>());
  }

  return recovered.get()->future();
}

void RegistrarProcess::_recover(
    const MasterInfo& info,
    const Future<Variable<Registry> >& recovery)
{
  updating = false;

  CHECK(!recovery.isPending());

  if (!recovery.isReady()) {
    recovered.get()->fail("Failed to recover registrar: " +
        (recovery.isFailed() ? recovery.failure() : "discarded"));
  } else {
    LOG(INFO) << "Successfully recovered registrar";

    // Save the registry.
    variable = recovery.get();

    // Perform the Recover operation to add the new MasterInfo.
    Owned<Operation> operation(new Recover(info));
    operations.push_back(operation);
    operation->future()
      .onAny(defer(self(), &Self::__recover, lambda::_1));

    update();
  }
}

void RegistrarProcess::__recover(const Future<bool>& recover)
{
  CHECK(!recover.isPending());

  if (!recover.isReady()) {
    recovered.get()->fail("Failed to recover registrar: "
        "Failed to persist MasterInfo: " +
        (recover.isFailed() ? recover.failure() : "discarded"));
  } else if (!recover.get()) {
    recovered.get()->fail("Failed to recover registrar: "
        "Failed to persist MasterInfo: version mismatch");
  } else {
    // At this point _update() has updated 'variable' to contain
    // the Registry with the latest MasterInfo.
    // Set the promise and un-gate any pending operations.
    CHECK_SOME(variable);
    recovered.get()->set(variable.get().get());
  }
}

Future<bool> RegistrarProcess::apply(Owned<Operation> operation)
{
  if (recovered.isNone()) {
    return Failure("Attempted to apply the operation before recovering");
  }

  return recovered.get()->future()
    .then(defer(self(), &Self::_apply, operation));
}

Future<bool> RegistrarProcess::_apply(Owned<Operation> operation)
{
  CHECK_SOME(variable);

  operations.push_back(operation);
  Future<bool> future = operation->future();
  if (!updating) {
    update();
  }
  return future;
}

void RegistrarProcess::update()
{
  if (operations.empty()) {
    return; // No-op.
  }

  CHECK(!updating);

  updating = true;

  LOG(INFO) << "Attempting to update the 'registry'";

  CHECK_SOME(variable);

  // Create a snapshot of the current registry.
  Registry registry = variable.get().get();

  // Create the 'slaveIDs' accumulator.
  hashset<SlaveID> slaveIDs;
  foreach (const Registry::Slave& slave, registry.slaves().slaves()) {
    slaveIDs.insert(slave.info().id());
  }

  foreach (Owned<Operation> operation, operations) {
    // No need to process the result of the operation.
    (*operation)(&registry, &slaveIDs, flags.registry_strict);
  }

  // Perform the store, and time the operation.
  metrics.state_store.time(state->store(variable.get().mutate(registry)))
    .after(flags.registry_store_timeout,
           lambda::bind(
               &timeout<Option<Variable<Registry> > >,
               "store",
               flags.registry_store_timeout,
               lambda::_1))
    .onAny(defer(self(), &Self::_update, lambda::_1, operations));

  // Clear the operations, _update will transition the Promises!
  operations.clear();
}

void RegistrarProcess::_update(
    const Future<Option<Variable<Registry> > >& store,
    deque<Owned<Operation> > applied)
{
  updating = false;

  // Set the variable if the storage operation succeeded.
  if (!store.isReady()) {
    LOG(ERROR) << "Failed to update 'registry': "
               << (store.isFailed() ? store.failure() : "discarded");
  } else if (store.get().isNone()) {
    LOG(WARNING) << "Failed to update 'registry': version mismatch";
  } else {
    LOG(INFO) << "Successfully updated 'registry'";
    variable = store.get().get();
  }

  // Remove the operations.
  while (!applied.empty()) {
    Owned<Operation> operation = applied.front();
    applied.pop_front();

    if (!store.isReady()) {
      operation->fail("Failed to update 'registry': " +
          (store.isFailed() ? store.failure() : "discarded"));
    } else {
      if (store.get().isNone()) {
        operation->fail("Failed to update 'registry': version mismatch");
      } else {
        operation->set();
      }
    }
  }

  applied.clear();

  if (!operations.empty()) {
    update();
  }
}

Registrar::Registrar(const Flags& flags, State* state)
{
  process = new RegistrarProcess(flags, state);
  spawn(process);
}

Registrar::~Registrar()
{
  terminate(process);
  wait(process);
  delete process;
}

Future<Registry> Registrar::recover(const MasterInfo& info)
{
  return dispatch(process, &RegistrarProcess::recover, info);
}

Future<bool> Registrar::apply(Owned<Operation> operation)
{
  return dispatch(process, &RegistrarProcess::apply, operation);
}

} // namespace master {
} // namespace internal {
} // namespace mesos {
<commit_msg>Added template parameter to Timer.<commit_after>/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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 <deque>
#include <string>

#include <process/defer.hpp>
#include <process/dispatch.hpp>
#include <process/future.hpp>
#include <process/help.hpp>
#include <process/http.hpp>
#include <process/id.hpp>
#include <process/owned.hpp>
#include <process/process.hpp>

#include <process/metrics/gauge.hpp>
#include <process/metrics/metrics.hpp>
#include <process/metrics/timer.hpp>

#include <stout/lambda.hpp>
#include <stout/none.hpp>
#include <stout/nothing.hpp>
#include <stout/option.hpp>
#include <stout/protobuf.hpp>

#include "common/type_utils.hpp"

#include "master/registrar.hpp"
#include "master/registry.hpp"

#include "state/protobuf.hpp"

using mesos::internal::state::protobuf::State;
using mesos::internal::state::protobuf::Variable;

using process::dispatch;
using process::spawn;
using process::terminate;
using process::wait; // Necessary on some OS's to disambiguate.

using process::DESCRIPTION;
using process::Failure;
using process::Future;
using process::HELP;
using process::Owned;
using process::Process;
using process::Promise;
using process::TLDR;
using process::USAGE;

using process::http::OK;

using process::metrics::Gauge;
using process::metrics::Timer;

using std::deque;
using std::string;

namespace mesos {
namespace internal {
namespace master {

using process::http::Response;
using process::http::Request;

class RegistrarProcess : public Process<RegistrarProcess>
{
public:
  RegistrarProcess(const Flags& _flags, State* _state)
    : ProcessBase(process::ID::generate("registrar")),
      metrics(*this),
      updating(false),
      flags(_flags),
      state(_state) {}

  virtual ~RegistrarProcess() {}

  // Registrar implementation.
  Future<Registry> recover(const MasterInfo& info);
  Future<bool> apply(Owned<Operation> operation);

protected:
  virtual void initialize()
  {
    route("/registry", registryHelp(), &RegistrarProcess::registry);
  }

private:
  // HTTP handlers.
  // /registrar(N)/registry
  Future<Response> registry(const Request& request);
  static string registryHelp();

  // The 'Recover' operation adds the latest MasterInfo.
  class Recover : public Operation
  {
  public:
    explicit Recover(const MasterInfo& _info) : info(_info) {}

  protected:
    virtual Try<bool> perform(
        Registry* registry,
        hashset<SlaveID>* slaveIDs,
        bool strict)
    {
      registry->mutable_master()->mutable_info()->CopyFrom(info);
      return true; // Mutation.
    }

  private:
    const MasterInfo info;
  };

  // Metrics.
  struct Metrics
  {
    explicit Metrics(const RegistrarProcess& process)
      : queued_operations(
          "registrar/queued_operations",
          defer(process, &RegistrarProcess::_queued_operations)),
        state_fetch("registrar/state_fetch"),
        state_store("registrar/state_store", Days(1))
    {
      process::metrics::add(queued_operations);

      process::metrics::add(state_fetch);
      process::metrics::add(state_store);
    }

    ~Metrics()
    {
      process::metrics::remove(queued_operations);

      process::metrics::remove(state_fetch);
      process::metrics::remove(state_store);
    }

    Gauge queued_operations;

    Timer<Milliseconds> state_fetch;
    Timer<Milliseconds> state_store;
  } metrics;

  // Gauge handlers
  double _queued_operations()
  {
    return operations.size();
  }

  // Continuations.
  void _recover(
      const MasterInfo& info,
      const Future<Variable<Registry> >& recovery);
  void __recover(const Future<bool>& recover);
  Future<bool> _apply(Owned<Operation> operation);

  // Helper for updating state (performing store).
  void update();
  void _update(
      const Future<Option<Variable<Registry> > >& store,
      deque<Owned<Operation> > operations);

  Option<Variable<Registry> > variable;
  deque<Owned<Operation> > operations;
  bool updating; // Used to signify fetching (recovering) or storing.

  const Flags flags;
  State* state;

  // Used to compose our operations with recovery.
  Option<Owned<Promise<Registry> > > recovered;
};

// Helper for treating State operations that timeout as failures.
template <typename T>
Future<T> timeout(
    const string& operation,
    const Duration& duration,
    Future<T> future)
{
  future.discard();

  return Failure(
      "Failed to perform " + operation + " within " + stringify(duration));
}

Future<Response> RegistrarProcess::registry(const Request& request)
{
  JSON::Object result;

  if (variable.isSome()) {
    result = JSON::Protobuf(variable.get().get());
  }

  return OK(result, request.query.get("jsonp"));
}

string RegistrarProcess::registryHelp()
{
  return HELP(
      TLDR(
          "Returns the current contents of the Registry in JSON."),
      USAGE(
          "/registrar(1)/registry"),
      DESCRIPTION(
          "Example:"
          "",
          "```",
          "{",
          "  \"master\":",
          "  {",
          "    \"info\":",
          "    {",
          "      \"hostname\": \"localhost\",",
          "      \"id\": \"20140325-235542-1740121354-5050-33357\",",
          "      \"ip\": 2130706433,",
          "      \"pid\": \"master@127.0.0.1:5050\",",
          "      \"port\": 5050",
          "    }",
          "  },",
          "",
          "  \"slaves\":",
          "  {",
          "    \"slaves\":",
          "    [",
          "      {",
          "        \"info\":",
          "        {",
          "          \"checkpoint\": true,",
          "          \"hostname\": \"localhost\",",
          "          \"id\":",
          "          { ",
          "            \"value\": \"20140325-234618-1740121354-5050-29065-0\"",
          "          },",
          "          \"port\": 5051,",
          "          \"resources\":",
          "          [",
          "            {",
          "              \"name\": \"cpus\",",
          "              \"role\": \"*\",",
          "              \"scalar\": { \"value\": 24 },",
          "              \"type\": \"SCALAR\"",
          "            }",
          "          ],",
          "          \"webui_hostname\": \"localhost\"",
          "        }",
          "      }",
          "    ]",
          "  }",
          "}",
          "```"));
}

Future<Registry> RegistrarProcess::recover(const MasterInfo& info)
{
  LOG(INFO) << "Recovering registrar";

  if (recovered.isNone()) {
    metrics.state_fetch.time(state->fetch<Registry>("registry"))
      .after(flags.registry_fetch_timeout,
             lambda::bind(
                 &timeout<Variable<Registry> >,
                 "fetch",
                 flags.registry_fetch_timeout,
                 lambda::_1))
      .onAny(defer(self(), &Self::_recover, info, lambda::_1));
    updating = true;
    recovered = Owned<Promise<Registry> >(new Promise<Registry>());
  }

  return recovered.get()->future();
}

void RegistrarProcess::_recover(
    const MasterInfo& info,
    const Future<Variable<Registry> >& recovery)
{
  updating = false;

  CHECK(!recovery.isPending());

  if (!recovery.isReady()) {
    recovered.get()->fail("Failed to recover registrar: " +
        (recovery.isFailed() ? recovery.failure() : "discarded"));
  } else {
    LOG(INFO) << "Successfully recovered registrar";

    // Save the registry.
    variable = recovery.get();

    // Perform the Recover operation to add the new MasterInfo.
    Owned<Operation> operation(new Recover(info));
    operations.push_back(operation);
    operation->future()
      .onAny(defer(self(), &Self::__recover, lambda::_1));

    update();
  }
}

void RegistrarProcess::__recover(const Future<bool>& recover)
{
  CHECK(!recover.isPending());

  if (!recover.isReady()) {
    recovered.get()->fail("Failed to recover registrar: "
        "Failed to persist MasterInfo: " +
        (recover.isFailed() ? recover.failure() : "discarded"));
  } else if (!recover.get()) {
    recovered.get()->fail("Failed to recover registrar: "
        "Failed to persist MasterInfo: version mismatch");
  } else {
    // At this point _update() has updated 'variable' to contain
    // the Registry with the latest MasterInfo.
    // Set the promise and un-gate any pending operations.
    CHECK_SOME(variable);
    recovered.get()->set(variable.get().get());
  }
}

Future<bool> RegistrarProcess::apply(Owned<Operation> operation)
{
  if (recovered.isNone()) {
    return Failure("Attempted to apply the operation before recovering");
  }

  return recovered.get()->future()
    .then(defer(self(), &Self::_apply, operation));
}

Future<bool> RegistrarProcess::_apply(Owned<Operation> operation)
{
  CHECK_SOME(variable);

  operations.push_back(operation);
  Future<bool> future = operation->future();
  if (!updating) {
    update();
  }
  return future;
}

void RegistrarProcess::update()
{
  if (operations.empty()) {
    return; // No-op.
  }

  CHECK(!updating);

  updating = true;

  LOG(INFO) << "Attempting to update the 'registry'";

  CHECK_SOME(variable);

  // Create a snapshot of the current registry.
  Registry registry = variable.get().get();

  // Create the 'slaveIDs' accumulator.
  hashset<SlaveID> slaveIDs;
  foreach (const Registry::Slave& slave, registry.slaves().slaves()) {
    slaveIDs.insert(slave.info().id());
  }

  foreach (Owned<Operation> operation, operations) {
    // No need to process the result of the operation.
    (*operation)(&registry, &slaveIDs, flags.registry_strict);
  }

  // Perform the store, and time the operation.
  metrics.state_store.time(state->store(variable.get().mutate(registry)))
    .after(flags.registry_store_timeout,
           lambda::bind(
               &timeout<Option<Variable<Registry> > >,
               "store",
               flags.registry_store_timeout,
               lambda::_1))
    .onAny(defer(self(), &Self::_update, lambda::_1, operations));

  // Clear the operations, _update will transition the Promises!
  operations.clear();
}

void RegistrarProcess::_update(
    const Future<Option<Variable<Registry> > >& store,
    deque<Owned<Operation> > applied)
{
  updating = false;

  // Set the variable if the storage operation succeeded.
  if (!store.isReady()) {
    LOG(ERROR) << "Failed to update 'registry': "
               << (store.isFailed() ? store.failure() : "discarded");
  } else if (store.get().isNone()) {
    LOG(WARNING) << "Failed to update 'registry': version mismatch";
  } else {
    LOG(INFO) << "Successfully updated 'registry'";
    variable = store.get().get();
  }

  // Remove the operations.
  while (!applied.empty()) {
    Owned<Operation> operation = applied.front();
    applied.pop_front();

    if (!store.isReady()) {
      operation->fail("Failed to update 'registry': " +
          (store.isFailed() ? store.failure() : "discarded"));
    } else {
      if (store.get().isNone()) {
        operation->fail("Failed to update 'registry': version mismatch");
      } else {
        operation->set();
      }
    }
  }

  applied.clear();

  if (!operations.empty()) {
    update();
  }
}

Registrar::Registrar(const Flags& flags, State* state)
{
  process = new RegistrarProcess(flags, state);
  spawn(process);
}

Registrar::~Registrar()
{
  terminate(process);
  wait(process);
  delete process;
}

Future<Registry> Registrar::recover(const MasterInfo& info)
{
  return dispatch(process, &RegistrarProcess::recover, info);
}

Future<bool> Registrar::apply(Owned<Operation> operation)
{
  return dispatch(process, &RegistrarProcess::apply, operation);
}

} // namespace master {
} // namespace internal {
} // namespace mesos {
<|endoftext|>


<commit_before>#include "../../../wrdSyntaxTest.hpp"

using namespace wrd;
using namespace std;

namespace {
    struct packTest : public wrdSyntaxTest {};

    class myfunc : public mgdFunc {
        WRD(CLASS(myfunc, mgdFunc))

        class myBlock : public blockExpr {
            WRD(CLASS(myBlock, blockExpr))

        public:
            str run(const ucontainable& args) override {
                WRD_I("hello world!");
                _executed = true;

                if(_lambda)
                    _res = _lambda(args, (frames&) wrd::thread::get().getFrames());
                return str();
            }

            void setLambda(function<wbool(const ucontainable&, const frames&)> lambda) {
                _lambda = lambda;
            }

            function<wbool(const ucontainable&, const frames&)> _lambda;
            wbool _res;
            wbool _executed;
        };

    public:
        myfunc(): super(params(), ttype<node>::get(), *new myBlock()) {
            WRD_I("myfunc(%x) new", this);
        }
        ~myfunc() {
            WRD_I("myfunc(%x) delete", this);
        }

        wbool isRun() const {
            return getBlock().cast<myBlock>()._executed;
        }

        void setLambda(function<wbool(const ucontainable&, const frames&)> lambda) {
            getBlock().cast<myBlock>()._lambda = lambda;
        }

        wbool isSuccess() const {
            return getBlock().cast<myBlock>()._res;
        }

        const wtype& getEvalType() const override {
            return ttype<node>::get();
        }

        const params& getParams() const override { return _params; }
        params& getParams() { return _params; }

    private:
        params _params;
    };

    class nativeFunc : public func {
        WRD(CLASS(nativeFunc, func))

    public:
        nativeFunc(): super() {}

        wbool isRun() const { return _executed; }

        void setLambda(function<wbool(const ucontainable&, const frames&)> lambda) {
            _lambda = lambda;
        }

        wbool isSuccess() const {
            return _res;
        }

        const wtype& getEvalType() const override {
            return ttype<node>::get();
        }

        const params& getParams() const override { return _params; }
        params& getParams() { return _params; }

        str run(const ucontainable& args) override {
            _executed = true;
            _res = _lambda(args, (frames&) wrd::thread::get().getFrames());
            return str();
        }

    private:
        function<wbool(const ucontainable&, const frames&)> _lambda;
        params _params;
        wbool _res;
        wbool _executed;
    };
}

TEST_F(packTest, parsePackTest) {
    make(manifest("demo")).parse(R"SRC(
pack demo
    )SRC").shouldParsed(true);
    ASSERT_FALSE(nul(getSubPack()));
    ASSERT_FALSE(nul(getPack().subs()));
    ASSERT_EQ(getPack().subs().len(), 0);
    ASSERT_EQ(getPack().getManifest().name, "demo");
}

TEST_F(packTest, packIsInFrameWhenCallMgdFunc) {
    // check whether pack's subnodes registered into frame when it calls:
    pack testPack(manifest("demo"), packLoadings());
    myfunc f1;

    params& ps = f1.getParams();
    ps.add(new param("age", ttype<wInt>::get()));
    ps.add(new param("grade", ttype<wFlt>::get()));
    f1.setLambda([](const auto& contain, const auto& sf) {
        const frame& fr = sf[sf.len() - 1];
        if(nul(fr)) return false;

        // checks pack is in frame:
        myfunc& cast = fr.sub<myfunc>("foo", narr(wInt(), wFlt()));
        if(nul(cast)) return false;

        const params& ps = cast.getParams();
        if(nul(ps)) return false;
        if(ps.len() != 2) return false;
        if(ps[0].getType() != ttype<wInt>()) return false;
        if(ps[1].getName() != "grade") return false;

        // checks args of funcs is in frame:
        wInt& age = fr.sub<wInt>("age");
        if(nul(age)) return false;
        if(age.cast<int>() != 1) return false;

        wFlt& grade = fr.sub("grade").cast<wFlt>();
        if(nul(grade)) return false;
        if(grade.get() < 3.4f || grade.get() > 3.6f) return false;

        return true;
    });

    testPack.subs().add("foo", f1);
    testPack.run("foo", narr(wInt(1), wFlt(3.5f)));
    ASSERT_TRUE(f1.isRun());
    ASSERT_TRUE(f1.isSuccess());
}

TEST_F(packTest, packIsNotInFrameWhenCallNativeFunc) {
    // check whether pack's subnodes not registered into frame when it calls:
    pack testPack(manifest("demo"), packLoadings());
    nativeFunc f1;
    params& ps = f1.getParams();
    ps.add(new param("age", ttype<wInt>::get()));
    ps.add(new param("grade", ttype<wFlt>::get()));
    f1.setLambda([](const auto& contain, const auto& sf) {
        const frame& fr = sf[sf.len() - 1];
        if(!nul(fr)) return WRD_E("fr == null"), false;

        return true;
    });
    testPack.subs().add("foo", f1);

    testPack.run("foo", narr(wInt(1), wFlt(3.5f)));
    ASSERT_TRUE(f1.isRun());
    ASSERT_TRUE(f1.isSuccess());
}
<commit_msg>wrd: test: param.getType() differs to param.getOrigin().getType()<commit_after>#include "../../../wrdSyntaxTest.hpp"

using namespace wrd;
using namespace std;

namespace {
    struct packTest : public wrdSyntaxTest {};

    class myfunc : public mgdFunc {
        WRD(CLASS(myfunc, mgdFunc))

        class myBlock : public blockExpr {
            WRD(CLASS(myBlock, blockExpr))

        public:
            str run(const ucontainable& args) override {
                WRD_I("hello world!");
                _executed = true;

                if(_lambda)
                    _res = _lambda(args, (frames&) wrd::thread::get().getFrames());
                return str();
            }

            void setLambda(function<wbool(const ucontainable&, const frames&)> lambda) {
                _lambda = lambda;
            }

            function<wbool(const ucontainable&, const frames&)> _lambda;
            wbool _res;
            wbool _executed;
        };

    public:
        myfunc(): super(params(), ttype<node>::get(), *new myBlock()) {
            WRD_I("myfunc(%x) new", this);
        }
        ~myfunc() {
            WRD_I("myfunc(%x) delete", this);
        }

        wbool isRun() const {
            return getBlock().cast<myBlock>()._executed;
        }

        void setLambda(function<wbool(const ucontainable&, const frames&)> lambda) {
            getBlock().cast<myBlock>()._lambda = lambda;
        }

        wbool isSuccess() const {
            return getBlock().cast<myBlock>()._res;
        }

        const wtype& getEvalType() const override {
            return ttype<node>::get();
        }

        const params& getParams() const override { return _params; }
        params& getParams() { return _params; }

    private:
        params _params;
    };

    class nativeFunc : public func {
        WRD(CLASS(nativeFunc, func))

    public:
        nativeFunc(): super() {}

        wbool isRun() const { return _executed; }

        void setLambda(function<wbool(const ucontainable&, const frames&)> lambda) {
            _lambda = lambda;
        }

        wbool isSuccess() const {
            return _res;
        }

        const wtype& getEvalType() const override {
            return ttype<node>::get();
        }

        const params& getParams() const override { return _params; }
        params& getParams() { return _params; }

        str run(const ucontainable& args) override {
            _executed = true;
            _res = _lambda(args, (frames&) wrd::thread::get().getFrames());
            return str();
        }

    private:
        function<wbool(const ucontainable&, const frames&)> _lambda;
        params _params;
        wbool _res;
        wbool _executed;
    };
}

TEST_F(packTest, parsePackTest) {
    make(manifest("demo")).parse(R"SRC(
pack demo
    )SRC").shouldParsed(true);
    ASSERT_FALSE(nul(getSubPack()));
    ASSERT_FALSE(nul(getPack().subs()));
    ASSERT_EQ(getPack().subs().len(), 0);
    ASSERT_EQ(getPack().getManifest().name, "demo");
}

TEST_F(packTest, packIsInFrameWhenCallMgdFunc) {
    // check whether pack's subnodes registered into frame when it calls:
    pack testPack(manifest("demo"), packLoadings());
    myfunc f1;

    params& ps = f1.getParams();
    ps.add(new param("age", ttype<wInt>::get()));
    ps.add(new param("grade", ttype<wFlt>::get()));
    f1.setLambda([](const auto& contain, const auto& sf) {
        const frame& fr = sf[sf.len() - 1];
        if(nul(fr)) return false;

        // checks pack is in frame:
        myfunc& cast = fr.sub<myfunc>("foo", narr(wInt(), wFlt()));
        if(nul(cast)) return false;

        const params& ps = cast.getParams();
        if(nul(ps)) return false;
        if(ps.len() != 2) return false;
        if(ps[0].getOrigin().getType() != ttype<wInt>()) return false;
        if(ps[1].getName() != "grade") return false;

        // checks args of funcs is in frame:
        wInt& age = fr.sub<wInt>("age");
        if(nul(age)) return false;
        if(age.cast<int>() != 1) return false;

        wFlt& grade = fr.sub("grade").cast<wFlt>();
        if(nul(grade)) return false;
        if(grade.get() < 3.4f || grade.get() > 3.6f) return false;

        return true;
    });

    testPack.subs().add("foo", f1);
    testPack.run("foo", narr(wInt(1), wFlt(3.5f)));
    ASSERT_TRUE(f1.isRun());
    ASSERT_TRUE(f1.isSuccess());
}

/* Concept changed: now, native call also make a frame instance.
 * TEST_F(packTest, packIsNotInFrameWhenCallNativeFunc) {
    // check whether pack's subnodes not registered into frame when it calls:
    pack testPack(manifest("demo"), packLoadings());
    nativeFunc f1;
    params& ps = f1.getParams();
    ps.add(new param("age", ttype<wInt>::get()));
    ps.add(new param("grade", ttype<wFlt>::get()));
    f1.setLambda([](const auto& contain, const auto& sf) {
        const frame& fr = sf[sf.len() - 1];
        if(!nul(fr)) return WRD_E("fr == null"), false;

        return true;
    });
    testPack.subs().add("foo", f1);

    testPack.run("foo", narr(wInt(1), wFlt(3.5f)));
    ASSERT_TRUE(f1.isRun());
    ASSERT_TRUE(f1.isSuccess());
}*/
<|endoftext|>


<commit_before>#include "webcam.hpp"
#include <chrono>

using namespace cv;
using namespace std;

Mat ellipticKernel(int width, int height = -1) {
 if (height==-1) {
  return getStructuringElement(MORPH_ELLIPSE,Size(width,width), Point(width/2, width/2));
 } else {
  return getStructuringElement(MORPH_ELLIPSE,Size(width,height), Point(width/2, height/2));
 }
}

unsigned long long getMilliseconds() {
 return chrono::system_clock::now().time_since_epoch()/chrono::milliseconds(1);
}

int main (int argc, char** argv) {

/*****
 begin camera setup
*****/

 CvCapture* capture = 0;
 int width, height;

 capture = cvCaptureFromCAM(0);

 if (!capture) {
  printf("No camera detected!");
  return -1;
 }

 ifstream config_file (".config");

 if (config_file.is_open()) {
// does not support corrupted .config

  string line;
  getline(config_file, line);
  istringstream(line)>>width;
  getline(config_file, line);
  istringstream(line)>>height;
  cvSetCaptureProperty(capture, CV_CAP_PROP_FRAME_WIDTH, width);
  cvSetCaptureProperty(capture, CV_CAP_PROP_FRAME_HEIGHT, height);

  config_file.close();

 } else {

  initResolutions();

  for (int i=36; i<150; i++) {
   cvSetCaptureProperty(capture, CV_CAP_PROP_FRAME_WIDTH, resolutions[i].width);
   cvSetCaptureProperty(capture, CV_CAP_PROP_FRAME_HEIGHT, resolutions[i].height);
  }

  width = cvGetCaptureProperty(capture, CV_CAP_PROP_FRAME_WIDTH);
  height = cvGetCaptureProperty(capture, CV_CAP_PROP_FRAME_HEIGHT);

  ofstream config_file_out(".config");
  config_file_out << width;
  config_file_out << "\n";
  config_file_out << height;
  config_file_out << "\n";
  config_file_out.close();
 }

 for (int i = 0; i < 30; i++) {
  // capture some frames so exposure correction takes place
  cvQueryFrame(capture);
 }

/*****
 end camera setup
*****/

/*****
 filter setup
*****/

 Mat acbg(256, 256, CV_8UC3, Scalar(0,0,0));
// accumulated background
 Mat acbg_m(256, 256, CV_8UC1, Scalar(0));
// accumulated background mask

 Mat acfg(256, 256, CV_8UC3, Scalar(0,0,0));
// accumulated foreground
 Mat acfg_t(256, 256, CV_8UC3, Scalar(0));
// accumulated foreground threshold

 Mat img_256_p(256, 256, CV_8UC3, Scalar(0,0,0));
// previous image
 Mat haar_t_p(256, 256, CV_8UC1, Scalar(1));
// previous possible locations for mouth

/*****
 end filter setup
*****/

/*****
 misc
*****/

 unsigned long long times[100];
 for (int i=0; i<100; i++)
  times[i] = 0;

 int tracker1, tracker2, tracker3;
 namedWindow("s",1);
 createTrackbar("1","s",&tracker1,100);
 createTrackbar("2","s",&tracker2,100);
 createTrackbar("3","s",&tracker3,100);

 unsigned long long timenow = getMilliseconds();

 bool keep_going = true;

 CascadeClassifier mouth_cascade;
 mouth_cascade.load("Mouth.xml");

/*****
 begin filter loop
*****/

 while (keep_going) {

  Mat image(cvQueryFrame(capture));
//  imshow("webcam", image);
// at some point preprocess by rotating according to OVR roll
// right now really annoying because of state variables

  Mat gray, img_256, gray_256;
  resize(image, img_256, Size(256, 256));
  cvtColor(image, gray, CV_RGB2GRAY);
  cvtColor(img_256, gray_256, CV_RGB2GRAY);

// this mask gets anything kind of dark (DK2) and dilates
// should work *great*
  Mat gr_m;
// gray mask
  equalizeHist(gray_256, gr_m);
  threshold(gr_m, gr_m, 215, 1, THRESH_BINARY);
  dilate(gr_m, gr_m, ellipticKernel(23));
  erode(gr_m, gr_m, ellipticKernel(45));

//  imshow("gray mask", gray_256.mul(1-gr_m));

  bitwise_or(acbg_m, gr_m, acbg_m);
//  imshow("accumulated bg mask", gray_256.mul(1-acbg_m));

// this mask watches for flow against accumulated bg
  Mat fl_m;
// flow mask
  absdiff(img_256, acbg, fl_m);
  cvtColor(fl_m, fl_m, CV_BGR2GRAY);
  fl_m = acbg_m.mul(fl_m);
  threshold(fl_m, fl_m, 45, 1, THRESH_BINARY_INV);
  erode(fl_m, fl_m, ellipticKernel(51));
//  imshow("flow mask", fl_m*255);

  Mat bg_m;
  bitwise_and(acbg_m, fl_m, bg_m);
  bitwise_or(gr_m, bg_m, bg_m);
//  imshow("bgm", bg_m*255);

// maybe do some morphological operations on bg_m?
// previously combined bg_m with its opening

// ugly way to compute:
// acbg = [acbg'.(1-bg_m)]+[((acbg'+img_256)/2).bg_m]
// find a nicer way later
// problem is masks are 1 channel while images are 3 channel

  Mat bg_m3;
  Mat tmp0[3];
  tmp0[0] = tmp0[1] = tmp0[2] = bg_m;
  merge(tmp0, 3, bg_m3);
//  imshow("bg_m3", bg_m3*255);
  acbg = acbg.mul(Scalar(1,1,1)-bg_m3) + (acbg/2+img_256/2).mul(bg_m3);
//  imshow("acbg", acbg);

//  imshow("bg mask", gray_256.mul(1-bg_m));

  Mat df_m;
// delta flow mask
  absdiff(img_256, img_256_p, df_m);
  cvtColor(df_m, df_m, CV_BGR2GRAY);
  threshold(df_m, df_m, 10, 1, THRESH_BINARY);
  int t1,t2;
  t1 = tracker1+1-(tracker1%2);
  if (t1<3) t1=3;
  if (t1>90) t1=91;
  t2 = tracker2+1-(tracker2%2);
  if (t2<3) t2=3;
  if (t2>90) t2=91;

  erode(df_m, df_m, ellipticKernel(t1, t2));
  imshow("delta flow mask", df_m*255);
  img_256_p = img_256.clone();

  Mat haar_m;
// bitwise_and(1 - bg_m, fg_m, haar_m);
  haar_m = 1 - bg_m;

// run haar classifier
  int scale = width/300;
  if (scale < 1)
   scale = 1;
// can't use 256x256 since haar isn't stretch invariant

  Mat thingy;
  resize(gray, thingy, Size(width/scale, height/scale));
  equalizeHist(thingy, thingy);
///////////////////
// need to change this after foreground stuff gets written

  vector<Rect> mouth_rects;
  resize(haar_m, haar_m, Size(width/scale, height/scale));

  thingy = thingy.mul(haar_m);
/////////////////

  mouth_cascade.detectMultiScale(thingy, mouth_rects, 1.1, 0, CV_HAAR_SCALE_IMAGE);
  Mat rect_image(height, width, CV_8UC1, Scalar(0));

  for (size_t i=0; i<mouth_rects.size(); i++) {
   Rect scaled(mouth_rects[i].x*scale, mouth_rects[i].y*scale, mouth_rects[i].width*scale,mouth_rects[i].height*scale);
   Mat new_rect(height, width, CV_8UC1, Scalar(0));
   rectangle(new_rect, scaled, Scalar(1), CV_FILLED);
   rect_image += new_rect;
  }

  double min_val, max_val;
  minMaxLoc(rect_image, &min_val, &max_val);

// or maybe equalize? this whole thing needs to be rewritten
// with the new fg and temporal coherence ideas

  Mat rect_thresh;
  threshold(rect_image, rect_thresh, max_val*0.9, 1, THRESH_BINARY);
  imshow("mouth", rect_thresh.mul(gray));

/*
  Moments m = moments(rect_thresh, 1);
  circle(image, Point(m.m10/m.m00,m.m01/m.m00),20,Scalar(128),30);
  imshow("centroid", image);
*/

  keep_going = (waitKey(1)<0);

 }

 cvReleaseCapture(&capture);

 return 0;
}

<commit_msg>hacking<commit_after>#include "webcam.hpp"
#include <chrono>

using namespace cv;
using namespace std;

Mat ellipticKernel(int width, int height = -1) {
 if (height==-1) {
  return getStructuringElement(MORPH_ELLIPSE,Size(width,width), Point(width/2, width/2));
 } else {
  return getStructuringElement(MORPH_ELLIPSE,Size(width,height), Point(width/2, height/2));
 }
}

unsigned long long getMilliseconds() {
 return chrono::system_clock::now().time_since_epoch()/chrono::milliseconds(1);
}

int main (int argc, char** argv) {

/*****
 begin camera setup
*****/

 CvCapture* capture = 0;
 int width, height;

 capture = cvCaptureFromCAM(0);

 if (!capture) {
  printf("No camera detected!");
  return -1;
 }

 ifstream config_file (".config");

 if (config_file.is_open()) {
// does not support corrupted .config

  string line;
  getline(config_file, line);
  istringstream(line)>>width;
  getline(config_file, line);
  istringstream(line)>>height;
  cvSetCaptureProperty(capture, CV_CAP_PROP_FRAME_WIDTH, width);
  cvSetCaptureProperty(capture, CV_CAP_PROP_FRAME_HEIGHT, height);

  config_file.close();

 } else {

  initResolutions();

  for (int i=36; i<150; i++) {
   cvSetCaptureProperty(capture, CV_CAP_PROP_FRAME_WIDTH, resolutions[i].width);
   cvSetCaptureProperty(capture, CV_CAP_PROP_FRAME_HEIGHT, resolutions[i].height);
  }

  width = cvGetCaptureProperty(capture, CV_CAP_PROP_FRAME_WIDTH);
  height = cvGetCaptureProperty(capture, CV_CAP_PROP_FRAME_HEIGHT);

  ofstream config_file_out(".config");
  config_file_out << width;
  config_file_out << "\n";
  config_file_out << height;
  config_file_out << "\n";
  config_file_out.close();
 }

 for (int i = 0; i < 30; i++) {
  // capture some frames so exposure correction takes place
  cvQueryFrame(capture);
 }

/*****
 end camera setup
*****/

/*****
 filter setup
*****/

 Mat acbg(256, 256, CV_8UC3, Scalar(0,0,0));
// accumulated background
 Mat acbg_m(256, 256, CV_8UC1, Scalar(0));
// accumulated background mask

 Mat acfg(256, 256, CV_8UC3, Scalar(0,0,0));
// accumulated foreground
 Mat acfg_t(256, 256, CV_8UC3, Scalar(0));
// accumulated foreground threshold

 Mat img_256_p(256, 256, CV_8UC3, Scalar(0,0,0));
// previous image
 Mat haar_t_p(256, 256, CV_8UC1, Scalar(1));
// previous possible locations for mouth

/*****
 end filter setup
*****/

/*****
 misc
*****/

 unsigned long long times[100];
 for (int i=0; i<100; i++)
  times[i] = 0;

 int tracker1, tracker2, tracker3;
 namedWindow("s",1);
 createTrackbar("1","s",&tracker1,100);
 createTrackbar("2","s",&tracker2,100);
 createTrackbar("3","s",&tracker3,100);

 unsigned long long timenow = getMilliseconds();

 bool keep_going = true;

 CascadeClassifier mouth_cascade;
 mouth_cascade.load("Mouth.xml");

/*****
 begin filter loop
*****/

 while (keep_going) {

  Mat image(cvQueryFrame(capture));
//  imshow("webcam", image);
// at some point preprocess by rotating according to OVR roll
// right now really annoying because of state variables

  Mat gray, img_256, gray_256;
  resize(image, img_256, Size(256, 256));
  cvtColor(image, gray, CV_RGB2GRAY);
  cvtColor(img_256, gray_256, CV_RGB2GRAY);

// this mask gets anything kind of dark (DK2) and dilates
// should work *great*
  Mat gr_m;
// gray mask
  equalizeHist(gray_256, gr_m);
  threshold(gr_m, gr_m, 215, 1, THRESH_BINARY);
  dilate(gr_m, gr_m, ellipticKernel(23));
  erode(gr_m, gr_m, ellipticKernel(45));

//  imshow("gray mask", gray_256.mul(1-gr_m));

  bitwise_or(acbg_m, gr_m, acbg_m);
//  imshow("accumulated bg mask", gray_256.mul(1-acbg_m));

// this mask watches for flow against accumulated bg
  Mat fl_m;
// flow mask
  absdiff(img_256, acbg, fl_m);
  cvtColor(fl_m, fl_m, CV_BGR2GRAY);
  fl_m = acbg_m.mul(fl_m);
  threshold(fl_m, fl_m, 45, 1, THRESH_BINARY_INV);
  erode(fl_m, fl_m, ellipticKernel(51));
//  imshow("flow mask", fl_m*255);

  Mat bg_m;
  bitwise_and(acbg_m, fl_m, bg_m);
  bitwise_or(gr_m, bg_m, bg_m);
//  imshow("bgm", bg_m*255);

// maybe do some morphological operations on bg_m?
// previously combined bg_m with its opening

// ugly way to compute:
// acbg = [acbg'.(1-bg_m)]+[((acbg'+img_256)/2).bg_m]
// find a nicer way later
// problem is masks are 1 channel while images are 3 channel

  Mat bg_m3;
  Mat tmp0[3];
  tmp0[0] = tmp0[1] = tmp0[2] = bg_m;
  merge(tmp0, 3, bg_m3);
//  imshow("bg_m3", bg_m3*255);
  acbg = acbg.mul(Scalar(1,1,1)-bg_m3) + (acbg/2+img_256/2).mul(bg_m3);
//  imshow("acbg", acbg);

//  imshow("bg mask", gray_256.mul(1-bg_m));

  Mat df_m;
// delta flow mask
  absdiff(img_256, img_256_p, df_m);
  cvtColor(df_m, df_m, CV_BGR2GRAY);
  threshold(df_m, df_m, 10, 1, THRESH_BINARY);
  int t1,t2;
  t1 = tracker1+1-(tracker1%2);
  if (t1<3) t1=3;
  if (t1>90) t1=91;
  t2 = tracker2+1-(tracker2%2);
  if (t2<3) t2=3;
  if (t2>90) t2=91;

  erode(df_m, df_m, ellipticKernel(5, 5));
  dilate(df_m, df_m, ellipticKernel(t1, t2));
  imshow("delta flow mask", df_m*255);
  img_256_p = img_256.clone();

  Mat haar_m;
// bitwise_and(1 - bg_m, fg_m, haar_m);
  haar_m = 1 - bg_m;

// run haar classifier
  int scale = width/300;
  if (scale < 1)
   scale = 1;
// can't use 256x256 since haar isn't stretch invariant

  Mat thingy;
  resize(gray, thingy, Size(width/scale, height/scale));
  equalizeHist(thingy, thingy);
///////////////////
// need to change this after foreground stuff gets written

  vector<Rect> mouth_rects;
  resize(haar_m, haar_m, Size(width/scale, height/scale));

  thingy = thingy.mul(haar_m);
/////////////////

  mouth_cascade.detectMultiScale(thingy, mouth_rects, 1.1, 0, CV_HAAR_SCALE_IMAGE);
  Mat rect_image(height, width, CV_8UC1, Scalar(0));

  for (size_t i=0; i<mouth_rects.size(); i++) {
   Rect scaled(mouth_rects[i].x*scale, mouth_rects[i].y*scale, mouth_rects[i].width*scale,mouth_rects[i].height*scale);
   Mat new_rect(height, width, CV_8UC1, Scalar(0));
   rectangle(new_rect, scaled, Scalar(1), CV_FILLED);
   rect_image += new_rect;
  }

  double min_val, max_val;
  minMaxLoc(rect_image, &min_val, &max_val);

// or maybe equalize? this whole thing needs to be rewritten
// with the new fg and temporal coherence ideas

  Mat rect_thresh;
  threshold(rect_image, rect_thresh, max_val*0.9, 1, THRESH_BINARY);
  imshow("mouth", rect_thresh.mul(gray));

/*
  Moments m = moments(rect_thresh, 1);
  circle(image, Point(m.m10/m.m00,m.m01/m.m00),20,Scalar(128),30);
  imshow("centroid", image);
*/

  keep_going = (waitKey(1)<0);

 }

 cvReleaseCapture(&capture);

 return 0;
}

<|endoftext|>


<commit_before>#include "webcam.hpp"

using namespace cv;
using namespace std;

void morphFast(Mat inout, int smallsize = 100, int factor = 25, int eq = 1, int diler = 0) {
  int width, height;
  width = inout.size().width;
  height = inout.size().height;
  factor = factor + (1-(factor%2));
  Mat downsample;
  resize(inout, downsample, Size(smallsize,smallsize));
  Mat kernel = getStructuringElement(MORPH_ELLIPSE,Size(factor,factor));
  if (diler) {
   dilate(downsample, downsample, kernel);
  } else {
   erode(downsample, downsample, kernel);
  }
  if (eq) {
   equalizeHist(downsample, downsample);
  }
  resize(downsample, inout, Size(width, height));
}

int main (int argc, char** argv) {

 int tracker1, tracker2, tracker3;
 namedWindow("s",1);
 createTrackbar("1","s",&tracker1,100);
 createTrackbar("2","s",&tracker2,100);
 createTrackbar("3","s",&tracker3,100);

 CvCapture* capture = 0;
 int width, height, fps;

 capture = cvCaptureFromCAM(0);

 if (!capture) {
  printf("No camera detected!");
  return -1;
 }

 ifstream configFile (".config");

 if (configFile.is_open()) {

  //probably want to support corrupted .config
  string line;
  getline(configFile, line);
  istringstream(line)>>width;
  getline(configFile, line);
  istringstream(line)>>height;
  cvSetCaptureProperty(capture, CV_CAP_PROP_FRAME_WIDTH, width);
  cvSetCaptureProperty(capture, CV_CAP_PROP_FRAME_HEIGHT, height);

  configFile.close();

 } else {

  initResolutions();

  for (int i=36; i<150; i++) {
   cvSetCaptureProperty(capture, CV_CAP_PROP_FRAME_WIDTH, resolutions[i].width);
   cvSetCaptureProperty(capture, CV_CAP_PROP_FRAME_HEIGHT, resolutions[i].height);
  }

  width = cvGetCaptureProperty(capture, CV_CAP_PROP_FRAME_WIDTH);
  height = cvGetCaptureProperty(capture, CV_CAP_PROP_FRAME_HEIGHT);

  ofstream configFileOut(".config");
  configFileOut << width;
  configFileOut << "\n";
  configFileOut << height;
  configFileOut << "\n";
  configFileOut.close();
 }

 bool keepGoing = true;

// srand(890);//not interested in good randomness

 for (int i = 0; i < 30; i++) {
  // capture some frames so exposure correction takes place
  cvQueryFrame(capture);
 }

 Mat background = cvQueryFrame(capture);
 background = background.clone();
 blur(background, background, Size(50,50));

 Mat image;
 Mat channel[3];

 while (keepGoing) {

  image = cvQueryFrame(capture);
// preprocess by rotating according to OVR roll
  imshow("webcam", image);

// let's make multiple masks where 0=not mouth, 1=uncertain

// then multiply them together and multiply that with image
// and run haar classifier on image

  Mat gray;
  cvtColor(image, gray, CV_RGB2GRAY);

// this mask filters out areas with too many edges
  Mat canny;
  Canny(gray, canny, 50, 50, 3);
  blur(canny, canny, Size(width/20,height/20));
  bitwise_not(canny, canny);
  threshold(canny, canny, 200, 1, THRESH_BINARY);
  blur(canny*255, canny, Size(width/10, height/10));
  threshold(canny, canny, 220, 1, THRESH_BINARY);
//  imshow("canny mask", gray.mul(canny));

// this mask filters out areas which have not changed much
// background needs to be updated when person is not in frame
// use OVR SDK to do this later
  Mat flow;
  blur(image, flow, Size(50,50));
  absdiff(flow, background, flow);
  cvtColor(flow, flow, CV_RGB2GRAY);
  morphFast(flow);
  threshold(flow, flow, 60, 1, THRESH_BINARY);
//  imshow("flow mask", gray.mul(flow));

// this mask gets anything kind of dark (DK2) and dilates
  Mat kindofdark;
  equalizeHist(gray, kindofdark);
  threshold(kindofdark, kindofdark, 100, 1, THRESH_BINARY_INV);
  imshow("lo", kindofdark*255);
  waitKey(1);
  morphFast(kindofdark, 100, 17, 0);
//  imshow("dark mask", gray.mul(kindofdark));

  Mat mask = flow.mul(kindofdark).mul(canny);
  morphFast(mask, 100, tracker1+11-(tracker1%2), 0, 1);
  morphFast(mask, 100, tracker2+11-(tracker2%2), 0, 0);

//  imshow("mask", gray.mul(mask));

//  Moments lol = moments(mask, 1);
//  circle(image, Point(lol.m10/lol.m00,lol.m01/lol.m00),20,Scalar(128),30);
//  imshow("leimage", image);

/*
  CascadeClassifier mouth_cascade;
  mouth_cascade.load("Mouth.xml");
  vector<Rect> mouths;
  Mat classifyThis;
  bilateralFilter(gray, classifyThis, 15, 10, 1);
  equalizeHist(classifyThis, classifyThis);
  classifyThis = classifyThis.mul(mask);
  mouth_cascade.detectMultiScale(classifyThis, mouths, 1.1, 2, CV_HAAR_SCALE_IMAGE);
  for (size_t i=0; i<mouths.size(); i++) {
   Point center( mouths[i].x + mouths[i].width*0.5, mouths[i].y + mouths[i].height*0.5 );
   ellipse( image, center, Size( mouths[i].width*0.5, mouths[i].height*0.5), 0, 0, 360, Scalar( 255, 0, 255 ), 4, 8, 0 );
  }
  imshow("MOUTH", image);
*/
  keepGoing = (waitKey(25)<0);

 }

 cvReleaseCapture(&capture);

 return 0;
}
<commit_msg>hacking<commit_after>#include "webcam.hpp"

using namespace cv;
using namespace std;

void morphFast(Mat inout, int smallsize = 100, int factor = 25, int eq = 1, int diler = 0) {
  int width, height;
  width = inout.size().width;
  height = inout.size().height;
  factor = factor + (1-(factor%2));
  Mat downsample;
  resize(inout, downsample, Size(smallsize,smallsize));
  Mat kernel = getStructuringElement(MORPH_ELLIPSE,Size(factor,factor));
  if (diler) {
   dilate(downsample, downsample, kernel);
  } else {
   erode(downsample, downsample, kernel);
  }
  if (eq) {
   equalizeHist(downsample, downsample);
  }
  resize(downsample, inout, Size(width, height));
}

int main (int argc, char** argv) {

 int tracker1, tracker2, tracker3;
 namedWindow("s",1);
 createTrackbar("1","s",&tracker1,100);
 createTrackbar("2","s",&tracker2,100);
 createTrackbar("3","s",&tracker3,100);

 CvCapture* capture = 0;
 int width, height, fps;

 capture = cvCaptureFromCAM(0);

 if (!capture) {
  printf("No camera detected!");
  return -1;
 }

 ifstream configFile (".config");

 if (configFile.is_open()) {

  //probably want to support corrupted .config
  string line;
  getline(configFile, line);
  istringstream(line)>>width;
  getline(configFile, line);
  istringstream(line)>>height;
  cvSetCaptureProperty(capture, CV_CAP_PROP_FRAME_WIDTH, width);
  cvSetCaptureProperty(capture, CV_CAP_PROP_FRAME_HEIGHT, height);

  configFile.close();

 } else {

  initResolutions();

  for (int i=36; i<150; i++) {
   cvSetCaptureProperty(capture, CV_CAP_PROP_FRAME_WIDTH, resolutions[i].width);
   cvSetCaptureProperty(capture, CV_CAP_PROP_FRAME_HEIGHT, resolutions[i].height);
  }

  width = cvGetCaptureProperty(capture, CV_CAP_PROP_FRAME_WIDTH);
  height = cvGetCaptureProperty(capture, CV_CAP_PROP_FRAME_HEIGHT);

  ofstream configFileOut(".config");
  configFileOut << width;
  configFileOut << "\n";
  configFileOut << height;
  configFileOut << "\n";
  configFileOut.close();
 }

 bool keepGoing = true;

// srand(890);//not interested in good randomness

 for (int i = 0; i < 30; i++) {
  // capture some frames so exposure correction takes place
  cvQueryFrame(capture);
 }

 Mat background = cvQueryFrame(capture);
 background = background.clone();
 blur(background, background, Size(50,50));

 Mat image;
 Mat channel[3];

 while (keepGoing) {

  image = cvQueryFrame(capture);
// preprocess by rotating according to OVR roll
//  imshow("webcam", image);

// let's make multiple masks where 0=not mouth, 1=uncertain

// then multiply them together and multiply that with image
// and run haar classifier on image

  Mat gray;
  cvtColor(image, gray, CV_RGB2GRAY);

// this mask filters out areas with too many edges
  Mat canny;
  Canny(gray, canny, 50, 50, 3);
  blur(canny, canny, Size(width/20,height/20));
  bitwise_not(canny, canny);
  threshold(canny, canny, 200, 1, THRESH_BINARY);
  blur(canny*255, canny, Size(width/10, height/10));
  threshold(canny, canny, 220, 1, THRESH_BINARY);
//  imshow("canny mask", gray.mul(canny));

// this mask filters out areas which have not changed much
// background needs to be updated when person is not in frame
// use OVR SDK to do this later
  Mat flow;
  blur(image, flow, Size(50,50));
  absdiff(flow, background, flow);
  cvtColor(flow, flow, CV_RGB2GRAY);
  morphFast(flow);
  threshold(flow, flow, 60, 1, THRESH_BINARY);
//  imshow("flow mask", gray.mul(flow));

// this mask gets anything kind of dark (DK2) and dilates
  Mat kindofdark;
  equalizeHist(gray, kindofdark);
  threshold(kindofdark, kindofdark, 100, 1, THRESH_BINARY_INV);
  morphFast(kindofdark, 100, 17, 0);
//  imshow("dark mask", gray.mul(kindofdark));

  Mat mask = flow.mul(kindofdark).mul(canny);
  morphFast(mask, 100, tracker1+11-(tracker1%2), 0, 1);
  morphFast(mask, 100, tracker2+11-(tracker2%2), 0, 0);

  imshow("mask", gray.mul(mask));

//  Moments lol = moments(mask, 1);
//  circle(image, Point(lol.m10/lol.m00,lol.m01/lol.m00),20,Scalar(128),30);
//  imshow("leimage", image);

/*
  CascadeClassifier mouth_cascade;
  mouth_cascade.load("Mouth.xml");
  vector<Rect> mouths;
  Mat classifyThis;
  bilateralFilter(gray, classifyThis, 15, 10, 1);
  equalizeHist(classifyThis, classifyThis);
  classifyThis = classifyThis.mul(mask);
  mouth_cascade.detectMultiScale(classifyThis, mouths, 1.1, 2, CV_HAAR_SCALE_IMAGE);
  for (size_t i=0; i<mouths.size(); i++) {
   Point center( mouths[i].x + mouths[i].width*0.5, mouths[i].y + mouths[i].height*0.5 );
   ellipse( image, center, Size( mouths[i].width*0.5, mouths[i].height*0.5), 0, 0, 360, Scalar( 255, 0, 255 ), 4, 8, 0 );
  }
  imshow("MOUTH", image);
*/
  keepGoing = (waitKey(25)<0);

 }

 cvReleaseCapture(&capture);

 return 0;
}
<|endoftext|>