<commit_before>/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve.
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 "paddle/operators/box_coder_op.h"

namespace paddle {
namespace operators {

class BoxCoderOp : public framework::OperatorWithKernel {
 public:
  using framework::OperatorWithKernel::OperatorWithKernel;

 protected:
  void InferShape(framework::InferShapeContext *ctx) const override {
    PADDLE_ENFORCE(ctx->HasInput("PriorBox"),
                   "Input(PriorBox) of BoxCoderOp should not be null.");
    PADDLE_ENFORCE(ctx->HasInput("PriorBoxVar"),
                   "Input(PriorBoxVar) of BoxCoderOp should not be null.");
    PADDLE_ENFORCE(ctx->HasInput("PriorBox"),
                   "Input(TargetBox) of BoxCoderOp should not be null.");

    auto prior_box_dims = ctx->GetInputDim("PriorBox");
    auto prior_box_var_dims = ctx->GetInputDim("PriorBoxVar");
    auto target_box_dims = ctx->GetInputDim("TargetBox");

    PADDLE_ENFORCE_EQ(prior_box_dims.size(), 2UL,
                      "The shape of PriorBox is [N, 4]");
    PADDLE_ENFORCE_EQ(prior_box_dims[1], 4UL,
                      "The shape of PriorBox is [N, 4]");
    PADDLE_ENFORCE_EQ(prior_box_var_dims.size(), 2UL,
                      "The shape of PriorBoxVar is [N, 4]");
    PADDLE_ENFORCE_EQ(prior_box_var_dims[1], 4UL,
                      "The shape of PriorBoxVar is [N, 4]");
    PADDLE_ENFORCE_EQ(target_box_dims.size(), 2UL,
                      "The shape of TargetBox is [M, 4]");
    PADDLE_ENFORCE_EQ(target_box_dims[1], 4UL,
                      "The shape of TargetBox is [M, 4]");

    GetBoxCodeType(ctx->Attrs().Get<std::string>("code_type"));

    ctx->SetOutputDim("OutputBox", framework::make_ddim({target_box_dims[0],
                                                         target_box_dims[1]}));
  }
};

class BoxCoderOpMaker : public framework::OpProtoAndCheckerMaker {
 public:
  BoxCoderOpMaker(OpProto *proto, OpAttrChecker *op_checker)
      : OpProtoAndCheckerMaker(proto, op_checker) {
    AddInput(
        "PriorBox",
        "(Tensor, default Tensor<float>) "
        "Box list PriorBox is a 2-D Tensor with shape [M, 4] holds N boxes, "
        "each box is represented as [xmin, ymin, xmax, ymax], "
        "[xmin, ymin] is the left top coordinate of the anchor box, "
        "if the input is image feature map, they are close to the origin "
        "of the coordinate system. [xmax, ymax] is the right bottom "
        "coordinate of the anchor box.");
    AddInput("PriorBoxVar",
             "(Tensor, default Tensor<float>) "
             "PriorBoxVar is a 2-D Tensor with shape [M, 4] holds N group "
             "of variance.");
    AddInput(
        "TargetBox",
        "(LoDTensor or Tensor) this input is a 2-D LoDTensor with shape "
        "[N, 4], each box is represented as [xmin, ymin, xmax, ymax], "
        "[xmin, ymin] is the left top coordinate of the box if the input "
        "is image feature map, they are close to the origin of the coordinate "
        "system. [xmax, ymax] is the right bottom coordinate of the box. "
        "This tensor can contain LoD information to represent a batch "
        "of inputs. One instance of this batch can contain different "
        "numbers of entities.");
    AddAttr<std::string>("code_type",
                         "(string, default encode_center_size) "
                         "the code type used with the target box")
        .SetDefault("encode_center_size")
        .InEnum({"encode_center_size", "decode_center_size"});
    AddOutput(
        "OutputBox",
        "(Tensor, default Tensor<float>)"
        "(Tensor) The output of box_coder_op, a tensor with shape [N, M, 4] "
        "representing the result of N target boxes encoded/decoded with "
        "M Prior boxes and variances.");

    AddComment(R"DOC(
Bounding Box Coder Operator.
Encode/Decode the priorbox information with the target bounding box.
)DOC");
  }
};

}  // namespace operators
}  // namespace paddle

namespace ops = paddle::operators;
REGISTER_OP_WITHOUT_GRADIENT(box_coder, ops::BoxCoderOp, ops::BoxCoderOpMaker);
REGISTER_OP_CPU_KERNEL(box_coder, ops::BoxCoderKernel<float>,
                       ops::BoxCoderKernel<double>);
<commit_msg>Update box_coder_op.cc<commit_after>/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve.
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 "paddle/operators/box_coder_op.h"

namespace paddle {
namespace operators {

class BoxCoderOp : public framework::OperatorWithKernel {
 public:
  using framework::OperatorWithKernel::OperatorWithKernel;

 protected:
  void InferShape(framework::InferShapeContext *ctx) const override {
    PADDLE_ENFORCE(ctx->HasInput("PriorBox"),
                   "Input(PriorBox) of BoxCoderOp should not be null.");
    PADDLE_ENFORCE(ctx->HasInput("PriorBoxVar"),
                   "Input(PriorBoxVar) of BoxCoderOp should not be null.");
    PADDLE_ENFORCE(ctx->HasInput("PriorBox"),
                   "Input(TargetBox) of BoxCoderOp should not be null.");

    auto prior_box_dims = ctx->GetInputDim("PriorBox");
    auto prior_box_var_dims = ctx->GetInputDim("PriorBoxVar");
    auto target_box_dims = ctx->GetInputDim("TargetBox");

    PADDLE_ENFORCE_EQ(prior_box_dims.size(), 2UL,
                      "The rank of Input of PriorBox must be 2");
    PADDLE_ENFORCE_EQ(prior_box_dims[1], 4UL,
                      "The shape of PriorBox is [N, 4]");
    PADDLE_ENFORCE_EQ(prior_box_var_dims.size(), 2UL,
                      "The rank of Input of PriorBoxVar must be 2");
    PADDLE_ENFORCE_EQ(prior_box_var_dims[1], 4UL,
                      "The shape of PriorBoxVar is [N, 4]");
    PADDLE_ENFORCE_EQ(target_box_dims.size(), 2UL,
                      "The rank of Input of TargetBox must be 2");
    PADDLE_ENFORCE_EQ(target_box_dims[1], 4UL,
                      "The shape of TargetBox is [M, 4]");

    GetBoxCodeType(ctx->Attrs().Get<std::string>("code_type"));

    ctx->SetOutputDim("OutputBox", framework::make_ddim({target_box_dims[0],
                                                         target_box_dims[1]}));
  }
};

class BoxCoderOpMaker : public framework::OpProtoAndCheckerMaker {
 public:
  BoxCoderOpMaker(OpProto *proto, OpAttrChecker *op_checker)
      : OpProtoAndCheckerMaker(proto, op_checker) {
    AddInput(
        "PriorBox",
        "(Tensor, default Tensor<float>) "
        "Box list PriorBox is a 2-D Tensor with shape [M, 4] holds N boxes, "
        "each box is represented as [xmin, ymin, xmax, ymax], "
        "[xmin, ymin] is the left top coordinate of the anchor box, "
        "if the input is image feature map, they are close to the origin "
        "of the coordinate system. [xmax, ymax] is the right bottom "
        "coordinate of the anchor box.");
    AddInput("PriorBoxVar",
             "(Tensor, default Tensor<float>) "
             "PriorBoxVar is a 2-D Tensor with shape [M, 4] holds N group "
             "of variance.");
    AddInput(
        "TargetBox",
        "(LoDTensor or Tensor) this input is a 2-D LoDTensor with shape "
        "[N, 4], each box is represented as [xmin, ymin, xmax, ymax], "
        "[xmin, ymin] is the left top coordinate of the box if the input "
        "is image feature map, they are close to the origin of the coordinate "
        "system. [xmax, ymax] is the right bottom coordinate of the box. "
        "This tensor can contain LoD information to represent a batch "
        "of inputs. One instance of this batch can contain different "
        "numbers of entities.");
    AddAttr<std::string>("code_type",
                         "(string, default encode_center_size) "
                         "the code type used with the target box")
        .SetDefault("encode_center_size")
        .InEnum({"encode_center_size", "decode_center_size"});
    AddOutput(
        "OutputBox",
        "(Tensor, default Tensor<float>)"
        "(Tensor) The output of box_coder_op, a tensor with shape [N, M, 4] "
        "representing the result of N target boxes encoded/decoded with "
        "M Prior boxes and variances.");

    AddComment(R"DOC(
Bounding Box Coder Operator.
Encode/Decode the priorbox information with the target bounding box.
)DOC");
  }
};

}  // namespace operators
}  // namespace paddle

namespace ops = paddle::operators;
REGISTER_OP_WITHOUT_GRADIENT(box_coder, ops::BoxCoderOp, ops::BoxCoderOpMaker);
REGISTER_OP_CPU_KERNEL(box_coder, ops::BoxCoderKernel<float>,
                       ops::BoxCoderKernel<double>);
<|endoftext|>


<commit_before>//===- llvm/unittest/ADT/ValueMapTest.cpp - ValueMap unit tests -*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "llvm/ADT/ValueMap.h"
#include "llvm/ADT/OwningPtr.h"
#include "llvm/Config/llvm-config.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/LLVMContext.h"
#include "gtest/gtest.h"

using namespace llvm;

namespace {

// Test fixture
template<typename T>
class ValueMapTest : public testing::Test {
protected:
  Constant *ConstantV;
  OwningPtr<BitCastInst> BitcastV;
  OwningPtr<BinaryOperator> AddV;

