1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 2008, 2009, 2010 Free Software Foundation, Inc.
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>. */
18 Create and update the values in the covariance matrix.
22 #include <data/case.h>
23 #include <data/category.h>
24 #include <data/variable.h>
25 #include <data/value.h>
26 #include <libpspp/hash.h>
27 #include <libpspp/hash-functions.h>
28 #include <math/covariance-matrix.h>
29 #include <math/moments.h>
34 Structure used to accumulate the covariance matrix in a single data
35 pass. Before passing the data, we do not know how many categories
36 there are in each categorical variable. Therefore we do not know the
37 size of the covariance matrix. To get around this problem, we
38 accumulate the elements of the covariance matrix in pointers to
39 COVARIANC_ACCUMULATOR. These values are then used to populate
40 the covariance matrix.
42 struct covariance_accumulator
44 const struct variable *v1;
45 const struct variable *v2;
46 const union value *val1;
47 const union value *val2;
56 struct covariance_matrix
58 struct design_matrix *cov;
59 struct design_matrix *ssize;
63 const struct variable **v_variables;
64 const struct interaction_variable **interactions;
69 enum mv_class missing_value;
70 void (*accumulate) (struct covariance_matrix *, const struct ccase *,
71 const struct interaction_variable **, size_t);
72 void (*update_moments) (struct covariance_matrix *, size_t, double);
77 static struct hsh_table *covariance_hsh_create (size_t *);
78 static hsh_hash_func covariance_accumulator_hash;
79 static unsigned int hash_numeric_alpha (const struct variable *,
80 const struct variable *,
81 const union value *, size_t);
82 static hsh_compare_func covariance_accumulator_compare;
83 static hsh_free_func covariance_accumulator_free;
84 static void update_moments1 (struct covariance_matrix *, size_t, double);
85 static void update_moments2 (struct covariance_matrix *, size_t, double);
86 static struct covariance_accumulator *get_new_covariance_accumulator (const
101 static void covariance_accumulate_listwise (struct covariance_matrix *,
102 const struct ccase *,
103 const struct interaction_variable **,
105 static void covariance_accumulate_pairwise (struct covariance_matrix *,
106 const struct ccase *,
107 const struct interaction_variable **,
110 struct covariance_matrix *
111 covariance_matrix_init (size_t n_variables,
112 const struct variable *v_variables[], int n_pass,
113 int missing_handling, enum mv_class missing_value)
116 struct covariance_matrix *result = NULL;
118 result = xmalloc (sizeof (*result));
120 result->n_variables = n_variables;
121 result->ca = covariance_hsh_create (&result->n_variables);
125 result->missing_handling = missing_handling;
126 result->missing_value = missing_value;
127 result->accumulate = (result->missing_handling == LISTWISE) ?
128 covariance_accumulate_listwise : covariance_accumulate_pairwise;
129 if (n_pass == ONE_PASS)
131 result->update_moments = update_moments1;
132 result->m1 = xnmalloc (n_variables, sizeof (*result->m1));
133 for (i = 0; i < n_variables; i++)
135 result->m1[i] = moments1_create (MOMENT_MEAN);
140 result->update_moments = update_moments2;
141 result->m = xnmalloc (n_variables, sizeof (*result->m));
142 for (i = 0; i < n_variables; i++)
144 result->m[i] = moments_create (MOMENT_MEAN);
147 result->v_variables = v_variables;
149 result->n_pass = n_pass;
154 covariance_interaction_set (struct covariance_matrix *cov,
155 const struct interaction_variable **intr, size_t n_intr)
157 cov->interactions = intr;
158 cov->n_intr = n_intr;
162 get_n_rows (size_t n_variables, const struct variable *v_variables[])
166 for (i = 0; i < n_variables; i++)
168 if (var_is_numeric (v_variables[i]))
172 else if (var_is_alpha (v_variables[i]))
174 size_t n_categories = cat_get_n_categories (v_variables[i]);
175 result += n_categories - 1;
181 The covariances are stored in a DESIGN_MATRIX structure.
183 struct design_matrix *
184 covariance_matrix_create (size_t n_variables,
185 const struct variable *v_variables[])
187 size_t n_rows = get_n_rows (n_variables, v_variables);
188 return design_matrix_create (n_variables, v_variables, n_rows);
192 get_n_rows_s (const struct variable *var)
195 if (var_is_numeric (var))
201 result += cat_get_n_categories (var) - 1;
205 static struct design_matrix *
206 covariance_matrix_create_s (struct covariance_matrix *cov)
208 struct variable **v_variables;
214 n_variables = cov->n_variables + cov->n_intr;
215 v_variables = xnmalloc (n_variables, sizeof (*v_variables));
216 for (i = 0; i < cov->n_variables; i++)
218 v_variables[i] = cov->v_variables[i];
219 n_rows += get_n_rows_s (v_variables[i]);
221 for (j = 0; j < cov->n_intr; j++)
223 v_variables[i + j] = interaction_get_variable (cov->interactions[j]);
224 n_rows += get_n_rows_s (v_variables[i]);
226 return design_matrix_create (n_variables, v_variables, n_rows);
230 update_moments1 (struct covariance_matrix *cov, size_t i, double x)
232 assert (cov->m1 != NULL);
233 moments1_add (cov->m1[i], x, 1.0);
237 update_moments2 (struct covariance_matrix *cov, size_t i, double x)
239 assert (cov->m != NULL);
240 moments_pass_one (cov->m[i], x, 1.0);
244 covariance_matrix_destroy (struct covariance_matrix *cov)
248 assert (cov != NULL);
249 design_matrix_destroy (cov->cov);
250 design_matrix_destroy (cov->ssize);
251 hsh_destroy (cov->ca);
252 if (cov->n_pass == ONE_PASS)
254 for (i = 0; i < cov->n_variables; i++)
256 moments1_destroy (cov->m1[i]);
262 for (i = 0; i < cov->n_variables; i++)
264 moments_destroy (cov->m[i]);
271 Update the covariance matrix with the new entries, assuming that ROW
272 corresponds to a categorical variable and V2 is numeric.
275 covariance_update_categorical_numeric (struct design_matrix *cov, double mean,
277 const struct variable *v2, double x,
278 const union value *val2)
283 assert (var_is_numeric (v2));
285 col = design_matrix_var_to_column (cov, v2);
286 assert (val2 != NULL);
287 tmp = design_matrix_get_element (cov, row, col);
288 design_matrix_set_element (cov, row, col, (val2->f - mean) * x + tmp);
289 design_matrix_set_element (cov, col, row, (val2->f - mean) * x + tmp);
292 column_iterate (struct design_matrix *cov, const struct variable *v,
293 double ssize, double x, const union value *val1, size_t row)
295 int width = var_get_width (v);
300 const union value *tmp_val;
302 col = design_matrix_var_to_column (cov, v);
303 for (i = 0; i < cat_get_n_categories (v) - 1; i++)
306 y = -1.0 * cat_get_category_count (i, v) / ssize;
307 tmp_val = cat_subscript_to_value (i, v);
308 if (!value_equal (tmp_val, val1, width))
312 tmp = design_matrix_get_element (cov, row, col);
313 design_matrix_set_element (cov, row, col, x * y + tmp);
314 design_matrix_set_element (cov, col, row, x * y + tmp);
319 Call this function in the second data pass. The central moments are
320 MEAN1 and MEAN2. Any categorical variables should already have their
321 values summarized in in its OBS_VALS element.
324 covariance_pass_two (struct design_matrix *cov, double mean1, double mean2,
325 double ssize, const struct variable *v1,
326 const struct variable *v2, const union value *val1,
327 const union value *val2)
333 const union value *tmp_val;
335 if (var_is_alpha (v1))
337 row = design_matrix_var_to_column (cov, v1);
338 for (i = 0; i < cat_get_n_categories (v1) - 1; i++)
341 x = -1.0 * cat_get_category_count (i, v1) / ssize;
342 tmp_val = cat_subscript_to_value (i, v1);
343 if (!value_equal (tmp_val, val1, var_get_width (v1)))
347 if (var_is_numeric (v2))
349 covariance_update_categorical_numeric (cov, mean2, row,
354 column_iterate (cov, v1, ssize, x, val1, row);
355 column_iterate (cov, v2, ssize, x, val2, row);
359 else if (var_is_alpha (v2))
362 Reverse the orders of V1, V2, etc. and put ourselves back
363 in the previous IF scope.
365 covariance_pass_two (cov, mean2, mean1, ssize, v2, v1, val2, val1);
370 Both variables are numeric.
372 row = design_matrix_var_to_column (cov, v1);
373 col = design_matrix_var_to_column (cov, v2);
374 x = (val1->f - mean1) * (val2->f - mean2);
375 x += design_matrix_get_element (cov, col, row);
376 design_matrix_set_element (cov, row, col, x);
377 design_matrix_set_element (cov, col, row, x);
382 covariance_accumulator_hash (const void *h, const void *aux)
384 struct covariance_accumulator *ca = (struct covariance_accumulator *) h;
385 size_t *n_vars = (size_t *) aux;
386 const struct variable *v_min;
387 const struct variable *v_max;
388 const union value *val_min;
389 const union value *val_max;
392 Order everything by the variables' addresses. This ensures we get the
393 same key regardless of the order in which the variables are stored
411 if (var_is_numeric (v_max) && var_is_numeric (v_min))
413 return hash_pointer (v_min, hash_pointer (v_max, 0));
415 if (var_is_numeric (v_max) && var_is_alpha (v_min))
417 return hash_numeric_alpha (v_max, v_min, val_min, *n_vars);
419 if (var_is_alpha (v_max) && var_is_numeric (v_min))
421 return (hash_numeric_alpha (v_min, v_max, val_max, *n_vars));
423 if (var_is_alpha (v_max) && var_is_alpha (v_min))
425 unsigned hash = value_hash (val_max, var_get_width (v_max), 0);
426 hash = value_hash (val_min, var_get_width (v_min), hash);
427 hash = hash_pointer (v_min, hash);
428 return hash_pointer (v_max, hash);
434 Make a hash table consisting of struct covariance_accumulators.
435 This allows the accumulation of the elements of a covariance matrix
436 in a single data pass. Call covariance_accumulate () for each case
439 static struct hsh_table *
440 covariance_hsh_create (size_t *n_vars)
442 return hsh_create (*n_vars * *n_vars, covariance_accumulator_compare,
443 covariance_accumulator_hash, covariance_accumulator_free,
448 covariance_accumulator_free (void *c_, const void *aux UNUSED)
450 struct covariance_accumulator *c = c_;
456 ordered_match_nodes (const struct covariance_accumulator *c, const struct variable *v1,
457 const struct variable *v2, const union value *val1, const union value *val2)
461 || (var_is_alpha (v1)
462 && !value_equal (val1, c->val1, var_get_width (v1)))
463 || (var_is_alpha (v2)
464 && !value_equal (val2, c->val2, var_get_width (v2))));
468 Hash comparison. Returns 0 for a match, or a non-zero int
469 otherwise. The sign of a non-zero return value *should* indicate the
470 position of C relative to the covariance_accumulator described by
471 the other arguments. But for now, it just returns 1 for any
472 non-match. This should be changed when someone figures out how to
473 compute a sensible sign for the return value.
476 match_nodes (const struct covariance_accumulator *c,
477 const struct variable *v1, const struct variable *v2,
478 const union value *val1, const union value *val2)
480 return (ordered_match_nodes (c, v1, v2, val1, val2)
481 && ordered_match_nodes (c, v2, v1, val2, val1));
485 This function is meant to be used as a comparison function for
486 a struct hsh_table in src/libpspp/hash.c.
489 covariance_accumulator_compare (const void *a1_, const void *a2_,
490 const void *aux UNUSED)
492 const struct covariance_accumulator *a1 = a1_;
493 const struct covariance_accumulator *a2 = a2_;
495 if (a1 == NULL && a2 == NULL)
498 if (a1 == NULL || a2 == NULL)
501 return match_nodes (a1, a2->v1, a2->v2, a2->val1, a2->val2);
505 hash_numeric_alpha (const struct variable *v1, const struct variable *v2,
506 const union value *val, size_t n_vars)
508 unsigned int result = -1u;
509 if (var_is_numeric (v1) && var_is_alpha (v2))
511 result = hash_pointer (v1, 0);
512 result = hash_pointer (v2, result);
513 result = value_hash (val, var_get_width (v2), result);
515 else if (var_is_alpha (v1) && var_is_numeric (v2))
517 result = hash_numeric_alpha (v2, v1, val, n_vars);
524 update_product (const struct variable *v1, const struct variable *v2,
525 const union value *val1, const union value *val2)
529 assert (val1 != NULL);
530 assert (val2 != NULL);
531 if (var_is_alpha (v1) && var_is_alpha (v2))
535 if (var_is_numeric (v1) && var_is_numeric (v2))
537 return (val1->f * val2->f);
539 if (var_is_numeric (v1) && var_is_alpha (v2))
543 if (var_is_numeric (v2) && var_is_alpha (v1))
553 update_sum (const struct variable *var, const union value *val, double weight)
555 assert (var != NULL);
556 assert (val != NULL);
557 if (var_is_alpha (var))
563 static struct covariance_accumulator *
564 get_new_covariance_accumulator (const struct variable *v1,
565 const struct variable *v2,
566 const union value *val1,
567 const union value *val2)
569 if ((v1 != NULL) && (v2 != NULL) && (val1 != NULL) && (val2 != NULL))
571 struct covariance_accumulator *ca;
572 ca = xmalloc (sizeof (*ca));
582 static const struct variable **
583 get_covariance_variables (const struct covariance_matrix *cov)
585 return cov->v_variables;
589 update_hash_entry_intr (struct hsh_table *c,
590 const struct variable *v1,
591 const struct variable *v2,
592 const union value *val1, const union value *val2,
593 const struct interaction_value *i_val1,
594 const struct interaction_value *i_val2)
596 struct covariance_accumulator *ca;
597 struct covariance_accumulator *new_entry;
601 iv_f1 = interaction_value_get_nonzero_entry (i_val1);
602 iv_f2 = interaction_value_get_nonzero_entry (i_val2);
603 ca = get_new_covariance_accumulator (v1, v2, val1, val2);
604 ca->dot_product = update_product (ca->v1, ca->v2, ca->val1, ca->val2);
605 ca->dot_product *= iv_f1 * iv_f2;
606 ca->sum1 = update_sum (ca->v1, ca->val1, iv_f1);
607 ca->sum2 = update_sum (ca->v2, ca->val2, iv_f2);
609 new_entry = hsh_insert (c, ca);
611 if (new_entry != NULL)
613 new_entry->dot_product += ca->dot_product;
614 new_entry->ssize += 1.0;
615 new_entry->sum1 += ca->sum1;
616 new_entry->sum2 += ca->sum2;
618 If DOT_PRODUCT is null, CA was not already in the hash
619 hable, so we don't free it because it was just inserted.
620 If DOT_PRODUCT was not null, CA is already in the hash table.
621 Unnecessary now, it must be freed here.
628 update_hash_entry (struct hsh_table *c,
629 const struct variable *v1,
630 const struct variable *v2,
631 const union value *val1, const union value *val2)
633 struct covariance_accumulator *ca;
634 struct covariance_accumulator *new_entry;
636 ca = get_new_covariance_accumulator (v1, v2, val1, val2);
637 ca->dot_product = update_product (ca->v1, ca->v2, ca->val1, ca->val2);
638 ca->sum1 = update_sum (ca->v1, ca->val1, 1.0);
639 ca->sum2 = update_sum (ca->v2, ca->val2, 1.0);
641 new_entry = hsh_insert (c, ca);
643 if (new_entry != NULL)
645 new_entry->dot_product += ca->dot_product;
646 new_entry->ssize += 1.0;
647 new_entry->sum1 += ca->sum1;
648 new_entry->sum2 += ca->sum2;
650 If DOT_PRODUCT is null, CA was not already in the hash
651 hable, so we don't free it because it was just inserted.
652 If DOT_PRODUCT was not null, CA is already in the hash table.
653 Unnecessary now, it must be freed here.
660 inner_intr_loop (struct covariance_matrix *cov, const struct ccase *ccase, const struct variable *var1,
661 const union value *val1, const struct interaction_variable **i_var,
662 const struct interaction_value *i_val1, size_t j)
664 struct variable *var2;
666 struct interaction_value *i_val2;
668 var2 = interaction_get_variable (i_var[j]);
669 i_val2 = interaction_case_data (ccase, i_var[j]);
670 val2 = interaction_value_get (i_val2);
672 if (!var_is_value_missing (var2, val2, cov->missing_value))
674 update_hash_entry_intr (cov->ca, var1, var2, val1, val2, i_val1, i_val2);
678 Compute the covariance matrix in a single data-pass. Cases with
679 missing values are dropped pairwise, in other words, only if one of
680 the two values necessary to accumulate the inner product is missing.
682 Do not call this function directly. Call it through the struct
683 covariance_matrix ACCUMULATE member function, for example,
684 cov->accumulate (cov, ccase).
687 covariance_accumulate_pairwise (struct covariance_matrix *cov,
688 const struct ccase *ccase,
689 const struct interaction_variable **i_var,
694 const union value *val1;
695 const union value *val2;
696 const struct variable **v_variables;
697 const struct variable *var1;
698 const struct variable *var2;
699 struct interaction_value *i_val1 = NULL;
700 struct interaction_value *i_val2 = NULL;
702 assert (cov != NULL);
703 assert (ccase != NULL);
705 v_variables = get_covariance_variables (cov);
706 assert (v_variables != NULL);
708 for (i = 0; i < cov->n_variables; ++i)
710 var1 = v_variables[i];
711 val1 = case_data (ccase, var1);
712 if (!var_is_value_missing (var1, val1, cov->missing_value))
714 cat_value_update (var1, val1);
715 if (var_is_numeric (var1))
716 cov->update_moments (cov, i, val1->f);
718 for (j = i; j < cov->n_variables; j++)
720 var2 = v_variables[j];
721 val2 = case_data (ccase, var2);
722 if (!var_is_value_missing
723 (var2, val2, cov->missing_value))
725 update_hash_entry (cov->ca, var1, var2, val1, val2);
728 for (j = 0; j < cov->n_intr; j++)
730 inner_intr_loop (cov, ccase, var1, val1, i_var, i_val1, j);
734 for (i = 0; i < cov->n_intr; i++)
736 var1 = interaction_get_variable (i_var[i]);
737 i_val1 = interaction_case_data (ccase, i_var[i]);
738 val1 = interaction_value_get (i_val1);
739 cat_value_update (var1, val1);
740 if (!var_is_value_missing (var1, val1, cov->missing_value))
742 for (j = i; j < cov->n_intr; j++)
744 inner_intr_loop (cov, ccase, var1, val1, i_var, i_val1, j);
751 Compute the covariance matrix in a single data-pass. Cases with
752 missing values are dropped listwise. In other words, if one of the
753 values for any variable in a case is missing, the entire case is
756 The caller must use a casefilter to remove the cases with missing
757 values before calling covariance_accumulate_listwise. This function
758 assumes that CCASE has already passed through this filter, and
759 contains no missing values.
761 Do not call this function directly. Call it through the struct
762 covariance_matrix ACCUMULATE member function, for example,
763 cov->accumulate (cov, ccase).
766 covariance_accumulate_listwise (struct covariance_matrix *cov,
767 const struct ccase *ccase,
768 const struct interaction_variable **i_var,
773 const union value *val1;
774 const union value *val2;
775 const struct variable **v_variables;
776 struct interaction_value *i_val1 = NULL;
777 struct interaction_value *i_val2 = NULL;
779 assert (cov != NULL);
780 assert (ccase != NULL);
782 v_variables = get_covariance_variables (cov);
783 assert (v_variables != NULL);
785 for (i = 0; i < cov->n_variables; ++i)
787 val1 = case_data (ccase, v_variables[i]);
788 cat_value_update (v_variables[i], val1);
789 if (var_is_numeric (v_variables[i]))
790 cov->update_moments (cov, i, val1->f);
792 for (j = i; j < cov->n_variables; j++)
794 update_hash_entry (cov->ca, v_variables[i], v_variables[j],
801 Call this function during the data pass. Each case will be added to
802 a hash containing all values of the covariance matrix. After the
803 data have been passed, call covariance_matrix_compute to put the
804 values in the struct covariance_matrix.
807 covariance_matrix_accumulate (struct covariance_matrix *cov,
808 const struct ccase *ccase, void **aux, size_t n_intr)
810 cov->accumulate (cov, ccase, (const struct interaction_variable **) aux, n_intr);
814 Return the value corresponding to subscript TARGET. If that value corresponds
815 to the origin, return NULL.
817 static const union value *
818 get_value_from_subscript (const struct design_matrix *dm, size_t target)
820 const union value *result = NULL;
821 const struct variable *var;
824 var = design_matrix_col_to_var (dm, target);
825 if (var_is_numeric (var))
829 for (i = 0; i < cat_get_n_categories (var); i++)
831 result = cat_subscript_to_value (i, var);
832 if (dm_get_exact_subscript (dm, var, result) == target)
841 is_covariance_contributor (const struct covariance_accumulator *ca, const struct design_matrix *dm,
845 const struct variable *v1;
846 const struct variable *v2;
849 v1 = design_matrix_col_to_var (dm, i);
850 v2 = design_matrix_col_to_var (dm, j);
855 k = dm_get_exact_subscript (dm, v1, ca->val1);
858 k = dm_get_exact_subscript (dm, v2, ca->val2);
866 else if (v1 == ca->v2)
870 k = dm_get_exact_subscript (dm, v1, ca->val2);
873 k = dm_get_exact_subscript (dm, v2, ca->val1);
885 get_sum (const struct covariance_matrix *cov, size_t i)
890 const struct variable *var;
891 const union value *val = NULL;
893 assert ( cov != NULL);
894 var = design_matrix_col_to_var (cov->cov, i);
897 if (var_is_alpha (var))
899 val = get_value_from_subscript (cov->cov, i);
900 k = cat_value_find (var, val);
901 return cat_get_category_count (k, var);
906 while (cov->v_variables[k] != var && k < cov->n_variables)
910 if (k < cov->n_variables)
912 moments1_calculate (cov->m1[k], &n, &mean, NULL, NULL, NULL);
921 update_ssize (struct design_matrix *dm, size_t i, size_t j, struct covariance_accumulator *ca)
923 const struct variable *var;
925 var = design_matrix_col_to_var (dm, i);
928 var = design_matrix_col_to_var (dm, j);
931 tmp = design_matrix_get_element (dm, i, j);
933 design_matrix_set_element (dm, i, j, tmp);
938 covariance_accumulator_to_matrix (struct covariance_matrix *cov)
945 struct covariance_accumulator *entry;
946 struct hsh_iterator iter;
948 cov->cov = covariance_matrix_create_s (cov);
949 cov->ssize = covariance_matrix_create_s (cov);
950 entry = hsh_first (cov->ca, &iter);
951 while (entry != NULL)
953 entry = hsh_next (cov->ca, &iter);
956 for (i = 0; i < design_matrix_get_n_cols (cov->cov); i++)
958 sum_i = get_sum (cov, i);
959 for (j = i; j < design_matrix_get_n_cols (cov->cov); j++)
961 sum_j = get_sum (cov, j);
962 entry = hsh_first (cov->ca, &iter);
963 while (entry != NULL)
965 update_ssize (cov->ssize, i, j, entry);
967 We compute the centered, un-normalized covariance matrix.
969 if (is_covariance_contributor (entry, cov->cov, i, j))
971 design_matrix_set_element (cov->cov, i, j, entry->dot_product);
973 entry = hsh_next (cov->ca, &iter);
975 tmp = design_matrix_get_element (cov->cov, i, j);
976 tmp -= sum_i * sum_j / design_matrix_get_element (cov->ssize, i, j);
977 design_matrix_set_element (cov->cov, i, j, tmp);
978 design_matrix_set_element (cov->cov, j, i, tmp);
985 Call this function after passing the data.
988 covariance_matrix_compute (struct covariance_matrix *cov)
990 if (cov->n_pass == ONE_PASS)
992 covariance_accumulator_to_matrix (cov);
996 struct design_matrix *
997 covariance_to_design (const struct covariance_matrix *c)
1006 covariance_matrix_get_n_rows (const struct covariance_matrix *c)
1008 return design_matrix_get_n_rows (c->cov);
1012 covariance_matrix_get_element (const struct covariance_matrix *c, size_t row, size_t col)
1014 return (design_matrix_get_element (c->cov, row, col));