def test_autodetect_coords_from_model(self, use_context): df_data = pd.DataFrame(columns=["date"]).set_index("date") dates = pd.date_range(start="2020-05-01", end="2020-05-20") for city, mu in {"Berlin": 15, "San Marino": 18, "Paris": 16}.items(): df_data[city] = np.random.normal(loc=mu, size=len(dates)) df_data.index = dates df_data.index.name = "date" coords = {"date": df_data.index, "city": df_data.columns} with pm.Model(coords=coords) as model: europe_mean = pm.Normal("europe_mean_temp", mu=15.0, sigma=3.0) city_offset = pm.Normal("city_offset", mu=0.0, sigma=3.0, dims="city") city_temperature = pm.Deterministic( "city_temperature", europe_mean + city_offset, dims="city" ) data_dims = ("date", "city") data = pm.ConstantData("data", df_data, dims=data_dims) _ = pm.Normal( "likelihood", mu=city_temperature, sigma=0.5, observed=data, dims=data_dims ) trace = pm.sample( return_inferencedata=False, compute_convergence_checks=False, cores=1, chains=1, tune=20, draws=30, step=pm.Metropolis(), ) if use_context: idata = to_inference_data(trace=trace) if not use_context: idata = to_inference_data(trace=trace, model=model) assert "city" in list(idata.posterior.dims) assert "city" in list(idata.observed_data.dims) assert "date" in list(idata.observed_data.dims) np.testing.assert_array_equal(idata.posterior.coords["city"], coords["city"]) np.testing.assert_array_equal(idata.observed_data.coords["date"], coords["date"]) np.testing.assert_array_equal(idata.observed_data.coords["city"], coords["city"])
def test_edge_case(self): # Edge case discovered in #2948 ndim = 3 with pm.Model() as m: pm.LogNormal("sigma", mu=np.zeros(ndim), tau=np.ones(ndim), shape=ndim) # variance for the correlation matrix pm.HalfCauchy("nu", beta=10) step = pm.NUTS() func = step._logp_dlogp_func initial_point = m.recompute_initial_point() func.set_extra_values(initial_point) q = func.dict_to_array(initial_point) logp, dlogp = func(q) assert logp.size == 1 assert dlogp.size == 4 npt.assert_allclose(dlogp, 0.0, atol=1e-5)
def test_sample_find_MAP_does_not_modify_start(): # see https://github.com/pymc-devs/pymc/pull/4458 with pm.Model(): pm.LogNormal("untransformed") # make sure find_Map does not modify the start dict start = {"untransformed": 2} pm.find_MAP(start=start) assert start == {"untransformed": 2} # make sure sample does not modify the start dict start = {"untransformed": 0.2} pm.sample(draws=10, step=pm.Metropolis(), tune=5, start=start, chains=3) assert start == {"untransformed": 0.2} # make sure sample does not modify the start when passes as list of dict start = [{"untransformed": 2}, {"untransformed": 0.2}] pm.sample(draws=10, step=pm.Metropolis(), tune=5, start=start, chains=2) assert start == [{"untransformed": 2}, {"untransformed": 0.2}]
def test_custom_dist_sum_stat(self, floatX): with aesara.config.change_flags(floatX=floatX): with pm.Model() as m: a = pm.Normal("a", mu=0, sigma=1) b = pm.HalfNormal("b", sigma=1) s = pm.Simulator( "s", self.normal_sim, a, b, distance=self.abs_diff, sum_stat=self.quantiles, observed=self.data, ) assert self.count_rvs(m.logpt()) == 1 with m: pm.sample_smc(draws=100)
def test_implicit_coords_series(self): pd = pytest.importorskip("pandas") ser_sales = pd.Series( data=np.random.randint(low=0, high=30, size=22), index=pd.date_range(start="2020-05-01", periods=22, freq="24H", name="date"), name="sales", ) with pm.Model() as pmodel: pm.ConstantData("sales", ser_sales, dims="date", export_index_as_coords=True) assert "date" in pmodel.coords assert len(pmodel.coords["date"]) == 22 assert pmodel.RV_dims == {"sales": ("date", )}
def test_vector_ode_1_param(self): """Test running model for a vector ODE with 1 parameter""" def system(y, t, p): ds = -p[0] * y[0] * y[1] di = p[0] * y[0] * y[1] - y[1] return [ds, di] times = np.array( [0.0, 0.8, 1.6, 2.4, 3.2, 4.0, 4.8, 5.6, 6.4, 7.2, 8.0]) yobs = np.array([ [1.02, 0.02], [0.86, 0.12], [0.43, 0.37], [0.14, 0.42], [0.05, 0.43], [0.03, 0.14], [0.02, 0.08], [0.02, 0.04], [0.02, 0.01], [0.02, 0.01], [0.02, 0.01], ]) ode_model = DifferentialEquation(func=system, t0=0, times=times, n_states=2, n_theta=1) with pm.Model() as model: R = pm.LogNormal("R", 1, 5, initval=1) sigma = pm.HalfCauchy("sigma", 1, shape=2, initval=[0.5, 0.5]) forward = ode_model(theta=[R], y0=[0.99, 0.01]) y = pm.LogNormal("y", mu=pm.math.log(forward), sd=sigma, observed=yobs) idata = pm.sample(100, tune=0, chains=1) assert idata.posterior["R"].shape == (1, 100) assert idata.posterior["sigma"].shape == (1, 100, 2)
def test_multivariate_observations(self): coords = {"direction": ["x", "y", "z"], "experiment": np.arange(20)} data = np.random.multinomial(20, [0.2, 0.3, 0.5], size=20) with pm.Model(coords=coords): p = pm.Beta("p", 1, 1, size=(3,)) p = p / p.sum() pm.Multinomial("y", 20, p, dims=("experiment", "direction"), observed=data) idata = pm.sample(draws=50, chains=2, tune=100, return_inferencedata=True) test_dict = { "posterior": ["p"], "sample_stats": ["lp"], "log_likelihood": ["y"], "observed_data": ["y"], } fails = check_multiple_attrs(test_dict, idata) assert not fails assert "direction" not in idata.log_likelihood.dims assert "direction" in idata.observed_data.dims assert idata.log_likelihood["y"].shape == (2, 50, 20)
def RunMCMC(wfModel, numSteps, burnIn, returnDict): """ This is outside main() and called with 'multiprocessing' to avoid memory leaks. """ M = pymc.MCMC(pymc.Model(wfModel)) M.use_step_method(pymc.Metropolis, M.startTime, proposal_sd=100., proposal_distribution='Normal') M.use_step_method(pymc.Metropolis, M.energy, proposal_sd=1., proposal_distribution='Normal') M.use_step_method(pymc.Metropolis, M.slowness, proposal_sd=100., proposal_distribution='Normal') M.sample(iter=numSteps, verbose=0) returnDict["startTimeTr"] = M.trace("startTime")[:] returnDict["energyTr"] = M.trace("energy")[:] returnDict["slownessTr"] = M.trace("slowness")[:]
def test_shapeerror_from_resize_immutable_dims(): """ Trying to resize an immutable dimension should raise a ShapeError. Even if the variable being updated is a SharedVariable and has other dimensions that are mutable. """ with pm.Model() as pmodel: a = pm.Normal("a", mu=[1, 2, 3], dims="fixed") m = pm.MutableData("m", [[1, 2, 3]], dims=("one", "fixed")) # This is fine because the "fixed" dim is not resized pm.set_data({"m": [[1, 2, 3], [3, 4, 5]]}) with pytest.raises(ShapeError, match="was initialized from 'a'"): # Can't work because the "fixed" dimension is linked to a constant shape: # Note that the new data tries to change both dimensions with pmodel: pm.set_data({"m": [[1, 2], [3, 4]]})
def getModel(t, iObs): k = getKernel(3, t) mu = getMean(2, t) aNodes = [] GP1 = pm.MvNormalCov('GP1', mu=mu, C=k) #@UndefinedVariable aNodes.append(GP1) for i in range(len(t)): aNodes.append(pm.Normal('N' + str(i + 1), mu=GP1[i], tau=1 / 50)) #@UndefinedVariable if i in iObs: aNodes.append( pm.Normal('oN' + str(i + 1), mu=GP1[i], tau=1 / 50, observed=True, value=-100)) #@UndefinedVariable return pm.Model(aNodes)
def getData(self, dfTrack, nTrackId, avgSpeed, nProb_Gait, sGait): # g = pgm.Graph(); # cpt1 = [.5, .5]; # cpt2 = {"['False']": [.5, .5],"['True']": [nProb_Gait, 1-nProb_Gait]}; # g.addnode(pgm.Node(sGait, ["False", "True"], [None], cpt1)); # g.addnode(pgm.Node("Speed=%.2f"%avgSpeed, ["False", "True"], [g.node[sGait]], cpt2)); # g.setup(); G_obs = [1.]; N = len(G_obs); gait = pm.Categorical(sGait, [0.5, 0.5], value=pl.ones(N)); p_speed = pm.Lambda('p_'+sGait, lambda gait=gait: pl.where(gait, nProb_Gait, [0.5, 0.5])); speed = pm.Categorical("Speed=%.2f"%avgSpeed, p_speed, value=G_obs, observed=True); model = pm.Model([gait, speed]); g = pm.graph.graph(model); g.write_pdf("./Models/Graph2_"+str(int(nTrackId))+"_"+str(sGait)+".pdf"); # g.write2pdf("./Models/Graph_"+str(int(nTrackId))+"_"+str(sGait)+".pdf"); data = {"TrackId":nTrackId, "Type":sGait, "Belief":nProb_Gait, "Obs":["Speed"], "Obs_Vals":[avgSpeed], "MEs":["Walk","Stand"], "Graph":g};
def test_implicit_coords_dataframe(self): N_rows = 5 N_cols = 7 df_data = pd.DataFrame() for c in range(N_cols): df_data[f"Column {c+1}"] = np.random.normal(size=(N_rows, )) df_data.index.name = "rows" df_data.columns.name = "columns" # infer coordinates from index and columns of the DataFrame with pm.Model() as pmodel: pm.Data("observations", df_data, dims=("rows", "columns"), export_index_as_coords=True) assert "rows" in pmodel.coords assert "columns" in pmodel.coords assert pmodel.RV_dims == {"observations": ("rows", "columns")}
def test_eval_rv_shapes(self): with pm.Model( coords={ "city": ["Sydney", "Las Vegas", "Düsseldorf"], } ) as pmodel: pm.MutableData("budget", [1, 2, 3, 4], dims="year") pm.Normal("untransformed", size=(1, 2)) pm.Uniform("transformed", size=(7,)) obs = pm.Uniform("observed", size=(3,), observed=[0.1, 0.2, 0.3]) pm.LogNormal("lognorm", mu=at.log(obs)) pm.Normal("from_dims", dims=("city", "year")) shapes = pmodel.eval_rv_shapes() assert shapes["untransformed"] == (1, 2) assert shapes["transformed"] == (7,) assert shapes["transformed_interval__"] == (7,) assert shapes["lognorm"] == (3,) assert shapes["lognorm_log__"] == (3,) assert shapes["from_dims"] == (3, 4)
def test_shared_scalar_as_rv_input(self): # See https://github.com/pymc-devs/pymc/issues/3139 with pm.Model() as m: shared_var = shared(5.0) v = pm.Normal("v", mu=shared_var, size=1) np.testing.assert_allclose( logpt(v, np.r_[5.0]).eval(), -0.91893853, rtol=1e-5, ) shared_var.set_value(10.0) np.testing.assert_allclose( logpt(v, np.r_[10.0]).eval(), -0.91893853, rtol=1e-5, )
def test_symbolic_coords(self): """ In v4 dimensions can be created without passing coordinate values. Their lengths are then automatically linked to the corresponding Tensor dimension. """ with pm.Model() as pmodel: intensity = pm.Data("intensity", np.ones((2, 3)), dims=("row", "column")) assert "row" in pmodel.dim_lengths assert "column" in pmodel.dim_lengths assert isinstance(pmodel.dim_lengths["row"], TensorVariable) assert isinstance(pmodel.dim_lengths["column"], TensorVariable) assert pmodel.dim_lengths["row"].eval() == 2 assert pmodel.dim_lengths["column"].eval() == 3 intensity.set_value(floatX(np.ones((4, 5)))) assert pmodel.dim_lengths["row"].eval() == 4 assert pmodel.dim_lengths["column"].eval() == 5
def sample(parts, outputfile, samples, adaptevery = 100, adaptafter = 100, singlecore = False): options = varcontainer.VarContainer() options.outputFile = outputfile options.singlecore = singlecore options.nsamples = samples options.adapt_every = adaptevery options.adapt_after = adaptafter manager = varcontainer.VarContainer() manager.options = options manager.model = pymc.Model(parts) runner = pma.MyMCRunner() runner.run(manager) runner.finalize(manager)
def memsample(parts, samples, adaptevery = 100, adaptafter = 100): options = varcontainer.VarContainer() options.singlecore = True options.nsamples = samples options.adapt_every = adaptevery options.adapt_after = adaptafter manager = varcontainer.VarContainer() manager.options = options manager.model = pymc.Model(parts) runner = pma.MyMCMemRunner() runner.run(manager) runner.finalize(manager) return manager.chain
def make_model(self): """ Returns a dictionary representing the model to be used with PyMC. """ # Hyper-parameters for the mean response theta_m, see Eq. (8) and assert isinstance(self.k, GPy.kern.RBF), 'Only working for RBF kernel' # Length scales for the mean response, see Eq. (43) and Eq. (46) #ell_m = pm.Uniform('ell_m', 0.1, 1., value=np.ones((self.input_dim,))) ell = LogLogistic('ell', value=np.ones(self.input_dim, )) # Signal strength of the mean response, see Eq. (47) for alpha = m s = Jeffreys('s', value=1.) #s_m = LogLogistic('s_m', value=1.) # Jitter of the mean covariance, see Eq. (48) for alpha = m j = Jeffreys('j', value=1.) # MEAN RESPONSE # Correlation matrix @pm.deterministic(plot=False, trace=False) def C(ell=ell, X=self.X): self.k.variance = 1. self.k.lengthscale = ell return self.k.K(X) # Covariance matrix without jitter @pm.deterministic(plot=False, trace=False) def Knj(s=s, C=C): return (s**2) * C # Covariance matrix with jitter @pm.deterministic(plot=False, trace=False) def K(j=j, Knj=Knj): return Knj + (j**2) * np.eye(Knj.shape[0]) # The Cholesky of the covariance U = Cholesky('U', C=K, plot=False, trace=False) # The observations y = MultivariateNormal('y', value=self.Y, mu=np.zeros((self.num_obs, )), U=U, observed=True) return pm.Model(locals())
def test_model_d2logp(jacobian): with pm.Model() as m: x = pm.Normal("x", 0, 1, size=2) y = pm.LogNormal("y", 0, 1, size=2) test_vals = np.array([0.0, -1.0]) state = {"x": test_vals, "y_log__": test_vals} expected_x_d2logp = expected_y_d2logp = np.eye(2) dlogps = m.compile_d2logp(jacobian=jacobian)(state) assert np.all(np.isclose(dlogps[:2, :2], expected_x_d2logp)) assert np.all(np.isclose(dlogps[2:, 2:], expected_y_d2logp)) x_dlogp2 = m.compile_d2logp(vars=[x], jacobian=jacobian)(state) assert np.all(np.isclose(x_dlogp2, expected_x_d2logp)) y_dlogp2 = m.compile_d2logp(vars=[y], jacobian=jacobian)(state) assert np.all(np.isclose(y_dlogp2, expected_y_d2logp))
def test_rvs_to_value_vars_nested(): # Test that calling rvs_to_value_vars in models with nested transformations # does not change the original rvs in place. See issue #5172 with pm.Model() as m: one = pm.LogNormal("one", mu=0) two = pm.LogNormal("two", mu=at.log(one)) # We add potentials or deterministics that are not in topological order pm.Potential("two_pot", two) pm.Potential("one_pot", one) before = aesara.clone_replace(m.free_RVs) # This call would change the model free_RVs in place in #5172 res, _ = rvs_to_value_vars(m.potentials, apply_transforms=True) after = aesara.clone_replace(m.free_RVs) assert equal_computations(before, after)
def test_check_bounds_flag(): """Test that CheckParameterValue Ops are replaced or removed when using compile_pymc""" logp = at.ones(3) cond = np.array([1, 0, 1]) bound = check_parameters(logp, cond) with pm.Model() as m: pass with pytest.raises(ParameterValueError): aesara.function([], bound)() m.check_bounds = False with m: assert np.all(compile_pymc([], bound)() == 1) m.check_bounds = True with m: assert np.all(compile_pymc([], bound)() == -np.inf)
def test_discrete_rounding_proposal(self): """ Test that discrete variable values are automatically rounded in SMC logp functions """ with pm.Model() as m: z = pm.Bernoulli("z", p=0.7) like = pm.Potential("like", z * 1.0) smc = IMH(model=m) smc.initialize_population() smc._initialize_kernel() assert smc.prior_logp_func(floatX(np.array([-0.51]))) == -np.inf assert np.isclose(smc.prior_logp_func(floatX(np.array([-0.49]))), np.log(0.3)) assert np.isclose(smc.prior_logp_func(floatX(np.array([0.49]))), np.log(0.3)) assert np.isclose(smc.prior_logp_func(floatX(np.array([0.51]))), np.log(0.7)) assert smc.prior_logp_func(floatX(np.array([1.51]))) == -np.inf
def main(): N = 100 p = pm.Uniform("freq_cheating", 0, 1) true_answers = pm.Bernoulli("truths", p, size=N) first_coin_flips = pm.Bernoulli("first_flips", 0.5, size=N) second_coin_flips = pm.Bernoulli("second_flips", 0.5, size=N) @pm.deterministic def observed_proportion(t_a=true_answers, fc=first_coin_flips, sc=second_coin_flips): result = t_a & fc | ~fc & sc return float(sum(result)) / len(result) X = 35 observations = pm.Binomial("obs", N, observed_proportion, value=X, observed=True) model = pm.Model([ p, true_answers, first_coin_flips, second_coin_flips, observed_proportion, observations ]) # To be explained in Chapter 3! mcmc = pm.MCMC(model) mcmc.sample(40000, 15000) figsize(12.5, 3) p_trace = mcmc.trace("freq_cheating")[:] plt.hist(p_trace, histtype="stepfilled", normed=True, alpha=0.85, bins=30, label="posterior distribution", color="#348ABD") plt.vlines([.05, .35], [0, 0], [5, 5], alpha=0.3) plt.xlim(0, 1) plt.legend() plt.show()
def setup_method(self): X = np.random.randn(50, 3) y = np.random.randn(50) Xnew = np.random.randn(60, 3) pnew = np.random.randn(60) with pm.Model() as model: cov_func = pm.gp.cov.ExpQuad(3, [0.1, 0.2, 0.3]) mean_func = pm.gp.mean.Constant(0.5) gp = pm.gp.Marginal(mean_func=mean_func, cov_func=cov_func) sigma = 0.1 f = gp.marginal_likelihood("f", X, y, noise=sigma) p = gp.conditional("p", Xnew) self.logp = model.logp({"p": pnew}) self.X = X self.Xnew = Xnew self.y = y self.sigma = sigma self.pnew = pnew self.gp = gp
def test_simultaneous_size_and_dims(self, with_dims_ellipsis): with pm.Model() as pmodel: x = pm.ConstantData("x", [1, 2, 3], dims="ddata") assert "ddata" in pmodel.dim_lengths # Size does not include support dims, so this test must use a dist with support dims. kwargs = dict(name="y", size=(2, 3), mu=at.ones((3, 4)), cov=at.eye(4)) if with_dims_ellipsis: y = pm.MvNormal(**kwargs, dims=("dsize", ...)) assert pmodel.RV_dims["y"] == ("dsize", None, None) else: y = pm.MvNormal(**kwargs, dims=("dsize", "ddata", "dsupport")) assert pmodel.RV_dims["y"] == ("dsize", "ddata", "dsupport") assert "dsize" in pmodel.dim_lengths assert y.eval().shape == (2, 3, 4)
def test_respects_overrides(self): with pm.Model() as pmodel: A = pm.Flat("A", initval="moment") B = pm.HalfFlat("B", initval=4) C = pm.Normal("C", mu=A + B, initval="moment") fn = make_initial_point_fn( model=pmodel, jitter_rvs={}, return_transformed=True, overrides={ A: at.as_tensor(2, dtype=int), B: 3, C: 5, }, ) iv = fn(0) assert iv["A"] == 2 assert np.isclose(iv["B_log__"], np.log(3)) assert iv["C"] == 5
def test_save_warmup_issue_1208_after_3_9(self): with pm.Model(): pm.Uniform("u1") pm.Normal("n1") trace = pm.sample( tune=100, draws=200, chains=2, cores=1, step=pm.Metropolis(), discard_tuned_samples=False, return_inferencedata=False, ) assert isinstance(trace, pm.backends.base.MultiTrace) assert len(trace) == 300 # from original trace, warmup draws should be separated out idata = to_inference_data(trace, save_warmup=True) test_dict = { "posterior": ["u1", "n1"], "sample_stats": ["~tune", "accept"], "warmup_posterior": ["u1", "n1"], "warmup_sample_stats": ["~tune", "accept"], } fails = check_multiple_attrs(test_dict, idata) assert not fails assert idata.posterior.dims["chain"] == 2 assert idata.posterior.dims["draw"] == 200 # manually sliced trace triggers the same warning as <=3.8 with pytest.warns(UserWarning, match="Warmup samples"): idata = to_inference_data(trace[-30:], save_warmup=True) test_dict = { "posterior": ["u1", "n1"], "sample_stats": ["~tune", "accept"], "~warmup_posterior": [], "~warmup_sample_stats": [], } fails = check_multiple_attrs(test_dict, idata) assert not fails assert idata.posterior.dims["chain"] == 2 assert idata.posterior.dims["draw"] == 30
def fridge(self, parameter, iterations=10000, **kwargs): if 1: import mock import sys # mock pymc.ZeroProbability as this is the only direct import of pymc that Bayesianfridge makes sys.modules.update((mod_name, mock.MagicMock()) for mod_name in ["pymc", "pymc.ZeroProbability"]) from bayesianfridge import sample # we create our own model mimicking a pymc model model = Model(parameter+self.additional_parameters, self.getLogProbability) else: import pymc from bayesianfridge import sample param_dict = {str(p): p for p in parameter} additional_param_dict = {str(p): p for p in self.additional_parameters} @ pymc.observed def Ylike(value=1, param_dict=param_dict, additional_param_dict=additional_param_dict): self.parameters.set_fit_parameters(param_dict.keys(), param_dict.values()) return self._getLogProbability_raw() model = pymc.Model(parameter + self.additional_parameters + [Ylike]) samples, marglike = sample(model, int(iterations), **kwargs) columns = [p.__name__ for p in parameter + self.additional_parameters] data = np.array([samples[c] for c in columns]).T probability = [] import tqdm for values in tqdm.tqdm(data): self.parameters.set_fit_parameters(columns, values) logprob = self.getLogProbability() probability.append(logprob) trace = pd.DataFrame(np.hstack((data, np.array(probability)[:, None])), columns=columns + ["probability"]) self.set_trace(trace) self.set_to_mean() return trace
def sample(self, model, mapstart=False, step_methods=None, iters=80000, burn=20000, num_chains=4, doplot=True, showplots=False, force=False, progress_bar=False): print('MCMC for %s' % self.name) if self.is_done(): if not force: print('\tAlready done, skipping...') return self.db_file else: print( '\tWARNING: recomputing, there might be spurious files from previous runs...' ) # Not a good idea # Let's graph the model graph = pymc.graph.dag(pymc.Model(model), name=self.name, path=self.model_dir) graph.write_png(op.join(self.model_dir, self.name + '.png')) start = time.time() if mapstart: # See http://stronginference.com/post/burn-in-and-other-mcmc-folklore # BUT WARNING, WOULD THIS MAKE MULTIPLE CHAIN START BE OVERLY CORRELATED? try: from pymc import MAP print('\tFinding MAP estimates...') M = MAP(model) M.fit() model = M.variables print('\tMAP estimates found...') except Exception, e: print('\tMAP Failed...', str(e))
def test_multiobservedrv_to_observed_data(self, multiobs): # fake regression data, with weights (W) np.random.seed(2019) N = 100 X = np.random.uniform(size=N) W = 1 + np.random.poisson(size=N) a, b = 5, 17 Y = a + np.random.normal(b * X) with pm.Model(): a = pm.Normal("a", 0, 10) b = pm.Normal("b", 0, 10) mu = a + b * X sigma = pm.HalfNormal("sigma", 1) w = W def weighted_normal(value, mu, sigma, w): return w * pm.Normal.logp(value, mu, sigma) y_logp = pm.DensityDist( # pylint: disable=unused-variable "y_logp", mu, sigma, w, logp=weighted_normal, observed=Y, size=N) idata = pm.sample(20, tune=20, return_inferencedata=True, idata_kwargs={"density_dist_obs": multiobs}) multiobs_str = "" if multiobs else "~" test_dict = { "posterior": ["a", "b", "sigma"], "sample_stats": ["lp"], "log_likelihood": ["y_logp"], f"{multiobs_str}observed_data": ["y", "w"], } fails = check_multiple_attrs(test_dict, idata) assert not fails if multiobs: assert idata.observed_data.y.dtype.kind == "f"