  ValueMapTest() :
    ConstantV(ConstantInt::get(Type::getInt32Ty(getGlobalContext()), 0)),
    BitcastV(new BitCastInst(ConstantV, Type::getInt32Ty(getGlobalContext()))),
    AddV(BinaryOperator::CreateAdd(ConstantV, ConstantV)) {
  }
};

// Run everything on Value*, a subtype to make sure that casting works as
// expected, and a const subtype to make sure we cast const correctly.
typedef ::testing::Types<Value, Instruction, const Instruction> KeyTypes;
TYPED_TEST_CASE(ValueMapTest, KeyTypes);

TYPED_TEST(ValueMapTest, Null) {
  ValueMap<TypeParam*, int> VM1;
  VM1[NULL] = 7;
  EXPECT_EQ(7, VM1.lookup(NULL));
}

TYPED_TEST(ValueMapTest, FollowsValue) {
  ValueMap<TypeParam*, int> VM;
  VM[this->BitcastV.get()] = 7;
  EXPECT_EQ(7, VM.lookup(this->BitcastV.get()));
  EXPECT_EQ(0, VM.count(this->AddV.get()));
  this->BitcastV->replaceAllUsesWith(this->AddV.get());
  EXPECT_EQ(7, VM.lookup(this->AddV.get()));
  EXPECT_EQ(0, VM.count(this->BitcastV.get()));
  this->AddV.reset();
  EXPECT_EQ(0, VM.count(this->AddV.get()));
  EXPECT_EQ(0, VM.count(this->BitcastV.get()));
  EXPECT_EQ(0U, VM.size());
}

TYPED_TEST(ValueMapTest, OperationsWork) {
  ValueMap<TypeParam*, int> VM;
  ValueMap<TypeParam*, int> VM2(16);  (void)VM2;
  typename ValueMapConfig<TypeParam*>::ExtraData Data;
  ValueMap<TypeParam*, int> VM3(Data, 16);  (void)VM3;
  EXPECT_TRUE(VM.empty());

  VM[this->BitcastV.get()] = 7;

  // Find:
  typename ValueMap<TypeParam*, int>::iterator I =
    VM.find(this->BitcastV.get());
  ASSERT_TRUE(I != VM.end());
  EXPECT_EQ(this->BitcastV.get(), I->first);
  EXPECT_EQ(7, I->second);
  EXPECT_TRUE(VM.find(this->AddV.get()) == VM.end());

  // Const find:
  const ValueMap<TypeParam*, int> &CVM = VM;
  typename ValueMap<TypeParam*, int>::const_iterator CI =
    CVM.find(this->BitcastV.get());
  ASSERT_TRUE(CI != CVM.end());
  EXPECT_EQ(this->BitcastV.get(), CI->first);
  EXPECT_EQ(7, CI->second);
  EXPECT_TRUE(CVM.find(this->AddV.get()) == CVM.end());

  // Insert:
  std::pair<typename ValueMap<TypeParam*, int>::iterator, bool> InsertResult1 =
    VM.insert(std::make_pair(this->AddV.get(), 3));
  EXPECT_EQ(this->AddV.get(), InsertResult1.first->first);
  EXPECT_EQ(3, InsertResult1.first->second);
  EXPECT_TRUE(InsertResult1.second);
  EXPECT_EQ(true, VM.count(this->AddV.get()));
  std::pair<typename ValueMap<TypeParam*, int>::iterator, bool> InsertResult2 =
    VM.insert(std::make_pair(this->AddV.get(), 5));
  EXPECT_EQ(this->AddV.get(), InsertResult2.first->first);
  EXPECT_EQ(3, InsertResult2.first->second);
  EXPECT_FALSE(InsertResult2.second);

  // Erase:
  VM.erase(InsertResult2.first);
  EXPECT_EQ(0U, VM.count(this->AddV.get()));
  EXPECT_EQ(1U, VM.count(this->BitcastV.get()));
  VM.erase(this->BitcastV.get());
  EXPECT_EQ(0U, VM.count(this->BitcastV.get()));
  EXPECT_EQ(0U, VM.size());

  // Range insert:
  SmallVector<std::pair<Instruction*, int>, 2> Elems;
  Elems.push_back(std::make_pair(this->AddV.get(), 1));
  Elems.push_back(std::make_pair(this->BitcastV.get(), 2));
  VM.insert(Elems.begin(), Elems.end());
  EXPECT_EQ(1, VM.lookup(this->AddV.get()));
  EXPECT_EQ(2, VM.lookup(this->BitcastV.get()));
}

template<typename ExpectedType, typename VarType>
void CompileAssertHasType(VarType) {
  typedef char assert[is_same<ExpectedType, VarType>::value ? 1 : -1];
}

TYPED_TEST(ValueMapTest, Iteration) {
  ValueMap<TypeParam*, int> VM;
  VM[this->BitcastV.get()] = 2;
  VM[this->AddV.get()] = 3;
  size_t size = 0;
  for (typename ValueMap<TypeParam*, int>::iterator I = VM.begin(), E = VM.end();
       I != E; ++I) {
    ++size;
    std::pair<TypeParam*, int> value = *I; (void)value;
    CompileAssertHasType<TypeParam*>(I->first);
    if (I->second == 2) {
      EXPECT_EQ(this->BitcastV.get(), I->first);
      I->second = 5;
    } else if (I->second == 3) {
      EXPECT_EQ(this->AddV.get(), I->first);
      I->second = 6;
    } else {
      ADD_FAILURE() << "Iterated through an extra value.";
    }
  }
  EXPECT_EQ(2U, size);
  EXPECT_EQ(5, VM[this->BitcastV.get()]);
  EXPECT_EQ(6, VM[this->AddV.get()]);

  size = 0;
  // Cast to const ValueMap to avoid a bug in DenseMap's iterators.
  const ValueMap<TypeParam*, int>& CVM = VM;
  for (typename ValueMap<TypeParam*, int>::const_iterator I = CVM.begin(),
         E = CVM.end(); I != E; ++I) {
    ++size;
    std::pair<TypeParam*, int> value = *I;  (void)value;
    CompileAssertHasType<TypeParam*>(I->first);
    if (I->second == 5) {
      EXPECT_EQ(this->BitcastV.get(), I->first);
    } else if (I->second == 6) {
      EXPECT_EQ(this->AddV.get(), I->first);
    } else {
      ADD_FAILURE() << "Iterated through an extra value.";
    }
  }
  EXPECT_EQ(2U, size);
}

TYPED_TEST(ValueMapTest, DefaultCollisionBehavior) {
  // By default, we overwrite the old value with the replaced value.
  ValueMap<TypeParam*, int> VM;
  VM[this->BitcastV.get()] = 7;
  VM[this->AddV.get()] = 9;
  this->BitcastV->replaceAllUsesWith(this->AddV.get());
  EXPECT_EQ(0, VM.count(this->BitcastV.get()));
  EXPECT_EQ(9, VM.lookup(this->AddV.get()));
}

TYPED_TEST(ValueMapTest, ConfiguredCollisionBehavior) {
  // TODO: Implement this when someone needs it.
}

template<typename KeyT>
struct LockMutex : ValueMapConfig<KeyT> {
  struct ExtraData {
    sys::Mutex *M;
    bool *CalledRAUW;
    bool *CalledDeleted;
  };
  static void onRAUW(const ExtraData &Data, KeyT Old, KeyT New) {
    *Data.CalledRAUW = true;
    EXPECT_FALSE(Data.M->tryacquire()) << "Mutex should already be locked.";
  }
  static void onDelete(const ExtraData &Data, KeyT Old) {
    *Data.CalledDeleted = true;
    EXPECT_FALSE(Data.M->tryacquire()) << "Mutex should already be locked.";
  }
  static sys::Mutex *getMutex(const ExtraData &Data) { return Data.M; }
};
#if LLVM_ENABLE_THREADS
TYPED_TEST(ValueMapTest, LocksMutex) {
  sys::Mutex M(false);  // Not recursive.
  bool CalledRAUW = false, CalledDeleted = false;
  typename LockMutex<TypeParam*>::ExtraData Data =
    {&M, &CalledRAUW, &CalledDeleted};
  ValueMap<TypeParam*, int, LockMutex<TypeParam*> > VM(Data);
  VM[this->BitcastV.get()] = 7;
  this->BitcastV->replaceAllUsesWith(this->AddV.get());
  this->AddV.reset();
  EXPECT_TRUE(CalledRAUW);
  EXPECT_TRUE(CalledDeleted);
}
#endif

template<typename KeyT>
struct NoFollow : ValueMapConfig<KeyT> {
  enum { FollowRAUW = false };
};

TYPED_TEST(ValueMapTest, NoFollowRAUW) {
  ValueMap<TypeParam*, int, NoFollow<TypeParam*> > VM;
  VM[this->BitcastV.get()] = 7;
  EXPECT_EQ(7, VM.lookup(this->BitcastV.get()));
  EXPECT_EQ(0, VM.count(this->AddV.get()));
  this->BitcastV->replaceAllUsesWith(this->AddV.get());
  EXPECT_EQ(7, VM.lookup(this->BitcastV.get()));
  EXPECT_EQ(0, VM.lookup(this->AddV.get()));
  this->AddV.reset();
  EXPECT_EQ(7, VM.lookup(this->BitcastV.get()));
  EXPECT_EQ(0, VM.lookup(this->AddV.get()));
  this->BitcastV.reset();
  EXPECT_EQ(0, VM.lookup(this->BitcastV.get()));
  EXPECT_EQ(0, VM.lookup(this->AddV.get()));
  EXPECT_EQ(0U, VM.size());
}

template<typename KeyT>
struct CountOps : ValueMapConfig<KeyT> {
  struct ExtraData {
    int *Deletions;
    int *RAUWs;
  };

  static void onRAUW(const ExtraData &Data, KeyT Old, KeyT New) {
    ++*Data.RAUWs;
  }
  static void onDelete(const ExtraData &Data, KeyT Old) {
    ++*Data.Deletions;
  }
};

TYPED_TEST(ValueMapTest, CallsConfig) {
  int Deletions = 0, RAUWs = 0;
  typename CountOps<TypeParam*>::ExtraData Data = {&Deletions, &RAUWs};
  ValueMap<TypeParam*, int, CountOps<TypeParam*> > VM(Data);
  VM[this->BitcastV.get()] = 7;
  this->BitcastV->replaceAllUsesWith(this->AddV.get());
  EXPECT_EQ(0, Deletions);
  EXPECT_EQ(1, RAUWs);
  this->AddV.reset();
  EXPECT_EQ(1, Deletions);
  EXPECT_EQ(1, RAUWs);
  this->BitcastV.reset();
  EXPECT_EQ(1, Deletions);
  EXPECT_EQ(1, RAUWs);
}

template<typename KeyT>
struct ModifyingConfig : ValueMapConfig<KeyT> {
  // We'll put a pointer here back to the ValueMap this key is in, so
  // that we can modify it (and clobber *this) before the ValueMap
  // tries to do the same modification.  In previous versions of
  // ValueMap, that exploded.
  typedef ValueMap<KeyT, int, ModifyingConfig<KeyT> > **ExtraData;

  static void onRAUW(ExtraData Map, KeyT Old, KeyT New) {
    (*Map)->erase(Old);
  }
  static void onDelete(ExtraData Map, KeyT Old) {
    (*Map)->erase(Old);
  }
};
TYPED_TEST(ValueMapTest, SurvivesModificationByConfig) {
  ValueMap<TypeParam*, int, ModifyingConfig<TypeParam*> > *MapAddress;
  ValueMap<TypeParam*, int, ModifyingConfig<TypeParam*> > VM(&MapAddress);
  MapAddress = &VM;
  // Now the ModifyingConfig can modify the Map inside a callback.
  VM[this->BitcastV.get()] = 7;
  this->BitcastV->replaceAllUsesWith(this->AddV.get());
  EXPECT_FALSE(VM.count(this->BitcastV.get()));
  EXPECT_FALSE(VM.count(this->AddV.get()));
  VM[this->AddV.get()] = 7;
  this->AddV.reset();
  EXPECT_FALSE(VM.count(this->AddV.get()));
}

}
<commit_msg>Silence g++ 4.9 build issue in unit tests<commit_after>//===- llvm/unittest/ADT/ValueMapTest.cpp - ValueMap unit tests -*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "llvm/ADT/ValueMap.h"
#include "llvm/ADT/OwningPtr.h"
#include "llvm/Config/llvm-config.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/LLVMContext.h"
#include "gtest/gtest.h"

using namespace llvm;

namespace {

// Test fixture
template<typename T>
class ValueMapTest : public testing::Test {
protected:
  Constant *ConstantV;
  OwningPtr<BitCastInst> BitcastV;
  OwningPtr<BinaryOperator> AddV;

