<jupyter_start><jupyter_text>Composite priorsA common usecase is to have a multidimensional prior with different distributions in each dimension. We achieve this with scipy and torch distributions.<jupyter_code>%load_ext autoreload
%autoreload 2
import torch
import scipy.stats
import numpy as np

import pylab as plt

from toolz import compose
from swyft.prior import Prior, PriorTruncator
from swyft.utils import tensor_to_array, array_to_tensor<jupyter_output><empty_output><jupyter_text>Here we define our priors. The `parameter_dimensions` variable is a list of integers which define the dimensionality of each random variable within the composite.<jupyter_code>loc = torch.tensor([1.0, -3.2])
scale = torch.tensor([0.1, 2.1])

upper = torch.tensor([2.5, 3.1])
lower = torch.tensor([0.1, -2.0])

parameter_dimensions = [len(loc), len(upper)]
n_parameters = sum(parameter_dimensions)<jupyter_output><empty_output><jupyter_text>SciPy DistributionsSciPy distributions are simple to use. Put the relevant functions in a list and define the `parameter_dimensions` value.<jupyter_code>normal_scipy = scipy.stats.norm(loc, scale)
uniform_scipy = scipy.stats.uniform(lower, upper)

composite_prior_scipy = Prior.composite_prior(
    cdfs=[normal_scipy.cdf, uniform_scipy.cdf],
    icdfs=[normal_scipy.ppf, uniform_scipy.ppf],
    log_probs=[normal_scipy.logpdf, uniform_scipy.logpdf],
    parameter_dimensions=parameter_dimensions,
)
samples_scipy = PriorTruncator(composite_prior_scipy, bound=None).sample(10_000)
for i in range(n_parameters):
    _ = plt.hist(samples_scipy[:, i], bins=100, alpha=0.5)<jupyter_output><empty_output><jupyter_text>Torch DistributionsThis requires the application of `Prior.conjugate_tensor_func` to convert the numpy arrays to tensors then the output tensors back to numpy arrays. The prior samples and simulations are usually saved as numpy data within swyft, so we must convert to use a pytorch distribution.We apply `Prior.conjugate_tensor_func` with a functional idiom.<jupyter_code>normal = torch.distributions.Normal(loc, scale)
uniform = torch.distributions.Uniform(lower, upper)

composite_prior_torch = Prior.composite_prior(
    cdfs=list(map(Prior.conjugate_tensor_func, [normal.cdf, uniform.cdf])),
    icdfs=list(map(Prior.conjugate_tensor_func, [normal.icdf, uniform.icdf])),
    log_probs=list(map(Prior.conjugate_tensor_func, [normal.log_prob, uniform.log_prob])),
    parameter_dimensions=parameter_dimensions,
)
samples = PriorTruncator(composite_prior_torch, bound=None).sample(10_000)
for i in range(n_parameters):
    _ = plt.hist(samples[:, i], bins=100, alpha=0.5)<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.09595768794862108, 'fraction_numerical': 0.013978088401964487, 'mean_word_length': 6.857566765578635, 'pattern_counts': {'":': 0, '<': 17, '<?xml version=': 0, '>': 17, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '4', 'dir': 'jupyter-structured-clean-dedup', 'id': '129967', 'license': 'MIT', 'n_tokens_mistral': 918, 'n_tokens_neox': 878, 'n_words': 212, 'path': 'notebooks/Examples - 7. Composite Priors.ipynb', 'repo_name': 'cweniger/swyft', 'size': '2647'}