@@ -16,24 +16,61 @@ def std_from_equipment(tolerance=0.1, probability=0.95):
1616def transform_linear_map (operator , data , std ):
1717 """
1818 Takes a linear map `operator` of size (len(data), dim_input)
19+ or (1, dim_input) for repeated observations, along with
20+ a vector `data` representing observations. It is assumed
21+ that `data` is formed with `M@truth + sigma` where `sigma ~ N(0, std)`
22+
23+ This then transforms it to the MWE form expected by the DCI framework.
24+ It returns a matrix `A` of shape (1, dim_input) and np.float `b`
1925 and transforms it to the MWE form expected by the DCI framework.
26+
27+ >>> X = np.ones((10, 2))
28+ >>> x = np.array([0.5, 0.5]).reshape(-1, 1)
29+ >>> std = 1
30+ >>> d = X @ x
31+ >>> A, b = transform_linear_map(X, d, std)
32+ >>> np.linalg.norm(A @ x + b)
33+ 0.0
34+ >>> A, b = transform_linear_map(X, d, [std]*10)
35+ >>> np.linalg.norm(A @ x + b)
36+ 0.0
37+ >>> A, b = transform_linear_map(np.array([[1, 1]]), d, std)
38+ >>> np.linalg.norm(A @ x + b)
39+ 0.0
40+ >>> A, b = transform_linear_map(np.array([[1, 1]]), d, [std]*10)
41+ Traceback (most recent call last):
42+ ...
43+ ValueError: For repeated measurements, pass a float for std
2044 """
21- num_observations = len (data )
22- assert operator .shape [0 ] == num_observations , "Operator shape mismatch"
23- if isinstance (std , int ) or isinstance (std , float ):
24- std = np .array ([std ] * num_observations )
25- if isinstance (std , list ) or isinstance (std , tuple ):
26- std = np .array (std )
2745 if isinstance (data , np .ndarray ):
28- data = list (data .ravel ())
29- assert len (std ) == num_observations , "Standard deviation shape mismatch"
30- D = np .diag (1.0 / (std * np .sqrt (num_observations )))
31- A = np .sum (D @ operator , axis = 0 )
32- b = np .sum (np .divide (data , std ))
33- return A , (- 1.0 / np .sqrt (num_observations )) * b .reshape (- 1 , 1 )
46+ data = data .ravel ()
47+
48+ num_observations = len (data )
49+
50+ if operator .shape [0 ] > 1 : # if not repeated observations
51+ assert operator .shape [0 ] == num_observations , \
52+ f"Operator shape mismatch, op={ operator .shape } , obs={ num_observations } "
53+ if isinstance (std , (float , int )):
54+ std = np .array ([std ] * num_observations )
55+ if isinstance (std , (list , tuple )):
56+ std = np .array (std )
57+ assert len (std ) == num_observations , "Standard deviation shape mismatch"
58+ assert 0 not in np .round (std , 14 ), "Std must be > 1E-14"
59+ D = np .diag (1.0 / (std * np .sqrt (num_observations )))
60+ A = np .sum (D @ operator , axis = 0 )
61+ else :
62+ if isinstance (std , (list , tuple , np .ndarray )):
63+ raise ValueError ("For repeated measurements, pass a float for std" )
64+ assert std > 1E-14 , "Std must be > 1E-14"
65+ A = np .sqrt (num_observations ) / std * operator
66+
67+ b = - 1.0 / np .sqrt (num_observations ) * np .sum (np .divide (data , std ))
68+ return A , b
3469
3570
3671def transform_linear_setup (operator_list , data_list , std_list ):
72+ if isinstance (std_list , (float , int )):
73+ std_list = [std_list ] * len (data_list )
3774 # repeat process for multiple quantities of interest
3875 results = [transform_linear_map (o , d , s ) for
3976 o , d , s in zip (operator_list , data_list , std_list )]
0 commit comments