Add new dependency to gui-tools
[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
71   if (n_vars > 0)
72     {
73       result = xmalloc (sizeof (*result));
74       result->n_alpha = 0;
75       result->members = xnmalloc (n_vars, sizeof (*result->members));
76       result->intr = var_create_internal (0);
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             }
85         }
86     }
87   /*
88     VAR_SET_WIDTH sets the type of the variable.
89    */
90   var_set_width (result->intr, MAX_SHORT_STRING * result->n_alpha + 1);
91
92   return result;
93 }
94 void interaction_variable_destroy (struct interaction_variable *iv)
95 {
96   var_destroy (iv->intr);
97   free (iv->members);
98   free (iv);
99 }
100
101 /*
102   Get one of the member variables.
103  */
104 const struct variable *
105 interaction_variable_get_member (const struct interaction_variable *iv, size_t i)
106 {
107   return iv->members[i];
108 }
109
110 size_t
111 interaction_get_n_vars (const struct interaction_variable *iv)
112 {
113   return (iv == NULL) ? 0 : iv->n_vars;
114 }
115
116 size_t
117 interaction_get_n_alpha (const struct interaction_variable *iv)
118 {
119   return iv->n_alpha;
120 }
121
122 size_t
123 interaction_get_n_numeric (const struct interaction_variable *iv)
124 {
125   return (interaction_get_n_vars (iv) - interaction_get_n_alpha (iv));
126 }
127
128 /*
129   Get the interaction varibale itself.
130  */
131 const struct variable *
132 interaction_variable_get_var (const struct interaction_variable *iv)
133 {
134   return iv->intr;
135 }
136 /*
137   Given list of values, compute the value of the corresponding
138   interaction.  This "value" is not stored as the typical vector of
139   0's and one double, but rather the string values are concatenated to
140   make one big string value, and the numerical values are multiplied
141   together to give the non-zero entry of the corresponding vector.
142  */
143 struct interaction_value *
144 interaction_value_create (const struct interaction_variable *var, const union value **vals)
145 {
146   struct interaction_value *result = NULL;
147   const struct variable *member;
148   size_t i;
149   size_t n_vars;
150   
151   if (var != NULL)
152     {
153       result = xmalloc (sizeof (*result));
154       result->intr = var;
155       n_vars = interaction_get_n_vars (var);
156       result->val = value_create (n_vars * MAX_SHORT_STRING + 1);
157       result->f = 1.0;
158       for (i = 0; i < n_vars; i++)
159         {
160           member = interaction_variable_get_member (var, i);
161
162           if (var_is_value_missing (member, vals[i], MV_ANY))
163             {
164               value_set_missing (result->val, MAX_SHORT_STRING);
165               result->f = SYSMIS;
166               break;
167             }
168           else
169             {
170               if (var_is_alpha (var->members[i]))
171                 {
172                   strncat (result->val->s, vals[i]->s, MAX_SHORT_STRING);
173                 }
174               else if (var_is_numeric (var->members[i]))
175                 {
176                   result->f *= vals[i]->f;
177                 }
178             }
179         }
180       if (interaction_get_n_alpha (var) == 0)
181         {
182           /*
183             If there are no categorical variables, then the
184             interaction consists of only numeric data. In this case,
185             code that uses this interaction_value will see the union
186             member as the numeric value. If we were to store that
187             numeric value in result->f as well, the calling code may
188             inadvertently square this value by multiplying by
189             result->val->f. Such multiplication would be correct for an
190             interaction consisting of both categorical and numeric
191             data, but a mistake for purely numerical interactions. To
192             avoid the error, we set result->f to 1.0 for numeric
193             interactions.
194            */
195           result->val->f = result->f;
196           result->f = 1.0;
197         }
198     }
199   return result;
200 }
201
202 union value *
203 interaction_value_get (const struct interaction_value *val)
204 {
205   return val->val;
206 }
207
208 /*
209   Returns the numeric value of the non-zero entry for the vector
210   corresponding to this interaction.  Do not use this function to get
211   the numeric value of a purley numeric interaction. Instead, use the
212   union value * returned by interaction_value_get.
213  */
214 double 
215 interaction_value_get_nonzero_entry (const struct interaction_value *val)
216 {
217   if (val != NULL)
218     return val->f;
219   return 1.0;
220 }
221
222 void 
223 interaction_value_destroy (struct interaction_value *val)
224 {
225   if (val != NULL)
226     {
227       free (val->val);
228       free (val);
229     }
230 }
231
232 /*
233   Return a value from a variable that is an interaction. 
234  */
235 struct interaction_value *
236 interaction_case_data (const struct ccase *ccase, const struct variable *var, 
237                        const struct interaction_variable **intr_vars, size_t n_intr)
238 {
239   size_t i;
240   size_t n_vars;
241   const struct interaction_variable *iv;
242   const struct variable *intr;
243   const struct variable *member;
244   const union value **vals = NULL;
245
246   for (i = 0; i < n_intr; i++)
247     {
248       iv = intr_vars[i];
249       intr = interaction_variable_get_var (iv);
250       if (var_get_dict_index (intr) == var_get_dict_index (var))
251         {
252           break;
253         }
254     }
255   n_vars = interaction_get_n_vars (iv);
256   vals = xnmalloc (n_vars, sizeof (*vals));
257   for (i = 0; i < n_vars; i++)
258     {
259       member = interaction_variable_get_member (iv, i);
260       vals[i] = case_data (ccase, member);
261     }
262   return interaction_value_create (iv, vals);
263 }
264
265 bool
266 is_interaction (const struct variable *var, const struct interaction_variable **iv, size_t n_intr)
267 {
268   size_t i;
269   const struct variable *intr;
270   
271   for (i = 0; i < n_intr; i++)
272     {
273       intr = interaction_variable_get_var (iv[i]);
274       if (var_get_dict_index (intr) == var_get_dict_index (var))
275         {
276           return true;
277         }
278     }
279   return false;
280 }
281