  ValueMapTest() :
    ConstantV(ConstantInt::get(Type::getInt32Ty(getGlobalContext()), 0)),
    BitcastV(new BitCastInst(ConstantV, Type::getInt32Ty(getGlobalContext()))),
    AddV(BinaryOperator::CreateAdd(ConstantV, ConstantV)) {
  }
};

// Run everything on Value*, a subtype to make sure that casting works as
// expected, and a const subtype to make sure we cast const correctly.
typedef ::testing::Types<Value, Instruction, const Instruction> KeyTypes;
TYPED_TEST_CASE(ValueMapTest, KeyTypes);

TYPED_TEST(ValueMapTest, Null) {
  ValueMap<TypeParam*, int> VM1;
  VM1[NULL] = 7;
  EXPECT_EQ(7, VM1.lookup(NULL));
}

TYPED_TEST(ValueMapTest, FollowsValue) {
  ValueMap<TypeParam*, int> VM;
  VM[this->BitcastV.get()] = 7;
  EXPECT_EQ(7, VM.lookup(this->BitcastV.get()));
  EXPECT_EQ(0, VM.count(this->AddV.get()));
  this->BitcastV->replaceAllUsesWith(this->AddV.get());
  EXPECT_EQ(7, VM.lookup(this->AddV.get()));
  EXPECT_EQ(0, VM.count(this->BitcastV.get()));
  this->AddV.reset();
  EXPECT_EQ(0, VM.count(this->AddV.get()));
  EXPECT_EQ(0, VM.count(this->BitcastV.get()));
  EXPECT_EQ(0U, VM.size());
}

TYPED_TEST(ValueMapTest, OperationsWork) {
  ValueMap<TypeParam*, int> VM;
  ValueMap<TypeParam*, int> VM2(16);  (void)VM2;
  typename ValueMapConfig<TypeParam*>::ExtraData Data;
  ValueMap<TypeParam*, int> VM3(Data, 16);  (void)VM3;
  EXPECT_TRUE(VM.empty());

  VM[this->BitcastV.get()] = 7;

  // Find:
  typename ValueMap<TypeParam*, int>::iterator I =
    VM.find(this->BitcastV.get());
  ASSERT_TRUE(I != VM.end());
  EXPECT_EQ(this->BitcastV.get(), I->first);
  EXPECT_EQ(7, I->second);
  EXPECT_TRUE(VM.find(this->AddV.get()) == VM.end());

  // Const find:
  const ValueMap<TypeParam*, int> &CVM = VM;
  typename ValueMap<TypeParam*, int>::const_iterator CI =
    CVM.find(this->BitcastV.get());
  ASSERT_TRUE(CI != CVM.end());
  EXPECT_EQ(this->BitcastV.get(), CI->first);
  EXPECT_EQ(7, CI->second);
  EXPECT_TRUE(CVM.find(this->AddV.get()) == CVM.end());

  // Insert:
  std::pair<typename ValueMap<TypeParam*, int>::iterator, bool> InsertResult1 =
    VM.insert(std::make_pair(this->AddV.get(), 3));
  EXPECT_EQ(this->AddV.get(), InsertResult1.first->first);
  EXPECT_EQ(3, InsertResult1.first->second);
  EXPECT_TRUE(InsertResult1.second);
  EXPECT_EQ(true, VM.count(this->AddV.get()));
  std::pair<typename ValueMap<TypeParam*, int>::iterator, bool> InsertResult2 =
    VM.insert(std::make_pair(this->AddV.get(), 5));
  EXPECT_EQ(this->AddV.get(), InsertResult2.first->first);
  EXPECT_EQ(3, InsertResult2.first->second);
  EXPECT_FALSE(InsertResult2.second);

  // Erase:
  VM.erase(InsertResult2.first);
  EXPECT_EQ(0U, VM.count(this->AddV.get()));
  EXPECT_EQ(1U, VM.count(this->BitcastV.get()));
  VM.erase(this->BitcastV.get());
  EXPECT_EQ(0U, VM.count(this->BitcastV.get()));
  EXPECT_EQ(0U, VM.size());

  // Range insert:
  SmallVector<std::pair<Instruction*, int>, 2> Elems;
  Elems.push_back(std::make_pair(this->AddV.get(), 1));
  Elems.push_back(std::make_pair(this->BitcastV.get(), 2));
  VM.insert(Elems.begin(), Elems.end());
  EXPECT_EQ(1, VM.lookup(this->AddV.get()));
  EXPECT_EQ(2, VM.lookup(this->BitcastV.get()));
}

template<typename ExpectedType, typename VarType>
void CompileAssertHasType(VarType) {
  LLVM_ATTRIBUTE_UNUSED typedef char
  assert[is_same<ExpectedType, VarType>::value ? 1 : -1];
}

TYPED_TEST(ValueMapTest, Iteration) {
  ValueMap<TypeParam*, int> VM;
  VM[this->BitcastV.get()] = 2;
  VM[this->AddV.get()] = 3;
  size_t size = 0;
  for (typename ValueMap<TypeParam*, int>::iterator I = VM.begin(), E = VM.end();
       I != E; ++I) {
    ++size;
    std::pair<TypeParam*, int> value = *I; (void)value;
    CompileAssertHasType<TypeParam*>(I->first);
    if (I->second == 2) {
      EXPECT_EQ(this->BitcastV.get(), I->first);
      I->second = 5;
    } else if (I->second == 3) {
      EXPECT_EQ(this->AddV.get(), I->first);
      I->second = 6;
    } else {
      ADD_FAILURE() << "Iterated through an extra value.";
    }
  }
  EXPECT_EQ(2U, size);
  EXPECT_EQ(5, VM[this->BitcastV.get()]);
  EXPECT_EQ(6, VM[this->AddV.get()]);

  size = 0;
  // Cast to const ValueMap to avoid a bug in DenseMap's iterators.
  const ValueMap<TypeParam*, int>& CVM = VM;
  for (typename ValueMap<TypeParam*, int>::const_iterator I = CVM.begin(),
         E = CVM.end(); I != E; ++I) {
    ++size;
    std::pair<TypeParam*, int> value = *I;  (void)value;
    CompileAssertHasType<TypeParam*>(I->first);
    if (I->second == 5) {
      EXPECT_EQ(this->BitcastV.get(), I->first);
    } else if (I->second == 6) {
      EXPECT_EQ(this->AddV.get(), I->first);
    } else {
      ADD_FAILURE() << "Iterated through an extra value.";
    }
  }
  EXPECT_EQ(2U, size);
}

TYPED_TEST(ValueMapTest, DefaultCollisionBehavior) {
  // By default, we overwrite the old value with the replaced value.
  ValueMap<TypeParam*, int> VM;
  VM[this->BitcastV.get()] = 7;
  VM[this->AddV.get()] = 9;
  this->BitcastV->replaceAllUsesWith(this->AddV.get());
  EXPECT_EQ(0, VM.count(this->BitcastV.get()));
  EXPECT_EQ(9, VM.lookup(this->AddV.get()));
}

TYPED_TEST(ValueMapTest, ConfiguredCollisionBehavior) {
  // TODO: Implement this when someone needs it.
}

template<typename KeyT>
struct LockMutex : ValueMapConfig<KeyT> {
  struct ExtraData {
    sys::Mutex *M;
    bool *CalledRAUW;
    bool *CalledDeleted;
  };
  static void onRAUW(const ExtraData &Data, KeyT Old, KeyT New) {
    *Data.CalledRAUW = true;
    EXPECT_FALSE(Data.M->tryacquire()) << "Mutex should already be locked.";
  }
  static void onDelete(const ExtraData &Data, KeyT Old) {
    *Data.CalledDeleted = true;
    EXPECT_FALSE(Data.M->tryacquire()) << "Mutex should already be locked.";
  }
  static sys::Mutex *getMutex(const ExtraData &Data) { return Data.M; }
};
#if LLVM_ENABLE_THREADS
TYPED_TEST(ValueMapTest, LocksMutex) {
  sys::Mutex M(false);  // Not recursive.
  bool CalledRAUW = false, CalledDeleted = false;
  typename LockMutex<TypeParam*>::ExtraData Data =
    {&M, &CalledRAUW, &CalledDeleted};
  ValueMap<TypeParam*, int, LockMutex<TypeParam*> > VM(Data);
  VM[this->BitcastV.get()] = 7;
  this->BitcastV->replaceAllUsesWith(this->AddV.get());
  this->AddV.reset();
  EXPECT_TRUE(CalledRAUW);
  EXPECT_TRUE(CalledDeleted);
}
#endif

template<typename KeyT>
struct NoFollow : ValueMapConfig<KeyT> {
  enum { FollowRAUW = false };
};

TYPED_TEST(ValueMapTest, NoFollowRAUW) {
  ValueMap<TypeParam*, int, NoFollow<TypeParam*> > VM;
  VM[this->BitcastV.get()] = 7;
  EXPECT_EQ(7, VM.lookup(this->BitcastV.get()));
  EXPECT_EQ(0, VM.count(this->AddV.get()));
  this->BitcastV->replaceAllUsesWith(this->AddV.get());
  EXPECT_EQ(7, VM.lookup(this->BitcastV.get()));
  EXPECT_EQ(0, VM.lookup(this->AddV.get()));
  this->AddV.reset();
  EXPECT_EQ(7, VM.lookup(this->BitcastV.get()));
  EXPECT_EQ(0, VM.lookup(this->AddV.get()));
  this->BitcastV.reset();
  EXPECT_EQ(0, VM.lookup(this->BitcastV.get()));
  EXPECT_EQ(0, VM.lookup(this->AddV.get()));
  EXPECT_EQ(0U, VM.size());
}

template<typename KeyT>
struct CountOps : ValueMapConfig<KeyT> {
  struct ExtraData {
    int *Deletions;
    int *RAUWs;
  };

  static void onRAUW(const ExtraData &Data, KeyT Old, KeyT New) {
    ++*Data.RAUWs;
  }
  static void onDelete(const ExtraData &Data, KeyT Old) {
    ++*Data.Deletions;
  }
};

TYPED_TEST(ValueMapTest, CallsConfig) {
  int Deletions = 0, RAUWs = 0;
  typename CountOps<TypeParam*>::ExtraData Data = {&Deletions, &RAUWs};
  ValueMap<TypeParam*, int, CountOps<TypeParam*> > VM(Data);
  VM[this->BitcastV.get()] = 7;
  this->BitcastV->replaceAllUsesWith(this->AddV.get());
  EXPECT_EQ(0, Deletions);
  EXPECT_EQ(1, RAUWs);
  this->AddV.reset();
  EXPECT_EQ(1, Deletions);
  EXPECT_EQ(1, RAUWs);
  this->BitcastV.reset();
  EXPECT_EQ(1, Deletions);
  EXPECT_EQ(1, RAUWs);
}

template<typename KeyT>
struct ModifyingConfig : ValueMapConfig<KeyT> {
  // We'll put a pointer here back to the ValueMap this key is in, so
  // that we can modify it (and clobber *this) before the ValueMap
  // tries to do the same modification.  In previous versions of
  // ValueMap, that exploded.
  typedef ValueMap<KeyT, int, ModifyingConfig<KeyT> > **ExtraData;

  static void onRAUW(ExtraData Map, KeyT Old, KeyT New) {
    (*Map)->erase(Old);
  }
  static void onDelete(ExtraData Map, KeyT Old) {
    (*Map)->erase(Old);
  }
};
TYPED_TEST(ValueMapTest, SurvivesModificationByConfig) {
  ValueMap<TypeParam*, int, ModifyingConfig<TypeParam*> > *MapAddress;
  ValueMap<TypeParam*, int, ModifyingConfig<TypeParam*> > VM(&MapAddress);
  MapAddress = &VM;
  // Now the ModifyingConfig can modify the Map inside a callback.
  VM[this->BitcastV.get()] = 7;
  this->BitcastV->replaceAllUsesWith(this->AddV.get());
  EXPECT_FALSE(VM.count(this->BitcastV.get()));
  EXPECT_FALSE(VM.count(this->AddV.get()));
  VM[this->AddV.get()] = 7;
  this->AddV.reset();
  EXPECT_FALSE(VM.count(this->AddV.get()));
}

}
<|endoftext|>


<commit_before>/*
 *  yosys -- Yosys Open SYnthesis Suite
 *
 *  Copyright (C) 2017 Robert Ou <rqou@robertou.com>
 *
 *  Permission to use, copy, modify, and/or distribute this software for any
 *  purpose with or without fee is hereby granted, provided that the above
 *  copyright notice and this permission notice appear in all copies.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 *  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 *  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 *  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 *  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 *  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 *  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 */

#include "kernel/yosys.h"
#include "kernel/sigtools.h"

USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN

struct ExtractReducePass : public Pass
{
	enum GateType {
		And,
		Or,
		Xor
	};

	ExtractReducePass() : Pass("extract_reduce", "converts gate chains into $reduce_* cells") { }

	virtual void help()
	{
		//   |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
		log("\n");
		log("    extract_reduce [selection]\n");
		log("\n");
		log("converts gate chains into $reduce_* cells\n");
		log("\n");
		log("This command finds chains of $_AND_, $_OR_, and $_XOR_ cells and replaces them\n");
		log("with their corresponding $reduce_* cells. Because this command only operates on\n");
		log("these cell types, it is recommended to map the design to only these cell types\n");
		log("using the `abc -g` command. Note that, in some cases, it may be more effective\n");
		log("to map the design to only $_AND_ cells, run extract_reduce, map the remaining\n");
		log("parts of the design to AND/OR/XOR cells, and run extract_reduce a second time.\n");
		log("\n");
	}

	virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
	{
		log_header(design, "Executing EXTRACT_REDUCE pass.\n");
		log_push();

		size_t argidx;
		for (argidx = 1; argidx < args.size(); argidx++)
		{
			// if (args[argidx] == "-v") {
			// 	verbose = true;
			// 	continue;
			// }
			break;
		}
		extra_args(args, argidx, design);

		for (auto module : design->selected_modules())
		{
			SigMap sigmap(module);

			// Index all of the nets in the module
			dict<SigBit, Cell*> sig_to_driver;
			dict<SigBit, pool<Cell*>> sig_to_sink;
			for (auto cell : module->selected_cells())
			{
				for (auto &conn : cell->connections())
				{
					if (cell->output(conn.first))
						for (auto bit : sigmap(conn.second))
							sig_to_driver[bit] = cell;

					if (cell->input(conn.first))
					{
						for (auto bit : sigmap(conn.second))
						{
							if (sig_to_sink.count(bit) == 0)
								sig_to_sink[bit] = pool<Cell*>();
							sig_to_sink[bit].insert(cell);
						}
					}
				}
			}

			// Need to check if any wires connect to module ports
			pool<SigBit> port_sigs;
			for (auto wire : module->selected_wires())
				if (wire->port_input || wire->port_output)
					for (auto bit : sigmap(wire))
						port_sigs.insert(bit);

			// Actual logic starts here
			pool<Cell*> consumed_cells;
			for (auto cell : module->selected_cells())
			{
				if (consumed_cells.count(cell))
					continue;

				GateType gt;

				if (cell->type == "$_AND_")
					gt = GateType::And;
				else if (cell->type == "$_OR_")
					gt = GateType::Or;
				else if (cell->type == "$_XOR_")
					gt = GateType::Xor;
				else
					continue;

				log("Working on cell %s...\n", cell->name.c_str());

				// Go all the way to the sink
				Cell* head_cell = cell;
				Cell* x = cell;
				while (true)
				{
					if (!((x->type == "$_AND_" && gt == GateType::And) ||
						(x->type == "$_OR_" && gt == GateType::Or) ||
						(x->type == "$_XOR_" && gt == GateType::Xor)))
						break;

					head_cell = x;

					auto y = sigmap(x->getPort("\\Y"));
					log_assert(y.size() == 1);

					// Should only continue if there is one fanout back into a cell (not to a port)
					if (sig_to_sink[y[0]].size() != 1)
						break;

					x = *sig_to_sink[y[0]].begin();
				}

				log("  Head cell is %s\n", head_cell->name.c_str());

				pool<Cell*> cur_supercell;
				std::deque<Cell*> bfs_queue = {head_cell};
				while (bfs_queue.size())
				{
					Cell* x = bfs_queue.front();
					bfs_queue.pop_front();

					cur_supercell.insert(x);

					auto a = sigmap(x->getPort("\\A"));
					log_assert(a.size() == 1);
					// Must have only one sink
					// XXX: Check that it is indeed this node?
					if (sig_to_sink[a[0]].size() + port_sigs.count(a[0]) == 1)
					{
						Cell* cell_a = sig_to_driver[a[0]];
						if (((cell_a->type == "$_AND_" && gt == GateType::And) ||
							(cell_a->type == "$_OR_" && gt == GateType::Or) ||
							(cell_a->type == "$_XOR_" && gt == GateType::Xor)))
						{
							// The cell here is the correct type, and it's definitely driving only
							// this current cell.
							bfs_queue.push_back(cell_a);
						}
					}

					auto b = sigmap(x->getPort("\\B"));
					log_assert(b.size() == 1);
					// Must have only one sink
					// XXX: Check that it is indeed this node?
					if (sig_to_sink[b[0]].size() + port_sigs.count(b[0]) == 1)
					{
						Cell* cell_b = sig_to_driver[b[0]];
						if (((cell_b->type == "$_AND_" && gt == GateType::And) ||
							(cell_b->type == "$_OR_" && gt == GateType::Or) ||
							(cell_b->type == "$_XOR_" && gt == GateType::Xor)))
						{
							// The cell here is the correct type, and it's definitely driving only
							// this current cell.
							bfs_queue.push_back(cell_b);
						}
					}
				}

				log("  Cells:\n");
				for (auto x : cur_supercell)
					log("    %s\n", x->name.c_str());

				if (cur_supercell.size() > 1)
				{
					// Worth it to create reduce cell
					log("  Creating $reduce_* cell!\n");

					pool<SigBit> input_pool;
					pool<SigBit> input_pool_intermed;
					for (auto x : cur_supercell)
					{
						input_pool.insert(sigmap(x->getPort("\\A"))[0]);
						input_pool.insert(sigmap(x->getPort("\\B"))[0]);
						input_pool_intermed.insert(sigmap(x->getPort("\\Y"))[0]);
					}
					SigSpec input;
					for (auto b : input_pool)
						if (input_pool_intermed.count(b) == 0)
							input.append_bit(b);

					SigBit output = sigmap(head_cell->getPort("\\Y")[0]);

					auto new_reduce_cell = module->addCell(NEW_ID,
						gt == GateType::And ? "$reduce_and" :
						gt == GateType::Or ? "$reduce_or" :
						gt == GateType::Xor ? "$reduce_xor" : "");
					new_reduce_cell->setParam("\\A_SIGNED", 0);
					new_reduce_cell->setParam("\\A_WIDTH", input.size());
					new_reduce_cell->setParam("\\Y_WIDTH", 1);
					new_reduce_cell->setPort("\\A", input);
					new_reduce_cell->setPort("\\Y", output);

					for (auto x : cur_supercell)
						consumed_cells.insert(x);
					}
				}

			// Remove every cell that we've used up
			for (auto cell : consumed_cells)
				module->remove(cell);
		}

		log_pop();
	}
} ExtractReducePass;

