# Restricting the value of a Deterministic variable

**URL:** <https://discourse.pymc.io/t/restricting-the-value-of-a-deterministic-variable/6258>\
**Category:** Questions\
**Created:** [November 14, 2020, 4:36am UTC](https://discourse.pymc.io/t/restricting-the-value-of-a-deterministic-variable/6258 "2020-11-14T04:36:41Z")\
**Posts on this page:** 6\
**Page:** 1

<div class="post-metadata">

**Author:** ![n1ck94](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.pymc.io/n1ck94/32/646_2.png) [@n1ck94](https://discourse.pymc.io/u/n1ck94)\
**Post date:** [November 14, 2020, 4:36am UTC](https://discourse.pymc.io/t/restricting-the-value-of-a-deterministic-variable/6258/1 "2020-11-14T04:36:41Z")

</div>

Is it possible to restrict the value of a Deterministic variable? For example, my model looks like this:

```auto
with pm.Model() as myModel:
    sig = pm.HalfNormal('sig', sd=2)
    kappa = pm.Normal('kappa', mu=1, sd=1)
    rhoCp = pm.Normal('rhoCp', mu=4, sd=1)
    alpha = pm.Determinstic('alpha', kappa/rhoCp)
    SE = ComplicatedModel(alpha, data) # Squared error between model output and data
    pm.Potential('res', -0.5*SE/sig**2)

```

If `alpha` is greater than or equal to 0.5, the `ComplicatedModel()` is unstable and returns NaNs. So is there a way for me to restrict `alpha<0.5` without causing the sampler too much grief? I suppose I could use `theano ifelse` or `theano maximum` like the following:

```auto
from theano import tensor as tt
from theano.ifelse import ifelse
with pm.Model() as myModel:
    sig = pm.HalfNormal('sig', sd=2)
    kappa = pm.Normal('kappa', mu=1, sd=1)
    rhoCp = pm.Normal('rhoCp', mu=4, sd=1)
    a = kappa/rhoCp
    alpha = pm.Determinstic('alpha', ifelse(tt.lt(a, 0.5), a, 0.5))
    # OR
    alpha = pm.Determinstic('alpha', tt.maximum(kappa/alpha, 0.5))

```

However, is the above likely to mess with the nice geometry of the parameter space and make sampling using NUTS difficult?

---

<div class="post-metadata">

**Author:** ![junpenglao](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.pymc.io/junpenglao/32/8_2.png) [@junpenglao](https://discourse.pymc.io/u/junpenglao)\
**Post date:** [November 14, 2020, 8:35am UTC](https://discourse.pymc.io/t/restricting-the-value-of-a-deterministic-variable/6258/2 "2020-11-14T08:35:38Z")

</div>

You can add a potential to your model: `pm.Potential('restrict_term', tt.switch(tt.lt(a, 0.5), 0., -np.inf))`  
This means that when a \>= .5, the model will produce -inf log\_prob, and the sample will be rejected.

---

<div class="post-metadata">

**Author:** ![n1ck94](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.pymc.io/n1ck94/32/646_2.png) [@n1ck94](https://discourse.pymc.io/u/n1ck94)\
**Post date:** [November 18, 2020, 1:02pm UTC](https://discourse.pymc.io/t/restricting-the-value-of-a-deterministic-variable/6258/3 "2020-11-18T13:02:46Z")

</div>

Works great. Thanks!

---

<div class="post-metadata">

**Author:** ![n1ck94](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.pymc.io/n1ck94/32/646_2.png) [@n1ck94](https://discourse.pymc.io/u/n1ck94)\
**Post date:** [November 22, 2020, 2:34am UTC](https://discourse.pymc.io/t/restricting-the-value-of-a-deterministic-variable/6258/4 "2020-11-22T02:34:13Z")

</div>

On further inspection, it looks like some instances where a\>=0.5 are still getting through to the `SE = ComplicatedModel(alpha, data)` line. I’m wondering at what point is the sample meant to be rejected? For example:

```auto
from theano import tensor as tt
with pm.Model() as myModel:
    sig = pm.HalfNormal('sig', sd=2)
    kappa = pm.Normal('kappa', mu=1, sd=1)
    rhoCp = pm.Normal('rhoCp', mu=4, sd=1)
    alpha = kappa/rhoCp
    pm.Potential('restrict_term', tt.switch(tt.lt(alpha, 0.5), 0., -np.inf))
    # Is the sample meant to be rejected at the above line preventing the 
    # non-conforming alpha value from being passed to the line below?
    SE = ComplicatedModel(alpha, data) 
    pm.Potential('res', -0.5*SE/sig**2)

```

I have included an `alpha_printed = tt.printing.Print('alpha')(alpha)` line inside the `ComplicatedModel(alpha, data)` function and found that an `alpha` value great than `0.5` is indeed finding its way into the function.

---

<div class="post-metadata">

**Author:** ![n1ck94](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.pymc.io/n1ck94/32/646_2.png) [@n1ck94](https://discourse.pymc.io/u/n1ck94)\
**Post date:** [November 27, 2020, 10:00pm UTC](https://discourse.pymc.io/t/restricting-the-value-of-a-deterministic-variable/6258/5 "2020-11-27T22:00:35Z")

</div>

There is a post about using random variables as bounds for prior distributions (see [here](https://discourse.pymc.io/t/prior-boundaries-that-are-not-constant-but-random-variables/656)) which is also helpful. Using the same method, early experiments indicate that the following works:

```auto
from theano import tensor as tt
alphaMax = tt.as_tensor_variable(0.5)
with pm.Model() as myModel:
    sig = pm.HalfNormal('sig', sd=2)
    kappa = pm.Normal('kappa', mu=1, sd=1)
    rhoCp_ = pm.Normal('rhoCp_', mu=4, sd=1)
    rhoCp = pm.Determinstic('rhoCp', tt.maximum(rhoCp, kappa/alphaMax))
    alpha = pm.Determinstic('alpha', kappa/rhoCp)
    SE = ComplicatedModel(alpha, data) # Squared error between model output and data
    pm.Potential('res', -0.5*SE/sig**2)

```

---

<div class="post-metadata">

**Author:** ![junpenglao](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.pymc.io/junpenglao/32/8_2.png) [@junpenglao](https://discourse.pymc.io/u/junpenglao)\
**Post date:** [November 28, 2020, 9:03am UTC](https://discourse.pymc.io/t/restricting-the-value-of-a-deterministic-variable/6258/6 "2020-11-28T09:03:17Z")

</div>

> [@n1ck94](#):
>
> I have included an `alpha_printed = tt.printing.Print('alpha')(alpha)` line inside the `ComplicatedModel(alpha, data)` function and found that an `alpha` value great than `0.5` is indeed finding its way into the function.

I find that quite surprising! Sampling with NUTS usually accept-reject all RVs at the same time (ie it is not sequential).  
I would evaluate at the point where you got a \>= .5, and check the logp:

```python
testpoint = model.test_point
# find the slice where you got a>=.5, and replace the value below
testpoint['a'] = ...
testpoint['sig'] = ...
...
# FYI, there might be better way to do this 
print(model.logp(testpoint))

```
