Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add initial state tests #68

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 62 additions & 0 deletions tests/test_estimation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import numpy as np
import pymc3 as pm
import theano.tensor as tt

from pymc3_hmm.distributions import DiscreteMarkovChain, PoissonZeroProcess
from pymc3_hmm.step_methods import FFBSStep


def simulate_poiszero_hmm(
N,
observed,
mu=10.0,
pi_0_a=np.r_[1, 1],
p_0_a=np.r_[5, 1],
p_1_a=np.r_[1, 1],
pi_0=None,
):
p_0_rv = pm.Dirichlet("p_0", p_0_a)
p_1_rv = pm.Dirichlet("p_1", p_1_a)
P_tt = tt.stack([p_0_rv, p_1_rv])
P_rv = pm.Deterministic("P_tt", tt.shape_padleft(P_tt))
if pi_0 is not None:
pi_0_tt = tt.as_tensor(pi_0)
else:
pi_0_tt = pm.Dirichlet("pi_0", pi_0_a)
S_rv = DiscreteMarkovChain("S_t", P_rv, pi_0_tt, shape=N)
S_rv.tag.test_value = (observed > 0) * 1
return PoissonZeroProcess("Y_t", mu, S_rv, observed=observed)
Comment on lines +9 to +28
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As a simple test for initial probability estimation, I don't know if the zero-Poisson model is really the best choice.

If we're going to use a model with a Dirac emission distribution, we should use one that consists entirely of Dirac emissions; it's simpler and accomplishes the same thing.

Otherwise, we need to consider whether or not a non-Dirac model is a better choice for testing (e.g. mixture of normals). Likewise for the choice of transition matrix.



def test_gamma_0_estimation():

np.random.seed(2032)
true_initial_states = np.array([0.02, 0.98])

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

seems like with true initial states set to [.02, .98] the initial state estimation fails. the error im seeing is

E       AssertionError: 
E       Arrays are not almost equal to 1 decimals
E       
E       Mismatched elements: 2 / 2 (100%)
E       Max absolute difference: 0.25904421
E       Max relative difference: 12.95221072
E        x: array([0.3, 0.7])
E        y: array([0., 1.])

with pm.Model(theano_config={"compute_test_value": "ignore"}) as sim_model:
_ = simulate_poiszero_hmm(30, np.zeros(30), 150, pi_0=true_initial_states)

sim_point = pm.sample_prior_predictive(samples=1, model=sim_model)
sim_point["Y_t"] = sim_point["Y_t"].squeeze()
y_test = sim_point["Y_t"]

Veganveins marked this conversation as resolved.
Show resolved Hide resolved
with pm.Model() as test_model:
_ = simulate_poiszero_hmm(
30,
y_test,
150,
)
states_step = FFBSStep([test_model["S_t"]])

posterior_trace = pm.sample(
step=[states_step],
draws=5,
return_inferencedata=True,
chains=1,
progressbar=True,
)

estimated_initial_state_probs = posterior_trace.posterior.pi_0.values[0].mean(0)
np.testing.assert_almost_equal(
estimated_initial_state_probs, true_initial_states, 1
)
15 changes: 15 additions & 0 deletions tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import theano.tensor as tt

from pymc3_hmm.utils import (
compute_steady_state,
compute_trans_freqs,
logdotexp,
multilogit_inv,
Expand Down Expand Up @@ -120,3 +121,17 @@ def test_multilogit_inv(test_input, test_output):
res = multilogit_inv(tt.as_tensor_variable(test_input))
res = res.eval()
assert np.array_equal(res.round(2), test_output)


test_cases = [
(np.ones((2, 2)) * 0.5, np.array([0.5, 0.5])),
(np.eye(4), np.array([1, 0, 0, 0])),
]


@pytest.mark.parametrize("transition_matrix, steady_state", test_cases)
def test_compute_steady_state(transition_matrix, steady_state):

P = tt.as_tensor_variable(transition_matrix)
ss_probs = compute_steady_state(P)
np.testing.assert_almost_equal(ss_probs.eval(), steady_state, 1)
7 changes: 5 additions & 2 deletions tests/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@


def simulate_poiszero_hmm(
N, mu=10.0, pi_0_a=np.r_[1, 1], p_0_a=np.r_[5, 1], p_1_a=np.r_[1, 1]
N, mu=10.0, pi_0_a=np.r_[1, 1], p_0_a=np.r_[5, 1], p_1_a=np.r_[1, 1], pi_0=None
):

with pm.Model() as test_model:
Expand All @@ -16,7 +16,10 @@ def simulate_poiszero_hmm(
P_tt = tt.stack([p_0_rv, p_1_rv])
P_rv = pm.Deterministic("P_tt", tt.shape_padleft(P_tt))

pi_0_tt = pm.Dirichlet("pi_0", pi_0_a)
if pi_0:
pi_0_tt = pm.Dirichlet("pi_0", pi_0)
else:
pi_0_tt = pm.Dirichlet("pi_0", pi_0_a)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@brandonwillard I think you helped me edit this simulate_poiszero_hmm function when I started working on this but I think I lost that change when I was trying to re-organize the commits. Does this if-else condition look right to you? I can't remember if this was exactly how we had it before

Copy link
Contributor

@brandonwillard brandonwillard Mar 26, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't recall; you'll need to check the Git history or walk through whatever problem(s) this is causing.

S_rv = DiscreteMarkovChain("S_t", P_rv, pi_0_tt, shape=N)

Expand Down