PRIVATE_NAMESPACE_END
<commit_msg>extract_reduce: Fix segfault on "undriven" inputs<commit_after>/*
 *  yosys -- Yosys Open SYnthesis Suite
 *
 *  Copyright (C) 2017 Robert Ou <rqou@robertou.com>
 *
 *  Permission to use, copy, modify, and/or distribute this software for any
 *  purpose with or without fee is hereby granted, provided that the above
 *  copyright notice and this permission notice appear in all copies.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 *  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 *  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 *  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 *  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 *  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 *  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 */

#include "kernel/yosys.h"
#include "kernel/sigtools.h"

USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN

struct ExtractReducePass : public Pass
{
	enum GateType {
		And,
		Or,
		Xor
	};

	ExtractReducePass() : Pass("extract_reduce", "converts gate chains into $reduce_* cells") { }

	virtual void help()
	{
		//   |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
		log("\n");
		log("    extract_reduce [selection]\n");
		log("\n");
		log("converts gate chains into $reduce_* cells\n");
		log("\n");
		log("This command finds chains of $_AND_, $_OR_, and $_XOR_ cells and replaces them\n");
		log("with their corresponding $reduce_* cells. Because this command only operates on\n");
		log("these cell types, it is recommended to map the design to only these cell types\n");
		log("using the `abc -g` command. Note that, in some cases, it may be more effective\n");
		log("to map the design to only $_AND_ cells, run extract_reduce, map the remaining\n");
		log("parts of the design to AND/OR/XOR cells, and run extract_reduce a second time.\n");
		log("\n");
	}

	virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
	{
		log_header(design, "Executing EXTRACT_REDUCE pass.\n");
		log_push();

		size_t argidx;
		for (argidx = 1; argidx < args.size(); argidx++)
		{
			// if (args[argidx] == "-v") {
			// 	verbose = true;
			// 	continue;
			// }
			break;
		}
		extra_args(args, argidx, design);

		for (auto module : design->selected_modules())
		{
			SigMap sigmap(module);

			// Index all of the nets in the module
			dict<SigBit, Cell*> sig_to_driver;
			dict<SigBit, pool<Cell*>> sig_to_sink;
			for (auto cell : module->selected_cells())
			{
				for (auto &conn : cell->connections())
				{
					if (cell->output(conn.first))
						for (auto bit : sigmap(conn.second))
							sig_to_driver[bit] = cell;

					if (cell->input(conn.first))
					{
						for (auto bit : sigmap(conn.second))
						{
							if (sig_to_sink.count(bit) == 0)
								sig_to_sink[bit] = pool<Cell*>();
							sig_to_sink[bit].insert(cell);
						}
					}
				}
			}

			// Need to check if any wires connect to module ports
			pool<SigBit> port_sigs;
			for (auto wire : module->selected_wires())
				if (wire->port_input || wire->port_output)
					for (auto bit : sigmap(wire))
						port_sigs.insert(bit);

			// Actual logic starts here
			pool<Cell*> consumed_cells;
			for (auto cell : module->selected_cells())
			{
				if (consumed_cells.count(cell))
					continue;

				GateType gt;

				if (cell->type == "$_AND_")
					gt = GateType::And;
				else if (cell->type == "$_OR_")
					gt = GateType::Or;
				else if (cell->type == "$_XOR_")
					gt = GateType::Xor;
				else
					continue;

				log("Working on cell %s...\n", cell->name.c_str());

				// Go all the way to the sink
				Cell* head_cell = cell;
				Cell* x = cell;
				while (true)
				{
					if (!((x->type == "$_AND_" && gt == GateType::And) ||
						(x->type == "$_OR_" && gt == GateType::Or) ||
						(x->type == "$_XOR_" && gt == GateType::Xor)))
						break;

					head_cell = x;

					auto y = sigmap(x->getPort("\\Y"));
					log_assert(y.size() == 1);

					// Should only continue if there is one fanout back into a cell (not to a port)
					if (sig_to_sink[y[0]].size() != 1)
						break;

					x = *sig_to_sink[y[0]].begin();
				}

				log("  Head cell is %s\n", head_cell->name.c_str());

				pool<Cell*> cur_supercell;
				std::deque<Cell*> bfs_queue = {head_cell};
				while (bfs_queue.size())
				{
					Cell* x = bfs_queue.front();
					bfs_queue.pop_front();

					cur_supercell.insert(x);

					auto a = sigmap(x->getPort("\\A"));
					log_assert(a.size() == 1);
					// Must have only one sink
					// XXX: Check that it is indeed this node?
					if (sig_to_sink[a[0]].size() + port_sigs.count(a[0]) == 1)
					{
						Cell* cell_a = sig_to_driver[a[0]];
						if (cell_a && ((cell_a->type == "$_AND_" && gt == GateType::And) ||
							(cell_a->type == "$_OR_" && gt == GateType::Or) ||
							(cell_a->type == "$_XOR_" && gt == GateType::Xor)))
						{
							// The cell here is the correct type, and it's definitely driving only
							// this current cell.
							bfs_queue.push_back(cell_a);
						}
					}

					auto b = sigmap(x->getPort("\\B"));
					log_assert(b.size() == 1);
					// Must have only one sink
					// XXX: Check that it is indeed this node?
					if (sig_to_sink[b[0]].size() + port_sigs.count(b[0]) == 1)
					{
						Cell* cell_b = sig_to_driver[b[0]];
						if (cell_b && ((cell_b->type == "$_AND_" && gt == GateType::And) ||
							(cell_b->type == "$_OR_" && gt == GateType::Or) ||
							(cell_b->type == "$_XOR_" && gt == GateType::Xor)))
						{
							// The cell here is the correct type, and it's definitely driving only
							// this current cell.
							bfs_queue.push_back(cell_b);
						}
					}
				}

				log("  Cells:\n");
				for (auto x : cur_supercell)
					log("    %s\n", x->name.c_str());

				if (cur_supercell.size() > 1)
				{
					// Worth it to create reduce cell
					log("  Creating $reduce_* cell!\n");

					pool<SigBit> input_pool;
					pool<SigBit> input_pool_intermed;
					for (auto x : cur_supercell)
					{
						input_pool.insert(sigmap(x->getPort("\\A"))[0]);
						input_pool.insert(sigmap(x->getPort("\\B"))[0]);
						input_pool_intermed.insert(sigmap(x->getPort("\\Y"))[0]);
					}
					SigSpec input;
					for (auto b : input_pool)
						if (input_pool_intermed.count(b) == 0)
							input.append_bit(b);

					SigBit output = sigmap(head_cell->getPort("\\Y")[0]);

					auto new_reduce_cell = module->addCell(NEW_ID,
						gt == GateType::And ? "$reduce_and" :
						gt == GateType::Or ? "$reduce_or" :
						gt == GateType::Xor ? "$reduce_xor" : "");
					new_reduce_cell->setParam("\\A_SIGNED", 0);
					new_reduce_cell->setParam("\\A_WIDTH", input.size());
					new_reduce_cell->setParam("\\Y_WIDTH", 1);
					new_reduce_cell->setPort("\\A", input);
					new_reduce_cell->setPort("\\Y", output);

					for (auto x : cur_supercell)
						consumed_cells.insert(x);
					}
				}

			// Remove every cell that we've used up
			for (auto cell : consumed_cells)
				module->remove(cell);
		}

		log_pop();
	}
} ExtractReducePass;

PRIVATE_NAMESPACE_END
<|endoftext|>


<commit_before>/*
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * Written (W) 2012 Fernando José Iglesias García
 * Copyright (C) 2012 Fernando José Iglesias García
 */

#include <shogun/lib/common.h>

#ifdef HAVE_EIGEN3

#include <shogun/multiclass/QDA.h>
#include <shogun/machine/NativeMulticlassMachine.h>
#include <shogun/features/Features.h>
#include <shogun/labels/Labels.h>
#include <shogun/labels/MulticlassLabels.h>
#include <shogun/mathematics/Math.h>

#include <shogun/mathematics/eigen3.h>

using namespace Eigen;

typedef Matrix< float64_t, Dynamic, Dynamic, ColMajor > EMatrix;
typedef Matrix< float64_t, Dynamic, 1, ColMajor > EVector;
typedef Array< float64_t, Dynamic, 1 > EArray;

using namespace shogun;

CQDA::CQDA(float64_t tolerance, bool store_covs)
: CNativeMulticlassMachine(), m_tolerance(tolerance),
	m_store_covs(store_covs), m_num_classes(0), m_dim(0)
{
	init();
}

CQDA::CQDA(CDenseFeatures<float64_t>* traindat, CLabels* trainlab, float64_t tolerance, bool store_covs)
: CNativeMulticlassMachine(), m_tolerance(tolerance), m_store_covs(store_covs), m_num_classes(0), m_dim(0)
{
	init();
	set_features(traindat);
	set_labels(trainlab);
}

CQDA::~CQDA()
{
	SG_UNREF(m_features);

	cleanup();
}

void CQDA::init()
{
	SG_ADD(&m_tolerance, "m_tolerance", "Tolerance member.", MS_AVAILABLE);
	SG_ADD(&m_store_covs, "m_store_covs", "Store covariances member", MS_NOT_AVAILABLE);
	SG_ADD((CSGObject**) &m_features, "m_features", "Feature object.", MS_NOT_AVAILABLE);
	SG_ADD(&m_means, "m_means", "Mean vectors list", MS_NOT_AVAILABLE);
	SG_ADD(&m_slog, "m_slog", "Vector used in classification", MS_NOT_AVAILABLE);

	//TODO include SGNDArray objects for serialization

	m_features  = NULL;
}

void CQDA::cleanup()
{
	m_means=SGMatrix<float64_t>();

	m_num_classes = 0;
}

CMulticlassLabels* CQDA::apply_multiclass(CFeatures* data)
{
	if (data)
	{
		if (!data->has_property(FP_DOT))
			SG_ERROR("Specified features are not of type CDotFeatures\n")

		set_features((CDotFeatures*) data);
	}

	if ( !m_features )
		return NULL;

	int32_t num_vecs = m_features->get_num_vectors();
	ASSERT(num_vecs > 0)
	ASSERT( m_dim == m_features->get_dim_feature_space() )

	CDenseFeatures< float64_t >* rf = (CDenseFeatures< float64_t >*) m_features;

	EMatrix X(num_vecs, m_dim);
	EMatrix A(num_vecs, m_dim);
	EVector norm2(num_vecs*m_num_classes);
	norm2.setZero();

	int32_t vlen;
	bool vfree;
	float64_t* vec;
	for (int k = 0; k < m_num_classes; k++)
	{
		// X = features - means
		for (int i = 0; i < num_vecs; i++)
		{
			vec = rf->get_feature_vector(i, vlen, vfree);
			ASSERT(vec)

			Eigen::Map< EVector > Evec(vec,vlen);
			Eigen::Map< EVector > Em_means_col(m_means.get_column_vector(k), m_dim);

			X.row(i) = Evec - Em_means_col;

			rf->free_feature_vector(vec, i, vfree);
		}

		Eigen::Map< EMatrix > Em_M(m_M.get_matrix(k), m_dim, m_dim);
		A = X*Em_M;

		for (int i = 0; i < num_vecs; i++)
			norm2(i + k*num_vecs) = A.row(i).array().square().sum();

#ifdef DEBUG_QDA
	SG_PRINT("\n>>> Displaying A ...\n")
	SGMatrix< float64_t >::display_matrix(A.data(), num_vecs, m_dim);
#endif
	}

	for (int i = 0; i < num_vecs; i++)
		for (int k = 0; k < m_num_classes; k++)
		{
			norm2[i + k*num_vecs] += m_slog[k];
			norm2[i + k*num_vecs] *= -0.5;
		}

	CMulticlassLabels* out = new CMulticlassLabels(num_vecs);

	for (int i = 0 ; i < num_vecs; i++)
		out->set_label(i, SGVector<float64_t>::arg_max(norm2.data()+i, num_vecs, m_num_classes));

#ifdef DEBUG_QDA
	SG_PRINT("\n>>> Displaying norm2 ...\n")
	SGMatrix< float64_t >::display_matrix(norm2.data(), num_vecs, m_num_classes);
	SG_PRINT("\n>>> Displaying out ...\n")
	SGVector< float64_t >::display_vector(out->get_labels().vector, num_vecs);
#endif

	return out;
}

