Quantity Tutorial
This tutorial provides an overview of basic mlmc.quantity.quantity.Quantity operations.
The mlmc.quantity module and its related classes allow you to:
- Estimate means and variances of MLMC sample results.
- Derive new quantities from existing ones.
- Perform arithmetic and NumPy-based operations on quantities.
Setup
Before exploring Quantity operations, we first set up a simple synthetic MLMC sampler.
import numpy as np
import mlmc.quantity.quantity_estimate
from examples.synthetic_quantity import create_sampler
Creating a Synthetic Quantity
We begin by creating a synthetic mlmc.quantity.quantity.Quantity with a predefined result_format:
# result_format = [
# mlmc.QuantitySpec(name="length", unit="m", shape=(2, 1), times=[1, 2, 3], locations=['10', '20']),
# mlmc.QuantitySpec(name="width", unit="mm", shape=(2, 1), times=[1, 2, 3], locations=['30', '40']),
# ]
sampler, simulation_factory, moments_fn = create_sampler()
root_quantity = mlmc.make_root_quantity(sampler.sample_storage, simulation_factory.result_format())
This means the sample results contain data for two quantities (length and width) at three time steps [1, 2, 3] and two locations each.
root_quantity is an instance of mlmc.quantity.quantity.Quantity, representing the full result data structure.
Mean Estimates
To compute the estimated mean of a quantity:
root_quantity_mean = mlmc.quantity.quantity_estimate.estimate_mean(root_quantity)
The returned object, root_quantity_mean, is a mlmc.quantity.quantity.QuantityMean instance.
To retrieve statistical values:
# Total mean and variance
root_quantity_mean.mean
root_quantity_mean.var
# Means and variances at each level
root_quantity_mean.l_means
root_quantity_mean.l_vars
Moments and Covariance Estimation
To create and estimate statistical moments:
moments_quantity = mlmc.quantity.quantity_estimate.moments(root_quantity, moments_fn=moments_fn)
moments_mean = mlmc.quantity.quantity_estimate.estimate_mean(moments_quantity)
To obtain central moments, first subtract the mean:
central_root_quantity = root_quantity - root_quantity_mean.mean
central_moments_quantity = mlmc.quantity.quantity_estimate.moments(
central_root_quantity, moments_fn=moments_fn
)
central_moments_mean = mlmc.quantity.quantity_estimate.estimate_mean(central_moments_quantity)
To estimate a covariance matrix:
covariance_quantity = mlmc.quantity.quantity_estimate.covariance(root_quantity, moments_fn=moments_fn)
cov_mean = mlmc.quantity.quantity_estimate.estimate_mean(covariance_quantity)
Quantity Selection
You can access and manipulate sub-quantities directly using the structure defined by result_format:
length = root_quantity["length"] # Get quantity with name="length"
width = root_quantity["width"] # Get quantity with name="width"
Both are still mlmc.quantity.quantity.Quantity instances.
Selecting by time:
length_locations = length.time_interpolation(2.5)
Selecting by location:
length_result = length_locations['10']
Now, length_result behaves like a NumPy array:
length_result[1, 0]
length_result[:, 0]
length_result[:, :]
length_result[:1, :1]
length_result[:2, ...]
Note
All derived quantities (like
length_locationsorlength_result) remain Quantity instances.Selecting a location before time is not supported.
Binary Operations
Quantity supports standard arithmetic operations:
Between quantities:
quantity = root_quantity + root_quantity
quantity = root_quantity + root_quantity + root_quantity
With constants:
const = 5
quantity_const_add = root_quantity + const
quantity_const_sub = root_quantity - const
quantity_const_mult = root_quantity * const
quantity_const_div = root_quantity / const
quantity_const_mod = root_quantity % const
quantity_add_mult = root_quantity + root_quantity * const
NumPy Universal Functions
Quantity objects are compatible with many NumPy universal functions (ufuncs):
quantity_np_add = np.add(root_quantity, root_quantity)
quantity_np_max = np.max(root_quantity, axis=0, keepdims=True)
quantity_np_sin = np.sin(root_quantity)
quantity_np_sum = np.sum(root_quantity, axis=0, keepdims=True)
quantity_np_maximum = np.maximum(root_quantity, root_quantity)
x = np.ones(24)
quantity_np_divide_const = np.divide(x, root_quantity)
quantity_np_add_const = np.add(x, root_quantity)
quantity_np_arctan2_const = np.arctan2(x, root_quantity)
Conditional Selection
You can select parts of a quantity using logical conditions via the select() method.
selected_quantity = root_quantity.select(0 < root_quantity)
Or using comparisons between quantities:
quantity_add = root_quantity + root_quantity
quantity_add_select = quantity_add.select(root_quantity < quantity_add)
root_quantity_selected = root_quantity.select(-1 != root_quantity)
Multiple conditions are combined using logical AND:
quantity_add.select(root_quantity < quantity_add, root_quantity < 10)
Use NumPy logical functions for more complex conditions:
selected_quantity_or = root_quantity.select(np.logical_or(0 < root_quantity, root_quantity < 10))
You can also explicitly define the selection mask:
mask = np.logical_and(0 < root_quantity, root_quantity < 10)
q_bounded = root_quantity.select(mask)