if_else function

24 views
Skip to first unread message

Jiqiang Guo

unread,
Apr 21, 2012, 9:27:34 PM4/21/12
to stan...@googlegroups.com
I am trying to use if_else function to get
0 ~ poisson(0 * parameters) 
out of the log posterior.  So I want to use 
something like 

lp__ <- lp__ + ifelse(indicator, poisson(...), 0);

But I would have the following problem if the above is used:

~/Desktop/stan/src/models/bugs_examples/vol1/leuk$ make
../../../../../bin/stanc  --name=leuk  leuk.stan
Model name=leuk
Input file=leuk.stan
Output file=leuk.cpp
clang++    -O3 -Wall -Wunused-function -g  -Wno-tautological-compare -DNDEBUG -I ../../../../../lib -I ../../../../../src leuk.cpp -o leuk -L../../../../../bin -lstan
leuk.cpp:190:36: error: no matching function for call to 'if_else'
                    lp__ = (lp__ + if_else(...
                                   ^~~~~~~
../../../../../src/stan/math/special_functions.hpp:485:7: note: candidate template ignored: deduced conflicting types for parameter 'T' ('stan::agrad::var' vs. 'int')
    T if_else(B c, T y_true, T y_false) {
      ^
leuk.cpp:228:16: warning: unused variable 'lp__' [-Wunused-variable]
        double lp__ = 0.0;
               ^
leuk.cpp:216:28: warning: unused variable 'function__' [-Wunused-variable]
        static const char* function__ = "leuk_namespace::write_csv(%1)";
                           ^
2 warnings and 1 error generated.
make: *** [leuk] Error 1

I can see the issue here.  So currently, I put a zero variable as follows, which 
could take vol1/leuk(leukfr) out of the problem list if we do not intend to 
solve 0 ~ poisson(0 * parameters). 

real zero;
zero <- 0;
lp__ <- lp + if_else(indicator, poisson_log(...), zero);


--
Jiqiang 




Bob Carpenter

unread,
Apr 22, 2012, 7:01:57 PM4/22/12
to stan...@googlegroups.com
Here's the definition again:

template <typename B, typename T>


T if_else(B c, T y_true, T y_false) {

return c ? y_true : y_false;
}

I now realize that this is totally broken.

1. if c is a var, we need c.val() ? ...
not just c ? ...

2. if y_true type doesn't match y_false type,
the template matching will fail. If we generalized
the templates to T1 and T2, then the ternary operator
would fail to match (and we would need to somehow
compute the return type).

I'm going to write the specific cases we care about,
because I can't see how to make templates calculate
the return value properly. I'd very much like this
to work for int values, so we have:

int if_else(int,int,int);

It'd be eaiser if everything could get promoted to
double -- then I could use promote_args. As is, I
can't figure out how to calculate the return type without
writing my own template metaprogram for it or without
a whole lot of code duplication.

First, we need both a regular C++ bool and
a var condition, with the latter calling the former:

if_else(bool c, ...) { ... }
if_else(var v, ...) { return if_else(v.val(),...); }

Then, in terms of the other two args, we need to spell
out seven different implementations:

(C,int,int) -> int
(C,,double,int) -> double
(C,int,double) -> double
(C,double,double) -> double

(C,var,T) -> var
(C,T,var) -> var
(C,var,var) -> var [otherwise former two ambiguous]

So that's 14 different functions for this. And the
API winds up being degeernate because it's only
being defined for int and double.

Am I missing a cleaner way to do this?

- Bob

Ben Goodrich

unread,
Apr 22, 2012, 9:24:31 PM4/22/12
to stan...@googlegroups.com
On Sunday, April 22, 2012 7:01:57 PM UTC-4, Bob Carpenter wrote:
Here's the definition again:

     template <typename B, typename T>
     T if_else(B c, T y_true, T y_false) {
       return c ? y_true : y_false;
     }

I now realize that this is totally broken.

Am I missing a cleaner way to do this?

Couldn't a bunch of cases could be covered by

return c * y_true + (!c) * y_false;

particularly if you define multiplication between a bool and a var appropriately.

Ben

Ben Goodrich

unread,
Apr 22, 2012, 11:01:40 PM4/22/12
to stan...@googlegroups.com

It may be better to not template on the first argument, like

     template <typename T>
     T if_else(bool c, T y_true, T y_false) {


       return c * y_true + (!c) * y_false;
     }

because the C++'11 standard codifies conversion of anything to bool in the de facto way of testing whether c != 0.

Ben


Bob Carpenter

unread,
Apr 23, 2012, 12:18:18 AM4/23/12
to stan...@googlegroups.com
Thanks for letting me know.
I'll stick to the first arg being a bool.

The only function signature defined for if-else
in stan/gm/function_signatures.hpp is:

add("if_else",DOUBLE_T,INT_T,DOUBLE_T,DOUBLE_T);

That is, return a double given an (int,double,double)
argument (Stan's "real" is just double behind the scenes).

For this, all we need are these:

var if_else(int,var,var);
var if_else(int,var,double);
var if_else(int,double,var);
double if_else(int,double,double)

I'll put those in for now.

I could use the T template for the first and
last of these, but then I'd probably want a ref
instead of the call-by-value way I did it the
first time.

The problem with the T,T template is that if
you give it (int,double), it won't match or if
you give it (var,double).

The problem with the arithmetic expression way
of doing things is that it does eager evaluation
and winds up putting too much on the auto-dif stack.

I went so far as to use if_else instead of the
ternary op to make the promote-to-var decision
lazy.

We really want to minimize the number of expressions
involving var (params, transformed params, local
vars in models).

So here's what I went with:

inline double if_else(bool c, double y_true, double y_false) {
return c ? y_true : y_false;
}

inline var if_else(bool c, const var& y_true, const var&y_false) {
return c ? y_true : y_false;
}

inline var if_else(bool c, double y_true, const var& y_false) {
if (c)
return var(y_true);
else
return y_false;
}

inline var if_else(bool c, const var& y_true, const double y_false) {
if (c)
return y_true;
else
return var(y_false);
}

I added unit tests for all the ways it's legal to
call from Stan.

- Bob



Reply all
Reply to author
Forward
0 new messages