bool CQDA::train_machine(CFeatures* data)
{
	if (!m_labels)
		SG_ERROR("No labels allocated in QDA training\n")

	if ( data )
	{
		if (!data->has_property(FP_DOT))
			SG_ERROR("Speficied features are not of type CDotFeatures\n")

		set_features((CDotFeatures*) data);
	}

	if (!m_features)
		SG_ERROR("No features allocated in QDA training\n")

	SGVector< int32_t > train_labels = ((CMulticlassLabels*) m_labels)->get_int_labels();

	if (!train_labels.vector)
		SG_ERROR("No train_labels allocated in QDA training\n")

	cleanup();

	m_num_classes = ((CMulticlassLabels*) m_labels)->get_num_classes();
	m_dim = m_features->get_dim_feature_space();
	int32_t num_vec  = m_features->get_num_vectors();

	if (num_vec != train_labels.vlen)
		SG_ERROR("Dimension mismatch between features and labels in QDA training")

	int32_t* class_idxs = SG_MALLOC(int32_t, num_vec*m_num_classes); // number of examples of each class
	int32_t* class_nums = SG_MALLOC(int32_t, m_num_classes);
	memset(class_nums, 0, m_num_classes*sizeof(int32_t));
	int32_t class_idx;

	for (int i = 0; i < train_labels.vlen; i++)
	{
		class_idx = train_labels.vector[i];

		if (class_idx < 0 || class_idx >= m_num_classes)
		{
			SG_ERROR("found label out of {0, 1, 2, ..., num_classes-1}...")
			return false;
		}
		else
		{
			class_idxs[ class_idx*num_vec + class_nums[class_idx]++ ] = i;
		}
	}

	for (int i = 0; i < m_num_classes; i++)
	{
		if (class_nums[i] <= 0)
		{
			SG_ERROR("What? One class with no elements\n")
			return false;
		}
	}

	if (m_store_covs)
	{
		// cov_dims will be free in m_covs.destroy_ndarray()
		index_t * cov_dims = SG_MALLOC(index_t, 3);
		cov_dims[0] = m_dim;
		cov_dims[1] = m_dim;
		cov_dims[2] = m_num_classes;
		m_covs = SGNDArray< float64_t >(cov_dims, 3);
	}

	m_means = SGMatrix< float64_t >(m_dim, m_num_classes, true);
	SGMatrix< float64_t > scalings  = SGMatrix< float64_t >(m_dim, m_num_classes);

	// rot_dims will be freed in rotations.destroy_ndarray()
	index_t* rot_dims = SG_MALLOC(index_t, 3);
	rot_dims[0] = m_dim;
	rot_dims[1] = m_dim;
	rot_dims[2] = m_num_classes;
	SGNDArray< float64_t > rotations = SGNDArray< float64_t >(rot_dims, 3);

	CDenseFeatures< float64_t >* rf = (CDenseFeatures< float64_t >*) m_features;

	m_means.zero();

	int32_t vlen;
	bool vfree;
	float64_t* vec;
	for (int k = 0; k < m_num_classes; k++)
	{
		EMatrix buffer(class_nums[k], m_dim);
		Eigen::Map< EVector > Em_means(m_means.get_column_vector(k), m_dim);
		for (int i = 0; i < class_nums[k]; i++)
		{
			vec = rf->get_feature_vector(class_idxs[k*num_vec + i], vlen, vfree);
			ASSERT(vec)

			Eigen::Map< EVector > Evec(vec, vlen);
			Em_means += Evec;
			buffer.row(i) = Evec;

			rf->free_feature_vector(vec, class_idxs[k*num_vec + i], vfree);
		}

		Em_means /= class_nums[k];

		for (int i = 0; i < class_nums[k]; i++)
			buffer.row(i) -= Em_means;

		// SVD
		float64_t * col = scalings.get_column_vector(k);
		float64_t * rot_mat = rotations.get_matrix(k);

		Eigen::JacobiSVD<EMatrix> eSvd;
		eSvd.compute(buffer,Eigen::ComputeFullV);
		memcpy(col, eSvd.singularValues().data(), m_dim*sizeof(float64_t));
		memcpy(rot_mat, eSvd.matrixV().data(), m_dim*m_dim*sizeof(float64_t));

		SGVector<float64_t>::vector_multiply(col, col, col, m_dim);
		SGVector<float64_t>::scale_vector(1.0/(class_nums[k]-1), col, m_dim);
		rotations.transpose_matrix(k);

		if (m_store_covs)
		{
			Eigen::Map< EMatrix > EM(SGVector<float64_t>::clone_vector(rot_mat, m_dim*m_dim), m_dim, m_dim);
			Eigen::Map< EArray > Escalings(scalings.get_column_vector(k), m_dim);
			for (int i = 0; i < m_dim; i++)
				EM.row(i) = ( (EM.row(i).array()) * Escalings ).matrix();

			Eigen::Map< EMatrix > Em_covs(m_covs.get_matrix(k), m_dim, m_dim);
			Eigen::Map< EMatrix > Erot_mat(rot_mat, m_dim, m_dim);

			Em_covs = EM*Erot_mat;
		}
	}

	/* Computation of terms required for classification */
	SGVector< float32_t > sinvsqrt(m_dim);

	// M_dims will be freed in m_M.destroy_ndarray()
	index_t* M_dims = SG_MALLOC(index_t, 3);
	M_dims[0] = m_dim;
	M_dims[1] = m_dim;
	M_dims[2] = m_num_classes;
	m_M = SGNDArray< float64_t >(M_dims, 3);

	m_slog = SGVector< float32_t >(m_num_classes);
	m_slog.zero();

	index_t idx = 0;
	for (int k = 0; k < m_num_classes; k++)
	{
		for (int j = 0; j < m_dim; j++)
		{
			sinvsqrt[j] = 1.0 / CMath::sqrt(scalings[k*m_dim + j]);
			m_slog[k]  += CMath::log(scalings[k*m_dim + j]);
		}

		for (int i = 0; i < m_dim; i++)
			for (int j = 0; j < m_dim; j++)
			{
				idx = k*m_dim*m_dim + i + j*m_dim;
				m_M[idx] = rotations[idx] * sinvsqrt[j];
			}
	}

#ifdef DEBUG_QDA
	SG_PRINT(">>> QDA machine trained with %d classes\n", m_num_classes)

	SG_PRINT("\n>>> Displaying means ...\n")
	SGMatrix< float64_t >::display_matrix(m_means.matrix, m_dim, m_num_classes);

	SG_PRINT("\n>>> Displaying scalings ...\n")
	SGMatrix< float64_t >::display_matrix(scalings.matrix, m_dim, m_num_classes);

	SG_PRINT("\n>>> Displaying rotations ... \n")
	for (int k = 0; k < m_num_classes; k++)
		SGMatrix< float64_t >::display_matrix(rotations.get_matrix(k), m_dim, m_dim);

	SG_PRINT("\n>>> Displaying sinvsqrt ... \n")
	sinvsqrt.display_vector();

	SG_PRINT("\n>>> Diplaying m_M matrices ... \n")
	for (int k = 0; k < m_num_classes; k++)
		SGMatrix< float64_t >::display_matrix(m_M.get_matrix(k), m_dim, m_dim);

	SG_PRINT("\n>>> Exit DEBUG_QDA\n")
#endif

	SG_FREE(class_idxs);
	SG_FREE(class_nums);
	return true;
}

#endif /* HAVE_EIGEN3 */
<commit_msg>fixed crash in QDA covar calculation<commit_after>/*
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * Written (W) 2012 Fernando José Iglesias García
 * Copyright (C) 2012 Fernando José Iglesias García
 */

#include <shogun/lib/common.h>

#ifdef HAVE_EIGEN3

#include <shogun/multiclass/QDA.h>
#include <shogun/machine/NativeMulticlassMachine.h>
#include <shogun/features/Features.h>
#include <shogun/labels/Labels.h>
#include <shogun/labels/MulticlassLabels.h>
#include <shogun/mathematics/Math.h>

#include <shogun/mathematics/eigen3.h>

using namespace Eigen;

typedef Matrix< float64_t, Dynamic, Dynamic, ColMajor > EMatrix;
typedef Matrix< float64_t, Dynamic, 1, ColMajor > EVector;
typedef Array< float64_t, Dynamic, 1 > EArray;

using namespace shogun;

CQDA::CQDA(float64_t tolerance, bool store_covs)
: CNativeMulticlassMachine(), m_tolerance(tolerance),
	m_store_covs(store_covs), m_num_classes(0), m_dim(0)
{
	init();
}

CQDA::CQDA(CDenseFeatures<float64_t>* traindat, CLabels* trainlab, float64_t tolerance, bool store_covs)
: CNativeMulticlassMachine(), m_tolerance(tolerance), m_store_covs(store_covs), m_num_classes(0), m_dim(0)
{
	init();
	set_features(traindat);
	set_labels(trainlab);
}

CQDA::~CQDA()
{
	SG_UNREF(m_features);

	cleanup();
}

void CQDA::init()
{
	SG_ADD(&m_tolerance, "m_tolerance", "Tolerance member.", MS_AVAILABLE);
	SG_ADD(&m_store_covs, "m_store_covs", "Store covariances member", MS_NOT_AVAILABLE);
	SG_ADD((CSGObject**) &m_features, "m_features", "Feature object.", MS_NOT_AVAILABLE);
	SG_ADD(&m_means, "m_means", "Mean vectors list", MS_NOT_AVAILABLE);
	SG_ADD(&m_slog, "m_slog", "Vector used in classification", MS_NOT_AVAILABLE);

	//TODO include SGNDArray objects for serialization

	m_features  = NULL;
}

void CQDA::cleanup()
{
	m_means=SGMatrix<float64_t>();

	m_num_classes = 0;
}

CMulticlassLabels* CQDA::apply_multiclass(CFeatures* data)
{
	if (data)
	{
		if (!data->has_property(FP_DOT))
			SG_ERROR("Specified features are not of type CDotFeatures\n")

		set_features((CDotFeatures*) data);
	}

	if ( !m_features )
		return NULL;

	int32_t num_vecs = m_features->get_num_vectors();
	ASSERT(num_vecs > 0)
	ASSERT( m_dim == m_features->get_dim_feature_space() )

	CDenseFeatures< float64_t >* rf = (CDenseFeatures< float64_t >*) m_features;

	EMatrix X(num_vecs, m_dim);
	EMatrix A(num_vecs, m_dim);
	EVector norm2(num_vecs*m_num_classes);
	norm2.setZero();

	int32_t vlen;
	bool vfree;
	float64_t* vec;
	for (int k = 0; k < m_num_classes; k++)
	{
		// X = features - means
		for (int i = 0; i < num_vecs; i++)
		{
			vec = rf->get_feature_vector(i, vlen, vfree);
			ASSERT(vec)

			Eigen::Map< EVector > Evec(vec,vlen);
			Eigen::Map< EVector > Em_means_col(m_means.get_column_vector(k), m_dim);

			X.row(i) = Evec - Em_means_col;

			rf->free_feature_vector(vec, i, vfree);
		}

		Eigen::Map< EMatrix > Em_M(m_M.get_matrix(k), m_dim, m_dim);
		A = X*Em_M;

		for (int i = 0; i < num_vecs; i++)
			norm2(i + k*num_vecs) = A.row(i).array().square().sum();

#ifdef DEBUG_QDA
	SG_PRINT("\n>>> Displaying A ...\n")
	SGMatrix< float64_t >::display_matrix(A.data(), num_vecs, m_dim);
#endif
	}

	for (int i = 0; i < num_vecs; i++)
		for (int k = 0; k < m_num_classes; k++)
		{
			norm2[i + k*num_vecs] += m_slog[k];
			norm2[i + k*num_vecs] *= -0.5;
		}

#ifdef DEBUG_QDA
	SG_PRINT("\n>>> Displaying norm2 ...\n")
	SGMatrix< float64_t >::display_matrix(norm2.data(), num_vecs, m_num_classes);
#endif

	CMulticlassLabels* out = new CMulticlassLabels(num_vecs);

	for (int i = 0 ; i < num_vecs; i++)
		out->set_label(i, SGVector<float64_t>::arg_max(norm2.data()+i, num_vecs, m_num_classes));

	return out;
}

