Skip to content

Commit

Permalink
semiring
Browse files Browse the repository at this point in the history
git-svn-id: https://ws10smt.googlecode.com/svn/trunk@608 ec762483-ff6d-05da-a07a-a48fb63a330f
  • Loading branch information
graehl committed Aug 21, 2010
1 parent fd703fc commit 1423db8
Show file tree
Hide file tree
Showing 4 changed files with 353 additions and 21 deletions.
87 changes: 68 additions & 19 deletions utils/logval.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,29 +8,54 @@
#include <cstdlib>
#include <cmath>
#include <limits>
#include "semiring.h"

//TODO: template for supporting negation or not - most uses are for nonnegative "probs" only; probably some 10-20% speedup available
template <class T>
class LogVal {
public:
LogVal() : s_(), v_(-std::numeric_limits<T>::infinity()) {}
typedef LogVal<T> Self;

LogVal() : s_(), v_(LOGVAL_LOG0) {}
explicit LogVal(double x) : s_(std::signbit(x)), v_(s_ ? std::log(-x) : std::log(x)) {}
LogVal(init_minus_1) : s_(true),v_(0) { }
LogVal(init_1) : s_(),v_(0) { }
LogVal(init_0) : s_(),v_(LOGVAL_LOG0) { }
LogVal(int x) : s_(x<0), v_(s_ ? std::log(-x) : std::log(x)) {}
LogVal(unsigned x) : s_(0), v_(std::log(x)) { }
LogVal(double lnx,bool sign) : s_(sign),v_(lnx) {}
static LogVal<T> exp(T lnx) { return LogVal(lnx,false); }
LogVal(double lnx,init_lnx) : s_(),v_(lnx) {}
static Self exp(T lnx) { return Self(lnx,false); }

// maybe the below are faster than == 1 and == 0. i don't know.
bool is_1() const { return v_==0&&s_==0; }
bool is_0() const { return v_==LOGVAL_LOG0; }

static LogVal<T> One() { return LogVal(1); }
static LogVal<T> Zero() { return LogVal(); }
static LogVal<T> e() { return LogVal(1,false); }
static Self One() { return Self(1); }
static Self Zero() { return Self(); }
static Self e() { return Self(1,false); }
void logeq(const T& v) { s_ = false; v_ = v; }

std::size_t hash_impl() const {
using namespace boost;
return hash_value(v_)+s_;
}

LogVal& operator+=(const LogVal& a) {
if (a.v_ == -std::numeric_limits<T>::infinity()) return *this;
// just like std::signbit, negative means true. weird, i know
bool signbit() const {
return s_;
}
friend inline bool signbit(Self const& x) { return x.signbit(); }

Self& besteq(const Self& a) {
assert(!a.s_ && !s_);
if (a.v_ < v_)
v_=a.v_;
return *this;
}

Self& operator+=(const Self& a) {
if (a.is_0()) return *this;
if (a.s_ == s_) {
if (a.v_ < v_) {
v_ = v_ + log1p(std::exp(a.v_ - v_));
Expand All @@ -48,31 +73,31 @@ class LogVal {
return *this;
}

LogVal& operator*=(const LogVal& a) {
Self& operator*=(const Self& a) {
s_ = (s_ != a.s_);
v_ += a.v_;
return *this;
}

LogVal& operator/=(const LogVal& a) {
Self& operator/=(const Self& a) {
s_ = (s_ != a.s_);
v_ -= a.v_;
return *this;
}

LogVal& operator-=(const LogVal& a) {
LogVal b = a;
b.invert();
Self& operator-=(const Self& a) {
Self b = a;
b.negate();
return *this += b;
}

// LogVal(fabs(log(x)),x.s_)
friend LogVal abslog(LogVal x) {
// Self(fabs(log(x)),x.s_)
friend Self abslog(Self x) {
if (x.v_<0) x.v_=-x.v_;
return x;
}

LogVal& poweq(const T& power) {
Self& poweq(const T& power) {
#if LOGVAL_CHECK_NEG
if (s_) {
std::cerr << "poweq(T) not implemented when s_ is true\n";
Expand All @@ -83,26 +108,50 @@ class LogVal {
return *this;
}

void invert() { s_ = !s_; }
//remember, s_ means negative.
inline bool lt(Self const& o) const {
return s_ ? (!o.s_ || o.v_<v_) : (o.s_ || v_<o.v_);
}
inline bool gt(Self const& o) const {
return s_ ? (o.s_ && v_<o.v_) : (!o.s_ && o.v_<v_);
}

LogVal pow(const T& power) const {
LogVal res = *this;
Self operator-() const {
return Self(v_,-s_);
}
void negate() { s_ = !s_; }

Self inverse() const { return Self(-v_,s_); }

Self pow(const T& power) const {
Self res = *this;
res.poweq(power);
return res;
}

LogVal root(const T& root) const {
Self root(const T& root) const {
return pow(1/root);
}

operator T() const {
if (s_) return -std::exp(v_); else return std::exp(v_);
}
T as_float() const {
if (s_) return -std::exp(v_); else return std::exp(v_);
}

bool s_;
T v_;
};

template <class T>
struct semiring_traits<LogVal<T> > : default_semiring_traits<LogVal<T> > {
static const bool has_logplus=true;
static const bool has_besteq=true;
static const bool has_subtract=true;
static const bool has_negative=true;
};

// copy elision - as opposed to explicit copy of LogVal<T> const& o1, we should be able to construct Logval r=a+(b+c) as a single result in place in r. todo: return std::move(o1) - C++0x
template<class T>
LogVal<T> operator+(LogVal<T> o1, const LogVal<T>& o2) {
Expand Down
10 changes: 8 additions & 2 deletions utils/logval_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,19 @@ TEST_F(LogValTest,Order) {
EXPECT_FALSE(a < a);
}

TEST_F(LogValTest,Invert) {
TEST_F(LogValTest,Negate) {
LogVal<double> x(-2.4);
LogVal<double> y(2.4);
y.invert();
y.negate();
EXPECT_FLOAT_EQ(x,y);
}

TEST_F(LogValTest,Inverse) {
LogVal<double> x(1/2.4);
LogVal<double> y(2.4);
EXPECT_FLOAT_EQ(x,y.inverse());
}

TEST_F(LogValTest,Minus) {
LogVal<double> x(12);
LogVal<double> y(2);
Expand Down
203 changes: 203 additions & 0 deletions utils/max_plus.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
#ifndef MAX_PLUS_H_
#define MAX_PLUS_H_

#include
// max-plus algebra. ordering a > b really means that (i.e. default a<b sorting will do worst (closest to 0) first. so get used to passing predicates like std::greater<MaxPlus<T> > around
// x+y := max{x,y}
// x*y := x+y
// 0 := -inf
// 1 := 0
// additive inverse does not, but mult. does. (inverse()) and x/y := x-y = x+y.inverse()
//WARNING: default order is reversed, on purpose, i.e. a<b means a "better than" b, i.e. log(p_a)>log(p_b). sorry. defaults in libs are to order ascending, but we want best first.

#include <boost/functional/hash.hpp>
#include <iostream>
#include <cstdlib>
#include <cmath>
#include <limits>
#include "semiring.h"
#define TROPICAL_DEBUG(x)
#undef LOGVAL_LOG0
#define LOGVAL_LOG0 -std::numeric_limits<T>::infinity()

template <class T>
class MaxPlus {
public:
typedef MaxPlus<T> Self;
MaxPlus() : v_(LOGVAL_LOG0) {}
explicit MaxPlus(double x) : v_(std::log(x)) {}
MaxPlus(init_1) : v_(0) { }
MaxPlus(init_0) : v_(LOGVAL_LOG0) { }
MaxPlus(int x) : v_(std::log(x)) {}
MaxPlus(unsigned x) : v_(std::log(x)) { }
MaxPlus(double lnx,bool sign) : v_(lnx) { TROPICAL_DEBUG(assert(!sign)); }
MaxPlus(double lnx,init_lnx) : v_(lnx) {}
static Self exp(T lnx) { return MaxPlus(lnx,false); }

// maybe the below are faster than == 1 and == 0. i don't know.
bool is_1() const { return v_==0; }
bool is_0() const { return v_==LOGVAL_LOG0; }

static Self One() { return Self(init_1()); }
static Self Zero() { return Self(init_0()); }
static Self e() { return Self(1,false); }
void logeq(const T& v) { v_ = v; }
bool signbit() const { return false; }

std::size_t hash_impl() const {
using namespace boost;
return hash_value(v_);
}

Self& logpluseq(const Self& a) {
if (a.is_0()) return *this;
if (a.s_ == s_) {
if (a.v_ < v_) {
v_ = v_ + log1p(std::exp(a.v_ - v_));
} else {
v_ = a.v_ + log1p(std::exp(v_ - a.v_));
}
} else {
if (a.v_ < v_) {
v_ = v_ + log1p(-std::exp(a.v_ - v_));
} else {
v_ = a.v_ + log1p(-std::exp(v_ - a.v_));
s_ = !s_;
}
}
return *this;
}

Self& besteq(const Self& a) {
if (a.v_ < v_)
v_=a.v_;
return *this;
}

Self& operator+=(const Self& a) {
if (a.v_ < v_)
v_=a.v_;
return *this;
}

Self& operator*=(const Self& a) {
v_ += a.v_;
return *this;
}

Self& operator/=(const Self& a) {
v_ -= a.v_;
return *this;
}

// Self(fabs(log(x)),x.s_)
friend Self abslog(Self x) {
if (x.v_<0) x.v_=-x.v_;
return x;
}

Self& poweq(const T& power) {
v_ *= power;
return *this;
}

Self inverse() const {
return Self(-v_,false);
}

Self pow(const T& power) const {
Self res = *this;
res.poweq(power);
return res;
}

Self root(const T& root) const {
return pow(1/root);
}

// copy elision - as opposed to explicit copy of Self const& o1, we should be able to construct Logval r=a+(b+c) as a single result in place in r. todo: return std::move(o1) - C++0x
friend inline operator+(Self a,Self const& b) {
a+=b;
return a;
}
friend inline operator*(Self a,Self const& b) {
a*=b;
return a;
}
friend inline operator/(Self a,Self const& b) {
a/=b;
return a;
}
friend inline T log(Self const& a) {
return a.v_;
}
friend inline T pow(Self const& a,T const& e) {
return a.pow(e);
}

// intentionally not defining an operator < or operator > - because you may want to default (for library convenience) a<b means a better than b (i.e. gt)
inline bool lt(Self const& o) const {
return v_<o.v_;
}
inline bool gt(Self const& o) const {
return o.v_<v_;
}
friend inline bool operator==(Self const& lhs, Self const&rhs) {
return lhs.v_ == rhs.v_;
}
friend inline bool operator!=(Self const& lhs, Self const&rhs) {
return lhs.v_ != rhs.v_;
}
/*
operator T() const {
return std::exp(v_);
}
*/
T as_float() const {
return std::exp(v_);
}

T v_;
};

template <class T>
struct semiring_traits<MaxPlus<T> > : default_semiring_traits<MaxPlus<T> > {
static const bool has_logplus=true;
static const bool has_besteq=true;
};


template <class T>

#if 0
template <class T>
bool operator<=(const MaxPlus<T>& lhs, const MaxPlus<T>& rhs) {
return (lhs.v_ <= rhs.v_);
}

template <class T>
bool operator>(const MaxPlus<T>& lhs, const MaxPlus<T>& rhs) {
return (lhs.v_ > rhs.v_);
}

template <class T>
bool operator>=(const MaxPlus<T>& lhs, const MaxPlus<T>& rhs) {
return (lhs.v_ >= rhs.v_);
}
#endif


template <class T>
std::size_t hash_value(const MaxPlus<T>& x) { return x.hash_impl(); }

template <class T>
bool operator==(const MaxPlus<T>& lhs, const MaxPlus<T>& rhs) {
return (lhs.v_ == rhs.v_) && (lhs.s_ == rhs.s_);
}

template <class T>
bool operator!=(const MaxPlus<T>& lhs, const MaxPlus<T>& rhs) {
return !(lhs == rhs);
}

#endif
Loading

0 comments on commit 1423db8

Please sign in to comment.