Correlated Distributions
Occasionally, you may want to randomize multiple variables subject to some specific constraints.
When randomizing physics parameters, you may want
V_LandV_Rto be randomized subject to the constraint thatV_L == -V_R.When adding noise, you may want to randomize the strengths of two different noise types, such that their sum is always a certain value.
This can be accomplished through any of the CorrelatedDistribution classes
provided with QDFlow, or by creating a custom CorrelatedDistribution.
from qdflow.util import distribution
import numpy as np
from qdflow import generate
A CorrelatedDistribution is essentially a multivariate distribution, where
the variables are correlated in some way. A single draw from a
CorrelatedDistribution will return an array of length num_variables.
The simplest CorrelatedDistribution included in QDFlow is FullyCorrelated,
which simply returns a number of copies of the same value.
dist_single = distribution.Normal(20, 5) # A single-variable distribution
# A correlated distribution that returns 5 copies of the same value
num_variables = 5
corr_dist = distribution.FullyCorrelated(dist_single, num_variables)
rng = np.random.default_rng(seed=6)
# Draw a single sample of each of the 5 variables
single_sample = corr_dist.draw(rng)
print("Single sample: ", single_sample)
# Draw multiple samples of each variable
num_samples = 3
samples = corr_dist.draw(rng, size=num_samples)
print("Multiple samples:\n", samples)
Single sample: [25.26557877 25.26557877 25.26557877 25.26557877 25.26557877]
Multiple samples:
[[28.88245652 28.88245652 28.88245652 28.88245652 28.88245652]
[ 7.23354081 7.23354081 7.23354081 7.23354081 7.23354081]
[19.31017469 19.31017469 19.31017469 19.31017469 19.31017469]]
When performing randomization, QDFlow expects each field in the randomization class to be given a seperate distribution.
Distributions for each of the individual variables can be obtained from a
CorrelatedDistribution via the dependent_distributions() method.
# Create a correlated distribution with 2 variables
dist_single = distribution.Normal(0, 1)
num_variables = 2
corr_dist = distribution.FullyCorrelated(dist_single, num_variables)
# Obtain a list [dist_1, dist_2] of the individual distributions for each variable
individual_dists = corr_dist.dependent_distributions()
dist_1, dist_2 = individual_dists
rng = np.random.default_rng(seed=7)
# Draw a single sample from one of the individual distributions
sample_1 = dist_1.draw(rng)
print("Sample 1: ", sample_1)
# Now draw a sample from the other individual distribution
sample_2 = dist_2.draw(rng)
print("Sample 2: ", sample_2)
Sample 1: 0.0012301533574825742
Sample 2: 0.0012301533574825742
These individual distributions can then be used to specify how physics parameters should be randomized.
# Create a randomization object with default distributions for each parameter
phys_rand = generate.PhysicsRandomization.default()
# Set V_L and V_R distributions such that they will always be negative of each other.
phys_rand.V_L = 2 * dist_1
phys_rand.V_R = -2 * dist_2
# Generate a randomized set of physics parameters
generate.set_rng_seed(8)
n_devices = 6
phys_params = generate.random_physics(phys_rand, n_devices)
print("V_L: ", phys_params[0].V_L)
print("V_R: ", phys_params[0].V_R)
V_L: -0.37779439216921556
V_R: 0.37779439216921556