bool CQDA::train_machine(CFeatures* data)
{
	if (!m_labels)
		SG_ERROR("No labels allocated in QDA training\n")

	if ( data )
	{
		if (!data->has_property(FP_DOT))
			SG_ERROR("Speficied features are not of type CDotFeatures\n")

		set_features((CDotFeatures*) data);
	}

	if (!m_features)
		SG_ERROR("No features allocated in QDA training\n")

	SGVector< int32_t > train_labels = ((CMulticlassLabels*) m_labels)->get_int_labels();

	if (!train_labels.vector)
		SG_ERROR("No train_labels allocated in QDA training\n")

	cleanup();

	m_num_classes = ((CMulticlassLabels*) m_labels)->get_num_classes();
	m_dim = m_features->get_dim_feature_space();
	int32_t num_vec  = m_features->get_num_vectors();

	if (num_vec != train_labels.vlen)
		SG_ERROR("Dimension mismatch between features and labels in QDA training")

	int32_t* class_idxs = SG_MALLOC(int32_t, num_vec*m_num_classes); // number of examples of each class
	int32_t* class_nums = SG_MALLOC(int32_t, m_num_classes);
	memset(class_nums, 0, m_num_classes*sizeof(int32_t));
	int32_t class_idx;

	for (int i = 0; i < train_labels.vlen; i++)
	{
		class_idx = train_labels.vector[i];

		if (class_idx < 0 || class_idx >= m_num_classes)
		{
			SG_ERROR("found label out of {0, 1, 2, ..., num_classes-1}...")
			return false;
		}
		else
		{
			class_idxs[ class_idx*num_vec + class_nums[class_idx]++ ] = i;
		}
	}

	for (int i = 0; i < m_num_classes; i++)
	{
		if (class_nums[i] <= 0)
		{
			SG_ERROR("What? One class with no elements\n")
			return false;
		}
	}

	if (m_store_covs)
	{
		// cov_dims will be free in m_covs.destroy_ndarray()
		index_t * cov_dims = SG_MALLOC(index_t, 3);
		cov_dims[0] = m_dim;
		cov_dims[1] = m_dim;
		cov_dims[2] = m_num_classes;
		m_covs = SGNDArray< float64_t >(cov_dims, 3);
	}

	m_means = SGMatrix< float64_t >(m_dim, m_num_classes, true);
	SGMatrix< float64_t > scalings  = SGMatrix< float64_t >(m_dim, m_num_classes);

	// rot_dims will be freed in rotations.destroy_ndarray()
	index_t* rot_dims = SG_MALLOC(index_t, 3);
	rot_dims[0] = m_dim;
	rot_dims[1] = m_dim;
	rot_dims[2] = m_num_classes;
	SGNDArray< float64_t > rotations = SGNDArray< float64_t >(rot_dims, 3);

	CDenseFeatures< float64_t >* rf = (CDenseFeatures< float64_t >*) m_features;

	m_means.zero();

	int32_t vlen;
	bool vfree;
	float64_t* vec;
	for (int k = 0; k < m_num_classes; k++)
	{
		EMatrix buffer(class_nums[k], m_dim);
		Eigen::Map< EVector > Em_means(m_means.get_column_vector(k), m_dim);
		for (int i = 0; i < class_nums[k]; i++)
		{
			vec = rf->get_feature_vector(class_idxs[k*num_vec + i], vlen, vfree);
			ASSERT(vec)

			Eigen::Map< EVector > Evec(vec, vlen);
			Em_means += Evec;
			buffer.row(i) = Evec;

			rf->free_feature_vector(vec, class_idxs[k*num_vec + i], vfree);
		}

		Em_means /= class_nums[k];

		for (int i = 0; i < class_nums[k]; i++)
			buffer.row(i) -= Em_means;

		// SVD
		float64_t * col = scalings.get_column_vector(k);
		float64_t * rot_mat = rotations.get_matrix(k);

		Eigen::JacobiSVD<EMatrix> eSvd;
		eSvd.compute(buffer,Eigen::ComputeFullV);
		memcpy(col, eSvd.singularValues().data(), m_dim*sizeof(float64_t));
		memcpy(rot_mat, eSvd.matrixV().data(), m_dim*m_dim*sizeof(float64_t));

		SGVector<float64_t>::vector_multiply(col, col, col, m_dim);
		SGVector<float64_t>::scale_vector(1.0/(class_nums[k]-1), col, m_dim);
		rotations.transpose_matrix(k);

		if (m_store_covs)
		{
			SGMatrix< float64_t > M(m_dim ,m_dim);
			EMatrix MEig = Map<EMatrix>(rot_mat,m_dim,m_dim);
			for (int i = 0; i < m_dim; i++)
				for (int j = 0; j < m_dim; j++)
					M(i,j)*=scalings[k*m_dim + j];
			EMatrix rotE = Map<EMatrix>(rot_mat,m_dim,m_dim);
			EMatrix resE(m_dim,m_dim);
			resE = MEig * rotE.transpose();
			memcpy(m_covs.get_matrix(k),resE.data(),m_dim*m_dim*sizeof(float64_t));
		}
	}

	/* Computation of terms required for classification */
	SGVector< float32_t > sinvsqrt(m_dim);

	// M_dims will be freed in m_M.destroy_ndarray()
	index_t* M_dims = SG_MALLOC(index_t, 3);
	M_dims[0] = m_dim;
	M_dims[1] = m_dim;
	M_dims[2] = m_num_classes;
	m_M = SGNDArray< float64_t >(M_dims, 3);

	m_slog = SGVector< float32_t >(m_num_classes);
	m_slog.zero();

	index_t idx = 0;
	for (int k = 0; k < m_num_classes; k++)
	{
		for (int j = 0; j < m_dim; j++)
		{
			sinvsqrt[j] = 1.0 / CMath::sqrt(scalings[k*m_dim + j]);
			m_slog[k]  += CMath::log(scalings[k*m_dim + j]);
		}

		for (int i = 0; i < m_dim; i++)
			for (int j = 0; j < m_dim; j++)
			{
				idx = k*m_dim*m_dim + i + j*m_dim;
				m_M[idx] = rotations[idx] * sinvsqrt[j];
			}
	}

#ifdef DEBUG_QDA
	SG_PRINT(">>> QDA machine trained with %d classes\n", m_num_classes)

	SG_PRINT("\n>>> Displaying means ...\n")
	SGMatrix< float64_t >::display_matrix(m_means.matrix, m_dim, m_num_classes);

	SG_PRINT("\n>>> Displaying scalings ...\n")
	SGMatrix< float64_t >::display_matrix(scalings.matrix, m_dim, m_num_classes);

	SG_PRINT("\n>>> Displaying rotations ... \n")
	for (int k = 0; k < m_num_classes; k++)
		SGMatrix< float64_t >::display_matrix(rotations.get_matrix(k), m_dim, m_dim);

	SG_PRINT("\n>>> Displaying sinvsqrt ... \n")
	sinvsqrt.display_vector();

	SG_PRINT("\n>>> Diplaying m_M matrices ... \n")
	for (int k = 0; k < m_num_classes; k++)
		SGMatrix< float64_t >::display_matrix(m_M.get_matrix(k), m_dim, m_dim);

	SG_PRINT("\n>>> Exit DEBUG_QDA\n")
#endif

	SG_FREE(class_idxs);
	SG_FREE(class_nums);
	return true;
}

#endif /* HAVE_EIGEN3 */
<|endoftext|>


<commit_before>/*
 * Ubitrack - Library for Ubiquitous Tracking
 * Copyright 2006, Technische Universitaet Muenchen, and individual
 * contributors as indicated by the @authors tag. See the
 * copyright.txt in the distribution for a full listing of individual
 * contributors.
 *
 * This 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 software 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 software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */

/**
 * @ingroup vision
 * @file
 * A class for encapsulating the image undistortion logic
 *
 * @author Daniel Pustka <daniel.pustka@in.tum.de>
 * @author Christian Waechter <christian.waechter@in.tum.de> (refactoring)
 */

#include "Undistortion.h"

// OpenCV
#include <opencv/cv.h>

// Ubitrack
#include "Image.h"
#include <utUtil/CalibFile.h>
#include <utUtil/Exception.h>
#include "Util/OpenCV.h"	// type conversion Ubitrack <-> OpenCV

// get a logger
#include <log4cpp/Category.hh>
static log4cpp::Category& logger( log4cpp::Category::getInstance( "Ubitrack.Vision.Undistortion" ) );

namespace {

	/// scales the intrinsic camera matrix parameters to used image resolution
	template< typename PrecisionType >
	void inline correctForScale( const Ubitrack::Vision::Image& image, Ubitrack::Math::CameraIntrinsics< PrecisionType >& intrinsics )
	{
		// scale the intrinsic matrix up or down if image size is different ( does not work if image is cropped instead of scaled)
		const PrecisionType scaleX = image.width / static_cast< PrecisionType > ( intrinsics.dimension( 0 ) );
		intrinsics.matrix ( 0, 0 ) *= scaleX;
		intrinsics.matrix ( 0, 2 ) *= scaleX;
		intrinsics.dimension ( 0 ) *= scaleX;

		const PrecisionType scaleY = image.height / static_cast< PrecisionType > ( intrinsics.dimension( 1 ) );
		intrinsics.matrix ( 1, 1 ) *= scaleY;
		intrinsics.matrix ( 1, 2 ) *= scaleY;
		intrinsics.dimension ( 1 ) *= scaleY;

		intrinsics.reset(); // recalculate the inverse
	}

	/// corrects the Ubitrack intrinsics matrix to the corresponding (left-handed) OpenCV camera matrix
	template< typename PrecisionType >
	void inline correctForOpenCV( Ubitrack::Math::CameraIntrinsics< PrecisionType >& intrinsics )
	{
		// compensate for left-handed OpenCV coordinate frame
		intrinsics.matrix ( 0, 2 ) *= -1;
		intrinsics.matrix ( 1, 2 ) *= -1;
		intrinsics.matrix ( 2, 2 ) *= -1;

		intrinsics.reset(); // recalculate the inverse
	}

	/// corrects the Ubitrack intrinsics parameters if the image is flipped upside-down
	template< typename PrecisionType >
	void inline correctForOrigin( const Ubitrack::Vision::Image& image, Ubitrack::Math::CameraIntrinsics< PrecisionType >& intrinsics )
	{
		if ( image.origin )
			return;

		{	// compensate if origin==0
			intrinsics.matrix( 1, 2 ) = image.height - 1 - intrinsics.matrix( 1, 2 );
			intrinsics.tangential_params( 1 ) *= -1.0;

			intrinsics.reset(); // recalculate the inverse
		}
	}

}	// anonymous namespace

namespace Ubitrack { namespace Vision {

Undistortion::Undistortion(){};

Undistortion::Undistortion( const std::string& intrinsicMatrixFile, const std::string& distortionFile )
{
	reset( intrinsicMatrixFile, distortionFile );
}

Undistortion::Undistortion( const std::string& cameraIntrinsicsFile )
{
	reset( cameraIntrinsicsFile );
}

Undistortion::Undistortion( const intrinsics_type& intrinsics )
{
	reset( intrinsics );
}

void Undistortion::reset( const std::string& CameraIntrinsicsFile )
{
	Measurement::CameraIntrinsics measMat;
	measMat.reset( new intrinsics_type() );
	Ubitrack::Util::readCalibFile( CameraIntrinsicsFile, measMat );
	reset( *measMat );
}

void Undistortion::reset( const intrinsics_type& camIntrinsics )
{
	m_intrinsics = camIntrinsics;
	m_intrinsicMatrix = m_intrinsics.matrix;

	m_coeffs( 0 ) = m_intrinsics.radial_params ( 0 );
	m_coeffs( 1 ) = m_intrinsics.radial_params ( 1 );
	m_coeffs( 2 ) = m_intrinsics.tangential_params ( 0 );
	m_coeffs( 3 ) = m_intrinsics.tangential_params ( 1 );

	for( std::size_t i = 4; i < (m_intrinsics.radial_size+2); ++i )
		m_coeffs( i ) = m_intrinsics.radial_params( i-2 );

}

void Undistortion::reset( const std::string& intrinsicMatrixFile, const std::string& distortionFile )
{
	// read intrinsics
	if ( !intrinsicMatrixFile.empty() )
	{

		Measurement::Matrix3x3 measMat;
		measMat.reset( new Ubitrack::Math::Matrix< double, 3, 3 >() );
		Ubitrack::Util::readCalibFile( intrinsicMatrixFile, measMat );
		m_intrinsicMatrix = *measMat;
		LOG4CPP_DEBUG(logger, "Loaded calibration file : " << m_intrinsicMatrix);
	}
	else
	{
		m_intrinsicMatrix( 0, 0 ) = 400;
		m_intrinsicMatrix( 0, 1 ) = 0;
		m_intrinsicMatrix( 0, 2 ) = -160;
		m_intrinsicMatrix( 1, 0 ) = 0;
		m_intrinsicMatrix( 1, 1 ) = 400;
		m_intrinsicMatrix( 1, 2 ) = -120;
		m_intrinsicMatrix( 2, 0 ) = 0;
		m_intrinsicMatrix( 2, 1 ) = 0;
		m_intrinsicMatrix( 2, 2 ) = -1;
	}

	// initialize to zero
	m_coeffs = Ubitrack::Math::Vector< double, 8 >::zeros();
	// read coefficients

	if ( !distortionFile.empty() )
	{
        // first try new, eight element distortion file
		Measurement::Vector8D measVec;
		measVec.reset( new Ubitrack::Math::Vector< double, 8 >() );
        try {
		    Ubitrack::Util::readCalibFile( distortionFile, measVec );
            m_coeffs = *measVec;
        }
		catch ( Ubitrack::Util::Exception )
		{
            LOG4CPP_ERROR( logger, "Cannot read new image distortion model. Trying old format." );
            // try old format, this time without exception handling
            Measurement::Vector4D measVec4D;
            measVec4D.reset( new Ubitrack::Math::Vector< double, 4 >() );
            Ubitrack::Util::readCalibFile( distortionFile, measVec4D );
            m_coeffs = Ubitrack::Math::Vector< double, 8 >::zeros();
            boost::numeric::ublas::subrange( m_coeffs, 0, 4 ) = *measVec4D;
        }
	}

	{	// set the intrinsics format from the old format
		intrinsics_type::radial_type radVec;
		radVec( 0 ) = m_coeffs( 0 );
		radVec( 1 ) = m_coeffs( 1 );
		radVec( 2 ) = m_coeffs( 4 );
		radVec( 3 ) = m_coeffs( 5 );
		radVec( 4 ) = m_coeffs( 6 );
		radVec( 5 ) = m_coeffs( 7 );
		intrinsics_type::tangential_type tanVec = intrinsics_type::tangential_type( m_coeffs( 2 ), m_coeffs( 3 ) );

		m_intrinsics = intrinsics_type( m_intrinsicMatrix, radVec, tanVec );
	}
}

/// resets the mapping to the provided image parameters
bool Undistortion::resetMapping( const int width, const int height, const intrinsics_type& intrinsics )
{
	LOG4CPP_INFO( logger, "initialize undistortion mapping with intrinsics:\n" << intrinsics );

	// reset the map images
	m_pMapX.reset( new Image( width, height, 1, IPL_DEPTH_32F ) );
	m_pMapY.reset( new Image( width, height, 1, IPL_DEPTH_32F ) );

	// copy the values to the corresponding opencv data-structures
	// CvMat cvIntrinsics;
	// CvMat cvCoeffs;
	CvMat* cvCoeffs =  cvCreateMat( 1, intrinsics.radial_size + 2, CV_32FC1 );
	CvMat* cvIntrinsics = cvCreateMat( 3, 3, CV_32FC1 );

	Util::cv1::assign( intrinsics, *cvCoeffs, *cvIntrinsics );

	// set values to the mapping
	cvInitUndistortMap( cvIntrinsics, cvCoeffs, *m_pMapX, *m_pMapY );

	// explicitly release the allocated memory
	cvReleaseMat( &cvCoeffs );
	cvReleaseMat( &cvIntrinsics );

	// alternative undistortion using special fisheye calibration 8 camera should be really fishy, seems buggy in 2.4.11 (CW@2015-03-24)
	//cv::fisheye::initUndistortRectifyMap( cv::Mat( pCvIntrinsics ), cv::Mat( pCvCoeffs ), cv::Mat::eye( 3, 3, CV_32F ), cv::Mat::eye( 3, 3, CV_32F ), cv::Size( width, height ), CV_32FC1, cv::Mat( *m_pMapX ), cv::Mat( *m_pMapY ) );
	LOG4CPP_INFO( logger, "Initialization of distortion maps finished." );

	return true;
}

bool Undistortion::resetMapping( const Vision::Image& image )
{
	// generate a local copy first
	intrinsics_type camIntrinsics = m_intrinsics;

	// image size different than intrinsics ? -> scale
	correctForScale( image, camIntrinsics );
	// always right-hand -> left-hand
	correctForOpenCV( camIntrinsics );
	// upside down? -> flip intrinsics and tangential param
	correctForOrigin( image, camIntrinsics );
	// set new lookup maps
	if( !resetMapping( image.width, image.height, camIntrinsics ) )
		return false;

	return isValid( image );
}

Vision::Image::Ptr Undistortion::undistort( Vision::Image::Ptr pImage )
{
	return undistort( *pImage );
}

Vision::Image::Ptr Undistortion::undistort( const Image& image )
{
	// check if old values are still valid for the image size, only reset mapping if not
	if( !isValid( image ) )
		if( !resetMapping( image ) )
			return image.Clone();

	// undistort
	Vision::Image::Ptr pImgUndistorted( new Image( image.width, image.height, image.nChannels, image.depth ) );
	pImgUndistorted->origin = image.origin;
	cvRemap( image, *pImgUndistorted, *m_pMapX, *m_pMapY );

	return pImgUndistorted;
}

bool Undistortion::isValid( const Vision::Image& image ) const
{
	if ( !m_pMapX || !m_pMapY )
		return false;

	if( m_pMapX->width != image.width || m_pMapX->height != image.height )
		return false;

	if( m_pMapY->width != image.width || m_pMapY->height != image.height )
		return false;

	return true;
}

} } // namespace Ubitrack::Vision
<commit_msg>fix to undistortion to set channelSeq fields, which is required to transmit RGB images through the dataflow graph<commit_after>/*
 * Ubitrack - Library for Ubiquitous Tracking
 * Copyright 2006, Technische Universitaet Muenchen, and individual
 * contributors as indicated by the @authors tag. See the
 * copyright.txt in the distribution for a full listing of individual
 * contributors.
 *
 * This 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 software 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 software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */

