|
| 1 | +""" |
| 2 | +Quantity examples and quick reference. |
| 3 | +
|
| 4 | +This module demonstrates basic operations with mlmc Quantity objects. |
| 5 | +It shows how to: |
| 6 | + - create a synthetic root quantity (result format specified below), |
| 7 | + - compute mean estimates, |
| 8 | + - estimate moments and covariance, |
| 9 | + - select sub-quantities, time-interpolate and slice, |
| 10 | + - perform arithmetic and NumPy ufuncs with Quantity objects, |
| 11 | + - perform selections using conditions and masks. |
| 12 | +
|
| 13 | +Result format used in the synthetic example: |
| 14 | +
|
| 15 | +result_format = [ |
| 16 | + mlmc.quantity.quantity_spec.QuantitySpec( |
| 17 | + name="length", unit="m", shape=(2, 1), times=[1, 2, 3], locations=['10', '20'] |
| 18 | + ), |
| 19 | + mlmc.quantity.quantity_spec.QuantitySpec( |
| 20 | + name="width", unit="mm", shape=(2, 1), times=[1, 2, 3], locations=['30', '40'] |
| 21 | + ), |
| 22 | +] |
| 23 | +
|
| 24 | +Meaning: |
| 25 | + - sample results contain data on two quantities ("length" and "width"), |
| 26 | + - each quantity is evaluated at three times [1,2,3] and two locations, |
| 27 | + - each quantity can also have its own internal shape. |
| 28 | +""" |
1 | 29 | import numpy as np |
2 | 30 | import mlmc.quantity.quantity_spec |
3 | 31 | from mlmc.quantity.quantity import make_root_quantity |
4 | 32 | import mlmc.quantity.quantity_estimate |
5 | 33 | from examples.synthetic_quantity import create_sampler |
6 | 34 |
|
7 | | -# An overview of basic Quantity operations |
8 | | - |
9 | | -###################################### |
10 | | -### Create synthetic quantity ### |
11 | | -###################################### |
12 | | -# Synthetic Quantity with the following result format |
13 | | -# result_format = [ |
14 | | -# mlmc.quantity.quantity_spec.QuantitySpec(name="length", unit="m", shape=(2, 1), times=[1, 2, 3], locations=['10', '20']), |
15 | | -# mlmc.quantity.quantity_spec.QuantitySpec(name="width", unit="mm", shape=(2, 1), times=[1, 2, 3], locations=['30', '40']), |
16 | | -# ] |
17 | | -# Meaning: sample results contain data on two quantities in three time steps [1, 2, 3] and in two locations, |
18 | | -# each quantity can have different shape |
| 35 | +# ----------------------------------------------------------------------------- |
| 36 | +# Create synthetic quantity |
| 37 | +# ----------------------------------------------------------------------------- |
| 38 | +""" |
| 39 | +Create a synthetic sampler + factory and produce a root Quantity instance. |
19 | 40 |
|
| 41 | +- create_sampler() returns: (sampler, simulation_factory, moments_fn) |
| 42 | +- make_root_quantity(storage, q_specs) builds a Quantity object that represents the |
| 43 | + whole result data structure (root with named sub-quantities). |
| 44 | +""" |
20 | 45 | sampler, simulation_factory, moments_fn = create_sampler() |
21 | 46 | root_quantity = make_root_quantity(sampler.sample_storage, simulation_factory.result_format()) |
22 | | -# root_quantity is mlmc.quantity.quantity.Quantity instance and represents the whole result data, |
| 47 | +# root_quantity is an mlmc.quantity.quantity.Quantity instance and represents the whole result data, |
23 | 48 | # it contains two sub-quantities named "length" and "width" |
24 | 49 |
|
25 | | -################################### |
26 | | -#### Mean estimates ##### |
27 | | -################################### |
28 | | -# To get estimated mean of a quantity: |
| 50 | +# ----------------------------------------------------------------------------- |
| 51 | +# Mean estimates |
| 52 | +# ----------------------------------------------------------------------------- |
| 53 | +""" |
| 54 | +Compute and inspect mean estimates for a Quantity. |
| 55 | +
|
| 56 | +- estimate_mean(quantity) returns a QuantityMean instance that contains: |
| 57 | + - .mean : mean Quantity |
| 58 | + - .var : variance information |
| 59 | + - .l_vars : level variances (if available) |
| 60 | +""" |
29 | 61 | root_quantity_mean = mlmc.quantity.quantity_estimate.estimate_mean(root_quantity) |
30 | 62 | # root_quantity_mean is an instance of mlmc.quantity.QuantityMean |
31 | 63 | # To get overall mean value: |
|
35 | 67 | # To get level variance value: |
36 | 68 | root_quantity_mean.l_vars |
37 | 69 |
|
38 | | -######################################################### |
39 | | -#### Estimate moments and covariance matrix ##### |
40 | | -######################################################### |
41 | | -# Create a quantity representing moments |
| 70 | +# ----------------------------------------------------------------------------- |
| 71 | +# Estimate moments and covariance matrix |
| 72 | +# ----------------------------------------------------------------------------- |
| 73 | +""" |
| 74 | +Construct moments and covariance quantities from a root Quantity. |
| 75 | +
|
| 76 | +- moments(root_quantity, moments_fn=moments_fn) returns a Quantity of moments. |
| 77 | +- estimate_mean(moments_quantity) computes means for those moments. |
| 78 | +- covariance(root_quantity, moments_fn=moments_fn) returns a Quantity describing covariance. |
| 79 | +""" |
42 | 80 | moments_quantity = mlmc.quantity.quantity_estimate.moments(root_quantity, moments_fn=moments_fn) |
43 | 81 | moments_mean = mlmc.quantity.quantity_estimate.estimate_mean(moments_quantity) |
44 | 82 | # Central moments: |
45 | 83 | central_root_quantity = root_quantity - root_quantity_mean.mean |
46 | | -central_moments_quantity = mlmc.quantity.quantity_estimate.moments(central_root_quantity, moments_fn=moments_fn) |
| 84 | +central_moments_quantity = mlmc.quantity.quantity_estimate.moments( |
| 85 | + central_root_quantity, moments_fn=moments_fn |
| 86 | +) |
47 | 87 | central_moments_mean = mlmc.quantity.quantity_estimate.estimate_mean(central_moments_quantity) |
48 | 88 | # Create a quantity representing covariance matrix |
49 | 89 | covariance_quantity = mlmc.quantity.quantity_estimate.covariance(root_quantity, moments_fn=moments_fn) |
50 | 90 | cov_mean = mlmc.quantity.quantity_estimate.estimate_mean(covariance_quantity) |
51 | 91 |
|
52 | | -# Both moments() and covariance() calls return mlmc.quantity.quantity.Quantity instance |
| 92 | +# Both moments() and covariance() calls return mlmc.quantity.quantity.Quantity instances |
53 | 93 |
|
54 | | -################################## |
55 | | -### Quantity selection #### |
56 | | -################################## |
57 | | -# According to the result_format, tt is possible to select items from a quantity |
| 94 | +# ----------------------------------------------------------------------------- |
| 95 | +# Quantity selection |
| 96 | +# ----------------------------------------------------------------------------- |
| 97 | +""" |
| 98 | +Examples of indexing and time/ location selection on a Quantity. |
| 99 | +
|
| 100 | +According to the result_format you can select by name, then time, then location. |
| 101 | +Selecting location before time is not supported. |
| 102 | +""" |
58 | 103 | length = root_quantity["length"] # Get quantity with name="length" |
59 | 104 | width = root_quantity["width"] # Get quantity with name="width" |
60 | | -# length and width are still mlmc.quantity.quantity.Quantity instances |
61 | 105 |
|
62 | | -# To get a quantity at particular time: |
| 106 | +# To get a quantity at a particular (interpolated) time: |
63 | 107 | length_locations = length.time_interpolation(2.5) |
64 | 108 | # length_locations represents results for all locations of quantity named "length" at the time 2.5 |
65 | 109 |
|
66 | | -# To get quantity at particular location |
| 110 | +# To get quantity at particular location: |
67 | 111 | length_result = length_locations['10'] |
68 | | -# length_result represents results shape=(2, 1) of quantity named "length" at the time 2,5 and location '10' |
69 | | - |
70 | | -# Now it is possible to slice Quantity length_result the same way as np.ndarray |
71 | | -# For example: |
72 | | -# length_result[1, 0] |
73 | | -# length_result[:, 0] |
74 | | -# length_result[:, :] |
75 | | -# length_result[:1, :1] |
76 | | -# length_result[:2, ...] |
77 | | - |
78 | | -# Keep in mind: |
79 | | -# - all derived quantities such as length_locations and length_result, ... are still mlmc.quantity.quantity.Quantity instances |
80 | | -# - selecting location before time is not supported! |
81 | | - |
82 | | -################################### |
83 | | -#### Binary operations ##### |
84 | | -################################### |
85 | | -# Following operations are supported |
86 | | -# Addition of compatible quantities |
| 112 | +# length_result represents shape=(2, 1) data of "length" at time 2.5 and location '10' |
| 113 | + |
| 114 | +# You can slice Quantity like an ndarray: |
| 115 | +# length_result[1, 0], length_result[:, 0], length_result[:2, ...], etc. |
| 116 | + |
| 117 | +# ----------------------------------------------------------------------------- |
| 118 | +# Binary operations |
| 119 | +# ----------------------------------------------------------------------------- |
| 120 | +""" |
| 121 | +Supported arithmetic operations between compatible Quantity objects and scalars. |
| 122 | +The result is a Quantity instance with the same result_format structure. |
| 123 | +""" |
87 | 124 | quantity = root_quantity + root_quantity |
88 | 125 | quantity = root_quantity + root_quantity + root_quantity |
89 | 126 |
|
|
96 | 133 | quantity_const_mod = root_quantity % const |
97 | 134 | quantity_add_mult = root_quantity + root_quantity * const |
98 | 135 |
|
99 | | - |
100 | | -################################### |
101 | | -#### NumPy universal functions #### |
102 | | -################################### |
103 | | -# Examples of tested NumPy universal functions: |
| 136 | +# ----------------------------------------------------------------------------- |
| 137 | +# NumPy universal functions (ufuncs) |
| 138 | +# ----------------------------------------------------------------------------- |
| 139 | +""" |
| 140 | +Many NumPy ufuncs are supported and return Quantity instances: |
| 141 | +- np.add, np.max, np.sin, np.sum, np.maximum, np.divide, np.arctan2, ... |
| 142 | +""" |
104 | 143 | quantity_np_add = np.add(root_quantity, root_quantity) |
105 | 144 | quantity_np_max = np.max(root_quantity, axis=0, keepdims=True) |
106 | 145 | quantity_np_sin = np.sin(root_quantity) |
|
112 | 151 | quantity_np_add_const = np.add(x, root_quantity) |
113 | 152 | quantity_np_arctan2_cosnt = np.arctan2(x, root_quantity) |
114 | 153 |
|
115 | | -################################################ |
116 | | -#### Quantity selection by a condition #### |
117 | | -################################################ |
118 | | -# Method select returns mlmc.quantity.quantity.Quantity instance |
| 154 | +# ----------------------------------------------------------------------------- |
| 155 | +# Quantity selection by a condition |
| 156 | +# ----------------------------------------------------------------------------- |
| 157 | +""" |
| 158 | +The select(condition) method extracts elements of a Quantity according to a boolean condition; |
| 159 | +it returns a Quantity (masking is internal and shape-aware). |
| 160 | +
|
| 161 | +Examples: |
| 162 | + - selected_quantity = root_quantity.select(0 < root_quantity) |
| 163 | + - quantity_add_select = quantity_add.select(root_quantity < quantity_add) |
| 164 | + - root_quantity_selected = root_quantity.select(-1 != root_quantity) |
| 165 | +
|
| 166 | +You can combine conditions via logical ufuncs (np.logical_or / np.logical_and), and |
| 167 | +you can explicitly pass a Quantity mask (mask is a Quantity instance). |
| 168 | +""" |
119 | 169 | selected_quantity = root_quantity.select(0 < root_quantity) |
120 | 170 |
|
121 | 171 | quantity_add = root_quantity + root_quantity |
|
125 | 175 | # Logical operation for more conditions is AND |
126 | 176 | quantity_add.select(root_quantity < quantity_add, root_quantity < 10) |
127 | 177 |
|
128 | | -# User can use one of the logical NumPy universal functions |
129 | | -selected_quantity_or = root_quantity.select(np.logical_or(0 < root_quantity, root_quantity < 10)) |
| 178 | +# Use NumPy logical ufuncs for complex masks |
| 179 | +selected_quantity_or = root_quantity.select( |
| 180 | + np.logical_or(0 < root_quantity, root_quantity < 10) |
| 181 | +) |
130 | 182 |
|
131 | | -# It is possible to explicitly define the selection condition of one quantity by another quantity |
132 | | -mask = np.logical_and(0 < root_quantity, root_quantity < 10) # mask is Quantity instance |
| 183 | +# Explicit mask Quantity |
| 184 | +mask = np.logical_and(0 < root_quantity, root_quantity < 10) # mask is a Quantity instance |
133 | 185 | q_bounded = root_quantity.select(mask) |
0 commit comments