fetish_lib/
test_utils.rs

1extern crate ndarray;
2extern crate ndarray_linalg;
3
4use ndarray::*;
5use crate::context::*;
6use crate::space_info::*;
7use crate::term_model::*;
8use crate::type_id::*;
9use crate::data_point::*;
10use crate::schmear::*;
11use crate::func_schmear::*;
12use crate::inverse_schmear::*;
13use crate::params::*;
14use rand::prelude::*;
15use ndarray_linalg::*;
16use ndarray_rand::RandomExt;
17use ndarray_rand::rand_distr::StandardNormal;
18use crate::func_scatter_tensor::*;
19use crate::model::*;
20use crate::normal_inverse_wishart::*;
21use crate::term_reference::*;
22use crate::prior_specification::*;
23use crate::array_utils::*;
24use crate::feature_space_info::*;
25use crate::feature_collection::*;
26use crate::fourier_feature_collection::*;
27use crate::sketched_linear_feature_collection::*;
28use crate::primitive_directory::*;
29use crate::rand_utils::*;
30
31///A collection of crate-internal utilities for constructing tests.
32
33pub const TEST_VECTOR_T : TypeId = 1 as TypeId;
34pub const TEST_SCALAR_T : TypeId = 0 as TypeId;
35pub const TEST_VECTOR_SIZE : usize = 2;
36
37fn get_test_vector_only_type_info_directory() -> TypeInfoDirectory {
38    let mut result = TypeInfoDirectory::new();
39    result.add(Type::VecType(1));
40    result.add(Type::VecType(TEST_VECTOR_SIZE));
41    result
42}
43
44pub fn get_test_vector_only_feature_space_info(base_dimensions : usize) -> FeatureSpaceInfo {
45    let sketcher = Option::None; 
46    let mut feature_collections = Vec::<Box<dyn FeatureCollection>>::new();
47    let fourier_feature_collection = FourierFeatureCollection::new(base_dimensions, base_dimensions * 2, 1.0f32,
48                                                          gen_nsphere_random);
49    let sketched_linear_feature_collection = SketchedLinearFeatureCollection::new(base_dimensions, base_dimensions * 2, 1.0f32);
50    feature_collections.push(Box::new(fourier_feature_collection));
51    feature_collections.push(Box::new(sketched_linear_feature_collection));
52
53    let feature_dimensions = get_total_feat_dims(&feature_collections);
54
55    FeatureSpaceInfo {
56        base_dimensions,
57        feature_dimensions,
58        feature_collections,
59        sketcher
60    }
61}
62
63fn get_test_vector_only_space_info_directory() -> SpaceInfoDirectory {
64    let mut feature_spaces = Vec::new();
65
66    feature_spaces.push(get_test_vector_only_feature_space_info(1));
67    feature_spaces.push(get_test_vector_only_feature_space_info(TEST_VECTOR_SIZE));
68
69    SpaceInfoDirectory {
70        feature_spaces
71    }
72}
73
74pub fn get_test_vector_only_context() -> Context {
75    let type_info_directory = get_test_vector_only_type_info_directory();
76    let space_info_directory = get_test_vector_only_space_info_directory();
77    let primitive_directory = PrimitiveDirectory::new(&type_info_directory);
78    Context {
79        type_info_directory,
80        space_info_directory,
81        primitive_directory
82    }
83}
84
85pub fn random_scalar() -> f32 {
86    let mut rng = rand::thread_rng();
87    let result : f32 = rng.gen();
88    result
89}
90
91pub fn random_data_point(in_dimensions : usize, out_dimensions : usize) -> DataPoint {
92    let in_vec = random_vector(in_dimensions);
93    let out_vec = random_vector(out_dimensions);
94
95    let mut rng = rand::thread_rng();
96    let weight_sqrt : f32 = rng.gen();
97    let weight = weight_sqrt * weight_sqrt;
98    
99    DataPoint {
100        in_vec,
101        out_vec,
102        weight
103    }
104}
105
106pub fn standard_normal_inverse_wishart(feature_dimensions : usize, out_dimensions : usize) -> NormalInverseWishart {
107    let mean = Array::zeros((out_dimensions, feature_dimensions));
108    
109    let in_precision = Array::eye(feature_dimensions);
110    let out_precision = Array::eye(out_dimensions);
111    let little_v = (out_dimensions as f32) + 2.0f32;
112
113    NormalInverseWishart::new(mean, in_precision, out_precision, little_v)
114}
115
116pub fn random_normal_inverse_wishart(feature_dimensions : usize, out_dimensions : usize) -> NormalInverseWishart {
117    let mean = random_matrix(out_dimensions, feature_dimensions);
118    let precision = random_psd_matrix(feature_dimensions);
119    let big_v = random_psd_matrix(out_dimensions);
120    let little_v = (out_dimensions as f32) + 4.0f32;
121
122    NormalInverseWishart::new(mean, precision, big_v, little_v)
123}
124//Yields a pair of models (func, arg), of type (in_dimensions -> middle_dimensions) ->
125//out_dimensions
126pub fn random_model_app<'a>(ctxt : &'a Context, func_type_id : TypeId) -> (Model<'a>, Model<'a>) {
127    let arg_type_id = ctxt.get_arg_type_id(func_type_id);
128
129    let in_type_id = ctxt.get_arg_type_id(arg_type_id);
130    let middle_type_id = ctxt.get_ret_type_id(arg_type_id);
131
132    let ret_type_id = ctxt.get_ret_type_id(func_type_id);
133    let arg_model = random_model(ctxt, in_type_id, middle_type_id);
134    let func_model = random_model(ctxt, arg_type_id, ret_type_id);
135    (func_model, arg_model)
136}
137
138pub struct TestPriorSpecification { }
139impl PriorSpecification for TestPriorSpecification {
140    fn get_in_precision_multiplier(&self, _feat_dims : usize) -> f32 {
141        1.0f32
142    }
143    fn get_out_covariance_multiplier(&self, _out_dims : usize) -> f32 {
144        1.0f32
145    }
146    fn get_out_pseudo_observations(&self, out_dims : usize) -> f32 {
147        (out_dims as f32) + 4.0f32
148    }
149}
150
151pub fn random_model<'a>(ctxt : &'a Context, arg_type_id : TypeId, ret_type_id : TypeId) -> Model {
152    let prior_specification = TestPriorSpecification { };
153
154    let mut result = Model::new(&prior_specification, arg_type_id, ret_type_id, ctxt);
155    let arg_feat_space_info = ctxt.get_feature_space_info(arg_type_id);
156    let ret_feat_space_info = ctxt.get_feature_space_info(ret_type_id);
157    result.data = random_normal_inverse_wishart(arg_feat_space_info.feature_dimensions, 
158                                                ret_feat_space_info.base_dimensions);
159    result 
160}
161
162pub fn assert_equal_schmears(one : &Schmear, two : &Schmear) {
163    assert_equal_matrices(one.covariance.view(), two.covariance.view());
164    assert_equal_vectors(one.mean.view(), two.mean.view());
165}
166
167pub fn assert_equal_inv_schmears(one : &InverseSchmear, two : &InverseSchmear) {
168    assert_equal_matrices(one.precision.view(), two.precision.view());
169    assert_equal_vectors(one.mean.view(), two.mean.view());
170}
171
172pub fn relative_frob_norm_error(actual : ArrayView2<f32>, expected : ArrayView2<f32>) -> f32 {
173    let denominator = expected.opnorm_fro().unwrap();
174    let diff = &actual - &expected;
175    let diff_norm = diff.opnorm_fro().unwrap();
176    diff_norm / denominator
177}
178
179pub fn are_equal_matrices_to_within(one : ArrayView2<f32>, two : ArrayView2<f32>, within : f32, print : bool) -> bool {
180    let diff = &one - &two;
181    let frob_norm = diff.opnorm_fro().unwrap();
182    if (frob_norm > within) {
183        if (print) {
184            println!("Actual: {}", one);
185            println!("Expected: {}", two);
186            println!("Diff: {}", diff);
187            println!("Frob norm: {}", frob_norm);
188        }
189        false
190    } else {
191        true
192    }
193}
194
195pub fn assert_equal_distributions_to_within(one : &NormalInverseWishart, two : &NormalInverseWishart, within : f32) {
196    assert_equal_matrices_to_within(one.mean.view(), two.mean.view(), within);
197    assert_equal_matrices_to_within(one.precision.view(), two.precision.view(), within);
198    assert_equal_matrices_to_within(one.big_v.view(), two.big_v.view(), within);
199    assert_eps_equals_to_within(one.little_v, two.little_v, within);
200}
201
202pub fn assert_equal_matrices_to_within(one : ArrayView2<f32>, two : ArrayView2<f32>, within : f32) {
203    if (!are_equal_matrices_to_within(one, two, within, true)) {
204        panic!();
205    }
206}
207
208pub fn assert_equal_matrices(one : ArrayView2<f32>, two : ArrayView2<f32>) {
209    assert_equal_matrices_to_within(one, two, DEFAULT_TEST_THRESH);
210}
211
212pub fn are_equal_vectors_to_within(one : ArrayView1<f32>, two : ArrayView1<f32>, within : f32, print : bool) -> bool {
213    let diff = &one - &two;
214    let sq_norm = diff.dot(&diff);
215    let norm = sq_norm.sqrt();
216    if (norm > within) {
217        if (print) {
218            println!("Actual: {}", one);
219            println!("Expected: {}", two);
220            println!("Diff: {}", diff);
221            println!("Norm: {}", norm);
222        }
223        false
224    } else {
225        true
226    }
227}
228
229pub fn assert_equal_vectors_to_within(one : ArrayView1<f32>, two : ArrayView1<f32>, within : f32) {
230    if(!are_equal_vectors_to_within(one, two, within, true)) {
231        panic!();
232    }
233}
234
235pub fn assert_equal_vector_term(actual : TermReference, expected : ArrayView1<f32>) {
236    if let TermReference::VecRef(_, vec) = actual {
237        assert_equal_vectors(from_noisy(vec.view()).view(), expected);
238    } else {
239        panic!();
240    }
241}
242
243pub fn assert_equal_vectors(one : ArrayView1<f32>, two : ArrayView1<f32>) {
244    assert_equal_vectors_to_within(one, two, DEFAULT_TEST_THRESH);
245}
246
247pub fn assert_eps_equals_to_within(one : f32, two : f32, epsilon : f32) {
248    let diff = one - two;
249    if (diff.abs() > epsilon) {
250        println!("Actual: {} Expected: {}", one, two);
251        panic!();
252    }
253}
254
255pub fn assert_eps_equals(one : f32, two : f32) {
256    assert_eps_equals_to_within(one, two, DEFAULT_TEST_THRESH);
257}
258pub fn assert_greater(one : f32, two : f32) {
259    if (two >= one) {
260        println!("{} is greater than {}", two, one);
261        panic!();
262    }
263}
264pub fn random_vector(t : usize) -> Array1<f32> {
265    Array::random((t,), StandardNormal)
266}
267pub fn random_matrix(t : usize, s : usize) -> Array2<f32> {
268    Array::random((t, s), StandardNormal)
269}
270pub fn random_diag_matrix(t : usize) -> Array2<f32> {
271    let mut result = Array::zeros((t, t));
272    let diag = Array::random((t,), StandardNormal);
273    for i in 0..t {
274        result[[i, i]] = diag[[i,]];
275    }
276    result
277}
278pub fn random_psd_matrix(t : usize) -> Array2<f32> {
279    let mut result = Array::zeros((t, t));
280    for _ in 0..t {
281        let matrix_sqrt = random_matrix(t, t);
282        let matrix = matrix_sqrt.t().dot(&matrix_sqrt);
283        result += &matrix;
284    }
285    result
286}
287
288pub fn random_schmear(t : usize) -> Schmear {
289    let covariance = random_psd_matrix(t);
290    let mean = random_vector(t);
291    Schmear {
292        mean,
293        covariance
294    }
295}
296pub fn random_inv_schmear(t : usize) -> InverseSchmear {
297    let precision = random_psd_matrix(t);
298    let mean = random_vector(t);
299    InverseSchmear {
300        mean,
301        precision
302    }
303}
304
305pub fn random_func_scatter_tensor(t : usize, s : usize) -> FuncScatterTensor {
306    let in_scatter = random_psd_matrix(s);
307    let out_scatter = random_psd_matrix(t);
308    FuncScatterTensor {
309        in_scatter,
310        out_scatter
311    }
312}
313
314pub fn random_func_schmear(t : usize, s : usize) -> FuncSchmear {
315    let scatter_tensor = random_func_scatter_tensor(t, s);
316    let mean = random_matrix(t, s);
317    let result = FuncSchmear {
318        mean : mean,
319        covariance : scatter_tensor
320    };
321    result
322}
323
324pub fn empirical_gradient<F>(f : F, x : ArrayView1<f32>) -> Array1<f32>
325    where F : Fn(ArrayView1<f32>) -> f32 {
326
327    let epsilon = 0.001f32;
328    let y = f(x);
329
330    let s = x.shape()[0];
331
332    let mut result = Array::zeros((s,));
333    for i in 0..s {
334        let mut delta_x : Array1<f32> = Array::zeros((s,));
335        delta_x[[i,]] = epsilon;
336
337        let new_x = &x + &delta_x;
338        let new_y = f(new_x.view());
339        let delta_y = new_y - y;
340
341        let grad = delta_y / epsilon;
342
343        result[[i,]] = grad;
344    }
345    result
346}
347
348pub fn empirical_jacobian<F>(f : F, x : ArrayView1<f32>) -> Array2<f32> 
349    where F : Fn(ArrayView1<f32>) -> Array1<f32> {
350    let epsilon = 0.001f32;
351    let y = f(x);
352    let s = x.shape()[0];
353    let t = y.shape()[0];
354
355    let mut result = Array::zeros((t, s));
356    for i in 0..s {
357        let mut delta_x : Array1<f32> = Array::zeros((s,));
358        delta_x[[i,]] = epsilon;
359
360        let new_x = &x + &delta_x;
361        let new_y = f(new_x.view()); 
362        let delta_y = &new_y - &y;
363
364        let grad = delta_y / epsilon;
365        for j in 0..t {
366            result[[j, i]] = grad[[j,]];
367        }
368    }
369    result
370}