/**
 * @ingroup vision
 * @file
 * A class for encapsulating the image undistortion logic
 *
 * @author Daniel Pustka <daniel.pustka@in.tum.de>
 * @author Christian Waechter <christian.waechter@in.tum.de> (refactoring)
 */

#include "Undistortion.h"

// OpenCV
#include <opencv/cv.h>

// Ubitrack
#include "Image.h"
#include <utUtil/CalibFile.h>
#include <utUtil/Exception.h>
#include "Util/OpenCV.h"	// type conversion Ubitrack <-> OpenCV

// get a logger
#include <log4cpp/Category.hh>
static log4cpp::Category& logger( log4cpp::Category::getInstance( "Ubitrack.Vision.Undistortion" ) );

namespace {

	/// scales the intrinsic camera matrix parameters to used image resolution
	template< typename PrecisionType >
	void inline correctForScale( const Ubitrack::Vision::Image& image, Ubitrack::Math::CameraIntrinsics< PrecisionType >& intrinsics )
	{
		// scale the intrinsic matrix up or down if image size is different ( does not work if image is cropped instead of scaled)
		const PrecisionType scaleX = image.width / static_cast< PrecisionType > ( intrinsics.dimension( 0 ) );
		intrinsics.matrix ( 0, 0 ) *= scaleX;
		intrinsics.matrix ( 0, 2 ) *= scaleX;
		intrinsics.dimension ( 0 ) *= scaleX;

		const PrecisionType scaleY = image.height / static_cast< PrecisionType > ( intrinsics.dimension( 1 ) );
		intrinsics.matrix ( 1, 1 ) *= scaleY;
		intrinsics.matrix ( 1, 2 ) *= scaleY;
		intrinsics.dimension ( 1 ) *= scaleY;

		intrinsics.reset(); // recalculate the inverse
	}

	/// corrects the Ubitrack intrinsics matrix to the corresponding (left-handed) OpenCV camera matrix
	template< typename PrecisionType >
	void inline correctForOpenCV( Ubitrack::Math::CameraIntrinsics< PrecisionType >& intrinsics )
	{
		// compensate for left-handed OpenCV coordinate frame
		intrinsics.matrix ( 0, 2 ) *= -1;
		intrinsics.matrix ( 1, 2 ) *= -1;
		intrinsics.matrix ( 2, 2 ) *= -1;

		intrinsics.reset(); // recalculate the inverse
	}

	/// corrects the Ubitrack intrinsics parameters if the image is flipped upside-down
	template< typename PrecisionType >
	void inline correctForOrigin( const Ubitrack::Vision::Image& image, Ubitrack::Math::CameraIntrinsics< PrecisionType >& intrinsics )
	{
		if ( image.origin )
			return;

		{	// compensate if origin==0
			intrinsics.matrix( 1, 2 ) = image.height - 1 - intrinsics.matrix( 1, 2 );
			intrinsics.tangential_params( 1 ) *= -1.0;

			intrinsics.reset(); // recalculate the inverse
		}
	}

}	// anonymous namespace

namespace Ubitrack { namespace Vision {

Undistortion::Undistortion(){};

Undistortion::Undistortion( const std::string& intrinsicMatrixFile, const std::string& distortionFile )
{
	reset( intrinsicMatrixFile, distortionFile );
}

Undistortion::Undistortion( const std::string& cameraIntrinsicsFile )
{
	reset( cameraIntrinsicsFile );
}

Undistortion::Undistortion( const intrinsics_type& intrinsics )
{
	reset( intrinsics );
}

void Undistortion::reset( const std::string& CameraIntrinsicsFile )
{
	Measurement::CameraIntrinsics measMat;
	measMat.reset( new intrinsics_type() );
	Ubitrack::Util::readCalibFile( CameraIntrinsicsFile, measMat );
	reset( *measMat );
}

void Undistortion::reset( const intrinsics_type& camIntrinsics )
{
	m_intrinsics = camIntrinsics;
	m_intrinsicMatrix = m_intrinsics.matrix;

	m_coeffs( 0 ) = m_intrinsics.radial_params ( 0 );
	m_coeffs( 1 ) = m_intrinsics.radial_params ( 1 );
	m_coeffs( 2 ) = m_intrinsics.tangential_params ( 0 );
	m_coeffs( 3 ) = m_intrinsics.tangential_params ( 1 );

	for( std::size_t i = 4; i < (m_intrinsics.radial_size+2); ++i )
		m_coeffs( i ) = m_intrinsics.radial_params( i-2 );

}

void Undistortion::reset( const std::string& intrinsicMatrixFile, const std::string& distortionFile )
{
	// read intrinsics
	if ( !intrinsicMatrixFile.empty() )
	{

		Measurement::Matrix3x3 measMat;
		measMat.reset( new Ubitrack::Math::Matrix< double, 3, 3 >() );
		Ubitrack::Util::readCalibFile( intrinsicMatrixFile, measMat );
		m_intrinsicMatrix = *measMat;
		LOG4CPP_DEBUG(logger, "Loaded calibration file : " << m_intrinsicMatrix);
	}
	else
	{
		m_intrinsicMatrix( 0, 0 ) = 400;
		m_intrinsicMatrix( 0, 1 ) = 0;
		m_intrinsicMatrix( 0, 2 ) = -160;
		m_intrinsicMatrix( 1, 0 ) = 0;
		m_intrinsicMatrix( 1, 1 ) = 400;
		m_intrinsicMatrix( 1, 2 ) = -120;
		m_intrinsicMatrix( 2, 0 ) = 0;
		m_intrinsicMatrix( 2, 1 ) = 0;
		m_intrinsicMatrix( 2, 2 ) = -1;
	}

	// initialize to zero
	m_coeffs = Ubitrack::Math::Vector< double, 8 >::zeros();
	// read coefficients

	if ( !distortionFile.empty() )
	{
        // first try new, eight element distortion file
		Measurement::Vector8D measVec;
		measVec.reset( new Ubitrack::Math::Vector< double, 8 >() );
        try {
		    Ubitrack::Util::readCalibFile( distortionFile, measVec );
            m_coeffs = *measVec;
        }
		catch ( Ubitrack::Util::Exception )
		{
            LOG4CPP_ERROR( logger, "Cannot read new image distortion model. Trying old format." );
            // try old format, this time without exception handling
            Measurement::Vector4D measVec4D;
            measVec4D.reset( new Ubitrack::Math::Vector< double, 4 >() );
            Ubitrack::Util::readCalibFile( distortionFile, measVec4D );
            m_coeffs = Ubitrack::Math::Vector< double, 8 >::zeros();
            boost::numeric::ublas::subrange( m_coeffs, 0, 4 ) = *measVec4D;
        }
	}

	{	// set the intrinsics format from the old format
		intrinsics_type::radial_type radVec;
		radVec( 0 ) = m_coeffs( 0 );
		radVec( 1 ) = m_coeffs( 1 );
		radVec( 2 ) = m_coeffs( 4 );
		radVec( 3 ) = m_coeffs( 5 );
		radVec( 4 ) = m_coeffs( 6 );
		radVec( 5 ) = m_coeffs( 7 );
		intrinsics_type::tangential_type tanVec = intrinsics_type::tangential_type( m_coeffs( 2 ), m_coeffs( 3 ) );

		m_intrinsics = intrinsics_type( m_intrinsicMatrix, radVec, tanVec );
	}
}

/// resets the mapping to the provided image parameters
bool Undistortion::resetMapping( const int width, const int height, const intrinsics_type& intrinsics )
{
	LOG4CPP_INFO( logger, "initialize undistortion mapping with intrinsics:\n" << intrinsics );

	// reset the map images
	m_pMapX.reset( new Image( width, height, 1, IPL_DEPTH_32F ) );
	m_pMapY.reset( new Image( width, height, 1, IPL_DEPTH_32F ) );

	// copy the values to the corresponding opencv data-structures
	// CvMat cvIntrinsics;
	// CvMat cvCoeffs;
	CvMat* cvCoeffs =  cvCreateMat( 1, intrinsics.radial_size + 2, CV_32FC1 );
	CvMat* cvIntrinsics = cvCreateMat( 3, 3, CV_32FC1 );

	Util::cv1::assign( intrinsics, *cvCoeffs, *cvIntrinsics );

	// set values to the mapping
	cvInitUndistortMap( cvIntrinsics, cvCoeffs, *m_pMapX, *m_pMapY );

	// explicitly release the allocated memory
	cvReleaseMat( &cvCoeffs );
	cvReleaseMat( &cvIntrinsics );

	// alternative undistortion using special fisheye calibration 8 camera should be really fishy, seems buggy in 2.4.11 (CW@2015-03-24)
	//cv::fisheye::initUndistortRectifyMap( cv::Mat( pCvIntrinsics ), cv::Mat( pCvCoeffs ), cv::Mat::eye( 3, 3, CV_32F ), cv::Mat::eye( 3, 3, CV_32F ), cv::Size( width, height ), CV_32FC1, cv::Mat( *m_pMapX ), cv::Mat( *m_pMapY ) );
	LOG4CPP_INFO( logger, "Initialization of distortion maps finished." );

	return true;
}

bool Undistortion::resetMapping( const Vision::Image& image )
{
	// generate a local copy first
	intrinsics_type camIntrinsics = m_intrinsics;

	// image size different than intrinsics ? -> scale
	correctForScale( image, camIntrinsics );
	// always right-hand -> left-hand
	correctForOpenCV( camIntrinsics );
	// upside down? -> flip intrinsics and tangential param
	correctForOrigin( image, camIntrinsics );
	// set new lookup maps
	if( !resetMapping( image.width, image.height, camIntrinsics ) )
		return false;

	return isValid( image );
}

Vision::Image::Ptr Undistortion::undistort( Vision::Image::Ptr pImage )
{
	return undistort( *pImage );
}

Vision::Image::Ptr Undistortion::undistort( const Image& image )
{
	// check if old values are still valid for the image size, only reset mapping if not
	if( !isValid( image ) )
		if( !resetMapping( image ) )
			return image.Clone();

	// undistort
	Vision::Image::Ptr pImgUndistorted( new Image( image.width, image.height, image.nChannels, image.depth ) );
	pImgUndistorted->origin = image.origin;
	cvRemap( image, *pImgUndistorted, *m_pMapX, *m_pMapY );

	pImgUndistorted->channelSeq[0] = image.channelSeq[0];
	pImgUndistorted->channelSeq[1] = image.channelSeq[1];
	pImgUndistorted->channelSeq[2] = image.channelSeq[2];

	return pImgUndistorted;
}

bool Undistortion::isValid( const Vision::Image& image ) const
{
	if ( !m_pMapX || !m_pMapY )
		return false;

	if( m_pMapX->width != image.width || m_pMapX->height != image.height )
		return false;

	if( m_pMapY->width != image.width || m_pMapY->height != image.height )
		return false;

	return true;
}

} } // namespace Ubitrack::Vision
<|endoftext|>


