Merge commit 'origin/data-encoding'
[pspp-builds.git] / src / math / interaction.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 2009 Free Software Foundation, Inc.
3
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.
8
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.
13
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/>. */
16
17 /*
18   An interaction is a gsl_vector containing a "product" of other
19   variables. The variables can be either categorical or numeric.
20   If the variables are all numeric, the interaction is just the
21   scalar product. If any of the variables are categorical, their
22   product is a vector containing 0's in all but one entry. This entry
23   is found by combining the vectors corresponding to the variables'
24   OBS_VALS member. If there are K categorical variables, each with
25   N_1, N_2, ..., N_K categories, then the interaction will have
26   N_1 * N_2 * N_3 *...* N_K - 1 entries.
27
28   When using these functions, make sure the orders of variables and
29   values match when appropriate.
30  */
31
32 #include <config.h>
33 #include <assert.h>
34 #include <gsl/gsl_math.h>
35 #include <gsl/gsl_vector.h>
36 #include <data/value.h>
37 #include <data/variable.h>
38 #include <math/interaction.h>
39 #include <string.h>
40 #include <xalloc.h>
41
42 struct interaction_variable
43 {
44   int n_vars;
45   const struct variable **members;
46   struct variable *intr;
47   size_t n_alpha;
48 };
49
50 struct interaction_value
51 {
52   const struct interaction_variable *intr;
53   union value val; /* Concatenation of the string values in this
54                       interaction's value, or the product of a bunch
55                       of numeric values for a purely numeric
56                       interaction.
57                     */
58   double f; /* Product of the numerical values in this interaction's value. */
59 };
60
61 /*
62   An interaction_variable has type alpha if any of members have type
63   alpha. Otherwise, its type is numeric.
64  */
65 struct interaction_variable *
66 interaction_variable_create (const struct variable **vars, int n_vars)
67 {
68   struct interaction_variable *result = NULL;
69   size_t i;
70   int width = 0;
71
72   if (n_vars > 0)
73     {
74       result = xmalloc (sizeof (*result));
75       result->n_alpha = 0;
76       result->members = xnmalloc (n_vars, sizeof (*result->members));
77       result->n_vars = n_vars;
78       for (i = 0; i < n_vars; i++)
79         {
80           result->members[i] = vars[i];
81           if (var_is_alpha (vars[i]))
82             {
83               result->n_alpha++;
84               width = 1;
85             }
86         }
87     }
88   result->intr = var_create_internal (0, width);
89
90   return result;
91 }
92 void interaction_variable_destroy (struct interaction_variable *iv)
93 {
94   var_destroy (iv->intr);
95   free (iv->members);
96   free (iv);
97 }
98
99 /*
100   Get one of the member variables.
101  */
102 const struct variable *
103 interaction_get_member (const struct interaction_variable *iv, size_t i)
104 {
105   return iv->members[i];
106 }
107
108 size_t
109 interaction_get_n_vars (const struct interaction_variable *iv)
110 {
111   return (iv == NULL) ? 0 : iv->n_vars;
112 }
113
114 size_t
115 interaction_get_n_alpha (const struct interaction_variable *iv)
116 {
117   return iv->n_alpha;
118 }
119
120 size_t
121 interaction_get_n_numeric (const struct interaction_variable *iv)
122 {
123   return (interaction_get_n_vars (iv) - interaction_get_n_alpha (iv));
124 }
125
126 /*
127   Get the interaction variable itself.
128  */
129 const struct variable *
130 interaction_get_variable (const struct interaction_variable *iv)
131 {
132   return iv->intr;
133 }
134 /*
135   Given list of values, compute the value of the corresponding
136   interaction.  This "value" is not stored as the typical vector of
137   0's and one double, but rather the string values are concatenated to
138   make one big string value, and the numerical values are multiplied
139   together to give the non-zero entry of the corresponding vector.
140  */
141 struct interaction_value *
142 interaction_value_create (const struct interaction_variable *var, const union value **vals)
143 {
144   struct interaction_value *result = NULL;
145   const struct variable *member;
146   size_t i;
147   size_t n_vars;
148   
149   if (var != NULL)
150     {
151       int val_width = 1;
152       uint8_t *val;
153
154       result = xmalloc (sizeof (*result));
155       result->intr = var;
156       n_vars = interaction_get_n_vars (var);
157       value_init (&result->val, val_width);
158       val = value_str_rw (&result->val, val_width);
159       val[0] = '\0';
160       result->f = 1.0;
161       for (i = 0; i < n_vars; i++)
162         {
163           member = interaction_get_member (var, i);
164
165           if (var_is_value_missing (member, vals[i], MV_ANY))
166             {
167               value_set_missing (&result->val, MAX_SHORT_STRING);
168               result->f = SYSMIS;
169               break;
170             }
171           else
172             {
173               if (var_is_alpha (var->members[i]))
174                 {
175                   int w = var_get_width (var->members[i]);
176                   value_resize (result, val_width, val_width + w);
177                   strncat (val, value_str (vals[i], w), w);
178                   val = value_str_rw (&result->val, val_width);
179                 }
180               else if (var_is_numeric (var->members[i]))
181                 {
182                   result->f *= vals[i]->f;
183                 }
184             }
185         }
186       if (interaction_get_n_alpha (var) == 0)
187         {
188           /*
189             If there are no categorical variables, then the
190             interaction consists of only numeric data. In this case,
191             code that uses this interaction_value will see the union
192             member as the numeric value. If we were to store that
193             numeric value in result->f as well, the calling code may
194             inadvertently square this value by multiplying by
195             result->val->f. Such multiplication would be correct for an
196             interaction consisting of both categorical and numeric
197             data, but a mistake for purely numerical interactions. To
198             avoid the error, we set result->f to 1.0 for numeric
199             interactions.
200            */
201           result->val.f = result->f;
202           result->f = 1.0;
203         }
204     }
205   return result;
206 }
207
208 const union value *
209 interaction_value_get (const struct interaction_value *val)
210 {
211   return &val->val;
212 }
213
214 /*
215   Returns the numeric value of the non-zero entry for the vector
216   corresponding to this interaction.  Do not use this function to get
217   the numeric value of a purley numeric interaction. Instead, use the
218   union value * returned by interaction_value_get.
219  */
220 double 
221 interaction_value_get_nonzero_entry (const struct interaction_value *val)
222 {
223   if (val != NULL)
224     return val->f;
225   return 1.0;
226 }
227
228 void 
229 interaction_value_destroy (struct interaction_value *val)
230 {
231   if (val != NULL)
232     {
233       size_t n_vars = interaction_get_n_vars (val->intr);
234       int val_width = n_vars * MAX_SHORT_STRING + 1;
235
236       value_destroy (&val->val, val_width);
237       free (val);
238     }
239 }
240
241 /*
242   Return a value from a variable that is an interaction. 
243  */
244 struct interaction_value *
245 interaction_case_data (const struct ccase *ccase, const struct interaction_variable *iv)
246 {
247   size_t i;
248   size_t n_vars;
249   const struct variable *member;
250   const union value **vals = NULL;
251
252   n_vars = interaction_get_n_vars (iv);
253   vals = xnmalloc (n_vars, sizeof (*vals));
254
255   for (i = 0; i < n_vars; i++)
256         {
257           member = interaction_get_member (iv, i);
258           vals[i] = case_data (ccase, member);
259         }
260
261   return interaction_value_create (iv, vals);
262 }
263
264 bool
265 is_interaction (const struct variable *var, const struct interaction_variable **iv, size_t n_intr)
266 {
267   size_t i;
268   const struct variable *intr;
269   
270   for (i = 0; i < n_intr; i++)
271     {
272       intr = interaction_get_variable (iv[i]);
273       if (var_get_dict_index (intr) == var_get_dict_index (var))
274         {
275           return true;
276         }
277     }
278   return false;
279 }
280