<commit_before>/**
 * @file system.hpp
 * Exact diagonaliser base classes.
 * @author Burkhard Ritter
 * @version
 * @date 2010-12-10
 */

#ifndef __TEST_HPP__
#define __TEST_HPP__

#include <iostream>
#include "basis.hpp"

namespace Filter
{
    class SelectAll
    {
    public:
        bool operator() (const State&) const {return true;}
    };
};

namespace Sorter
{
    class DontSort
    {
    public:
        bool operator() (const State&, const State&) const {return false;}
    };
};

/**
 * Caching creator.
 *
 * Constructs one creator martrix for each orbital of the system and caches them.
 *
 * @tparam System
 */
template<class System>
class Creator
{
public:
    Creator (const System& s_)
    : s(s_), cs(s.N_orbital)
    {
        for (size_t i=0; i<s.N_orbital; i++)
            constructMatrix(i);
    }

    const SMatrix& operator() (size_t i) const
    {
        return cs[i];
    }

private:
    void constructMatrix (size_t i)
    {
        cs[i] = SMatrix(s.basis.size(), s.basis.size());
        // we expect one entry per column
        cs[i].reserve(s.basis.size());
        for (size_t col=0; col<s.basis.size(); col++)
        {
            cs[i].startVec(col);
            if (s.basis(col)[i] == 1)
                continue;
            State state(s.basis(col));
            state[i] = 1;
            const size_t row = s.basis(state);
            const size_t sum = state.count(i);
            const double sign = (sum%2==0)?1:-1; // probably faster than using (-1)^sum
            cs[i].insertBack(row, col) = sign;
        }
        cs[i].finalize();
    }

    const System& s;
    std::vector<SMatrix> cs;
};

template<class System>
class Annihilator
{
public:
    Annihilator (const System& s_)
    : s(s_), as(s.N_orbital)
    {
        for (size_t i=0; i<s.N_orbital; i++)
            as[i] = SMatrix(s.creator(i).transpose());
    }

    const SMatrix& operator() (size_t i) const
    {
        return as[i];
    }

private:
    const System& s;
    std::vector<SMatrix> as;
};

template<class System>
class Hamiltonian
{
public:
    Hamiltonian (const System& s_)
    : s(s_), H(s.basis.size(), s.basis.size())
    {}

    /**
     * Construct the Hamiltonian matrix.
     *
     * Has to be overwritten by deriving classes.
     */
    void construct() {}

    void diagonalise ()
    {
        /*
         * This assertion is somewhat arbitrary, just because my computers tend
         * to have 4GB ~ 4 10^9 bytes of memory.
         * Also, this is not the solution for the problem. I don't understand
         * why Eigen gives a segfault instead of an exception.
         */
        assert(4E9 > s.basis.size()*s.basis.size()*sizeof(double));
        DMatrix m(H);
        SelfAdjointEigenSolver<DMatrix> es(m);
        eigenvalues = es.eigenvalues();
        eigenvectors = es.eigenvectors();
        Emin = eigenvalues.minCoeff();
    }

    DMatrix eigenvectors;
    DVector eigenvalues;
    double Emin;

protected:
    const System& s;
    SMatrix H;
};

template<class System>
class EnsembleAverage
{
public:
    EnsembleAverage (const System& s_)
    : s(s_)
    {}

    double operator() (double beta, const SMatrix& O) const
    {
        double sum = 0;
        for (int i=0; i<s.H.eigenvalues.size(); i++)
            sum +=
                std::exp(-beta * (s.H.eigenvalues(i) - s.H.Emin)) *
                s.H.eigenvectors.col(i).adjoint() * O * s.H.eigenvectors.col(i);
        return sum / partitionFunction(beta);
    }

    double partitionFunction (double beta) const
    {
        double Z = 0;
        for (int i=0; i<s.H.eigenvalues.size(); i++)
            Z += std::exp(-beta * (s.H.eigenvalues(i) - s.H.Emin));
        return Z;
    }

private:
    const System& s;
};

template<class Filter, class Sorter>
class MinimalSystem
{
public:
    MinimalSystem (size_t N_orbital_, const Filter& filter, const Sorter& sorter)
    : N_orbital(N_orbital_), basis(N_orbital, filter, sorter)
    {}

    size_t N_orbital;
    Basis<Filter, Sorter> basis;
};

template<class Filter, class Sorter>
class BasicSystem : public MinimalSystem<Filter, Sorter>
{
public:
    BasicSystem (size_t N_orbital_, const Filter& filter, const Sorter& sorter)
    : MinimalSystem<Filter, Sorter>(N_orbital_, filter, sorter), creator(*this),
      annihilator(*this)
    {}

    Creator<BasicSystem> creator;
    Annihilator<BasicSystem> annihilator;
};

/*
template<class Filter, class Sorter>
class System
{
public:
    System (size_t N_orbital_, const Filter& filter, const Sorter& sorter)
    : N_orbital(N_orbital_), basis(N_orbital, filter, sorter), creator(*this),
      annihilator(*this)
    {}

    size_t N_orbital;
    Basis<Filter, Sorter> basis;
    Creator<System> creator;
    Annihilator<System> annihilator;
};
*/

#endif // __TEST_HPP__
<commit_msg>Bit more documentation.<commit_after>/**
 * @file system.hpp
 * Exact diagonaliser base classes.
 * @author Burkhard Ritter
 * @version
 * @date 2010-12-10
 */

#ifndef __TEST_HPP__
#define __TEST_HPP__

#include <iostream>
#include "basis.hpp"

/**
 * Filters to filter the basis states.
 */
namespace Filter
{
    /**
     * Selects all states.
     *
     * Thus it's a filter that doesn't filter at all. It's the default Filter
     * used.
     */
    class SelectAll
    {
    public:
        bool operator() (const State&) const {return true;}
    };
};

/**
 * Sorters to sort the basis states.
 */
namespace Sorter
{
    /**
     * Does not sort the states.
     *
     * Thus it's a sorter that doesn't sort at all. It's the default Sorter
     * used.
     */
    class DontSort
    {
    public:
        bool operator() (const State&, const State&) const {return false;}
    };
};

/**
 * Caching creator.
 *
 * Constructs one creator martrix for each orbital of the system and caches
 * them.
 *
 * @tparam System
 */
template<class System>
class Creator
{
public:
    Creator (const System& s_)
    : s(s_), cs(s.N_orbital)
    {
        for (size_t i=0; i<s.N_orbital; i++)
            constructMatrix(i);
    }

    /**
     * Creator matrix for the given orbital.
     *
     * \f$c^{\dag}_i\f$
     *
     * @param i orbital of the system
     *
     * @return
     */
    const SMatrix& operator() (size_t i) const
    {
        return cs[i];
    }

private:
    void constructMatrix (size_t i)
    {
        cs[i] = SMatrix(s.basis.size(), s.basis.size());
        // we expect one entry per column
        cs[i].reserve(s.basis.size());
        for (size_t col=0; col<s.basis.size(); col++)
        {
            cs[i].startVec(col);
            if (s.basis(col)[i] == 1)
                continue;
            State state(s.basis(col));
            state[i] = 1;
            const size_t row = s.basis(state);
            const size_t sum = state.count(i);
            const double sign = (sum%2==0)?1:-1; // probably faster than using (-1)^sum
            cs[i].insertBack(row, col) = sign;
        }
        cs[i].finalize();
    }

    const System& s;
    std::vector<SMatrix> cs;
};

/**
 * Caching annihilator.
 *
 * Constructs one annihilator matrix for each orbital of the system and caches
 * them.
 *
 * @tparam System
 */
template<class System>
class Annihilator
{
public:
    Annihilator (const System& s_)
    : s(s_), as(s.N_orbital)
    {
        for (size_t i=0; i<s.N_orbital; i++)
            as[i] = SMatrix(s.creator(i).transpose());
    }

    /**
     * Annihilator matrix for the given orbital.
     *
     * \f$c_i\f$
     *
     * @param i orbital of the system
     *
     * @return
     */
    const SMatrix& operator() (size_t i) const
    {
        return as[i];
    }

private:
    const System& s;
    std::vector<SMatrix> as;
};

/**
 * Hamiltonian of the system.
 *
 * The heart of the fermionic quantum system. Although it is an
 * operator it behaves a little bit different than the other operators, which
 * maybe doesn't entirely come as a surprise. It is
 * not a function object. It has to be subclassed and
 * overwritten for each different physical system. Typical usage:
 * @code
 * Hamiltonian H(mySystem);
 * H.construct();
 * H.diagonalise(); //here the real work happens
 * H.eigenvalues(); //use eigenenergies
 * @endcode
 *
 * @tparam System
 */
template<class System>
class Hamiltonian
{
public:
    Hamiltonian (const System& s_)
    : s(s_), H(s.basis.size(), s.basis.size())
    {}

    /**
     * Construct the Hamiltonian matrix.
     *
     * Has to be overwritten by deriving classes.
     */
    void construct() {}

    /**
     * Diagonalises the Hamiltonian matrix.
     *
     * Uses a dense matrix. Relies on Eigen for the eigenvalue decomposition.
     */
    void diagonalise ()
    {
        /*
         * This assertion is somewhat arbitrary, just because my computers tend
         * to have 4GB ~ 4 10^9 bytes of memory.
         * Also, this is not the solution for the problem. I don't understand
         * why Eigen gives a segfault instead of an exception.
         */
        assert(4E9 > s.basis.size()*s.basis.size()*sizeof(double));
        DMatrix m(H);
        SelfAdjointEigenSolver<DMatrix> es(m);
        eigenvalues = es.eigenvalues();
        eigenvectors = es.eigenvectors();
        Emin = eigenvalues.minCoeff();
    }

    DMatrix eigenvectors;
    DVector eigenvalues;
    double Emin;

protected:
    const System& s;
    SMatrix H;
};

/**
 * Calculate the ensemble average.
 *
 * An operator. Example usage:
 * @code
 * EnsembleAverage ensembleAverage(mySystem);
 * MyFunkyOperator O(mySystem);
 * ensembleAverage(beta, O); //will expect and use mySystem.H
 * @endcode
 *
 * Dependencies: System.basis and System.H (Hamiltonian)
 *
 * @tparam System
 */
template<class System>
class EnsembleAverage
{
public:
    EnsembleAverage (const System& s_)
    : s(s_)
    {}

    /**
     * Calculate the ensemble average for the given operator.
     *
     * @param beta = 1/T (temperature)
     * @param O operator matrix
     *
     * @return
     */
    double operator() (double beta, const SMatrix& O) const
    {
        double sum = 0;
        for (int i=0; i<s.H.eigenvalues.size(); i++)
            sum +=
                std::exp(-beta * (s.H.eigenvalues(i) - s.H.Emin)) *
                s.H.eigenvectors.col(i).adjoint() * O * s.H.eigenvectors.col(i);
        return sum / partitionFunction(beta);
    }

    /**
     * Calculate the partition function at the given temperature.
     *
     * @param beta = 1/T (temperature)
     *
     * @return
     */
    double partitionFunction (double beta) const
    {
        double Z = 0;
        for (int i=0; i<s.H.eigenvalues.size(); i++)
            Z += std::exp(-beta * (s.H.eigenvalues(i) - s.H.Emin));
        return Z;
    }

private:
    const System& s;
};

/**
 * Minimal base class for a fermionic quantum system.
 *
 * Only contains the basis. Filter and Sorter are passed on to the basis
 * and are used to filter and sort the basis states.
 *
 * Typical usage is to subclass and specify a custom Hamiltonian (subclassed
 * from Hamiltonian) as mySystem.H and probably some observables of interest
 * (say the particle number) as well as the EnsembleAverage.
 *
 * @tparam Filter
 * @tparam Sorter
 */
template<class Filter, class Sorter>
class MinimalSystem
{
public:
    /**
     * Construct the minimal system.
     *
     * @param N_orbital number of orbitals
     * @param filter filter to filter basis states
     * @param sorter sorter to sort basis states
     */
    MinimalSystem (size_t N_orbital_, const Filter& filter, const Sorter& sorter)
    : N_orbital(N_orbital_), basis(N_orbital, filter, sorter)
    {}

    size_t N_orbital;
    Basis<Filter, Sorter> basis;
};

/**
 * Basic base class for fermionic quantum systems.
 *
 * In addition to the basis the basic system conveniently defines creator and
 * annihilator.
 *
 * @tparam Filter
 * @tparam Sorter
 */
template<class Filter, class Sorter>
class BasicSystem : public MinimalSystem<Filter, Sorter>
{
public:
    /**
     * Construct the basic system.
     *
     * @param N_orbital_
     * @param filter
     * @param sorter
     */
    BasicSystem (size_t N_orbital_, const Filter& filter, const Sorter& sorter)
    : MinimalSystem<Filter, Sorter>(N_orbital_, filter, sorter), creator(*this),
      annihilator(*this)
    {}

    Creator<BasicSystem> creator;
    Annihilator<BasicSystem> annihilator;
};

#endif // __TEST_HPP__
<|endoftext|>