Introduce gmxpre.h for truly global definitions
[alexxy/gromacs.git] / src / gromacs / selection / sm_compare.cpp
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 2009,2010,2011,2012,2013,2014, by the GROMACS development team, led by
5  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
6  * and including many others, as listed in the AUTHORS file in the
7  * top-level source directory and at http://www.gromacs.org.
8  *
9  * GROMACS is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public License
11  * as published by the Free Software Foundation; either version 2.1
12  * of the License, or (at your option) any later version.
13  *
14  * GROMACS is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with GROMACS; if not, see
21  * http://www.gnu.org/licenses, or write to the Free Software Foundation,
22  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
23  *
24  * If you want to redistribute modifications to GROMACS, please
25  * consider that scientific software is very special. Version
26  * control is crucial - bugs must be traceable. We will be happy to
27  * consider code for inclusion in the official distribution, but
28  * derived work must not be called official GROMACS. Details are found
29  * in the README & COPYING files - if they are missing, get the
30  * official version at http://www.gromacs.org.
31  *
32  * To help us fund GROMACS development, we humbly ask that you cite
33  * the research papers on the package. Check out http://www.gromacs.org.
34  */
35 /*! \internal \file
36  * \brief
37  * Implements internal selection method for comparison expressions.
38  *
39  * \author Teemu Murtola <teemu.murtola@gmail.com>
40  * \ingroup module_selection
41  */
42 #include "gmxpre.h"
43
44 #include <cmath>
45
46 #include "gromacs/legacyheaders/macros.h"
47
48 #include "gromacs/math/utilities.h"
49 #include "gromacs/selection/selmethod.h"
50 #include "gromacs/utility/common.h"
51 #include "gromacs/utility/exceptions.h"
52 #include "gromacs/utility/smalloc.h"
53
54 /** Defines the comparison operator for comparison expressions. */
55 typedef enum
56 {
57     CMP_INVALID,        /**< Indicates an error */
58     CMP_LESS,           /**< '<' */
59     CMP_LEQ,            /**< '<=' */
60     CMP_GTR,            /**< '>' */
61     CMP_GEQ,            /**< '>=' */
62     CMP_EQUAL,          /**< '==' */
63     CMP_NEQ             /**< '!=' */
64 } e_comparison_t;
65
66 /** The operand has a single value. */
67 #define CMP_SINGLEVAL  1
68 /** The operand value is dynamic. */
69 #define CMP_DYNAMICVAL 2
70 /** The value is real. */
71 #define CMP_REALVAL    4
72 /** The integer array is allocated. */
73 #define CMP_ALLOCINT   16
74 /** The real array is allocated. */
75 #define CMP_ALLOCREAL  32
76
77 /*! \internal \brief
78  * Data structure for comparison expression operand values.
79  */
80 typedef struct
81 {
82     /** Flags that describe the type of the operand. */
83     int             flags;
84     /** (Array of) integer value(s). */
85     int            *i;
86     /** (Array of) real value(s). */
87     real           *r;
88 } t_compare_value;
89
90 /*! \internal \brief
91  * Data structure for comparison expression evaluation.
92  */
93 typedef struct
94 {
95     /** Comparison operator as a string. */
96     char            *cmpop;
97     /** Comparison operator type. */
98     e_comparison_t   cmpt;
99     /** Left value. */
100     t_compare_value  left;
101     /** Right value. */
102     t_compare_value  right;
103 } t_methoddata_compare;
104
105 /*! \brief
106  * Allocates data for comparison expression evaluation.
107  *
108  * \param[in]     npar  Not used (should be 5).
109  * \param[in,out] param Method parameters (should point to a copy of
110  *   \ref smparams_compare).
111  * \returns       Pointer to the allocated data (\c t_methoddata_compare).
112  *
113  * Allocates memory for a \c t_methoddata_compare structure.
114  */
115 static void *
116 init_data_compare(int npar, gmx_ana_selparam_t *param);
117 /*! \brief
118  * Initializes data for comparison expression evaluation.
119  *
120  * \param[in] top   Not used.
121  * \param[in] npar  Not used (should be 5).
122  * \param[in] param Method parameters (should point to \ref smparams_compare).
123  * \param[in] data  Should point to a \c t_methoddata_compare.
124  * \returns   0 if the input data is valid, -1 on error.
125  */
126 static void
127 init_compare(t_topology *top, int npar, gmx_ana_selparam_t *param, void *data);
128 /** Frees the memory allocated for comparison expression evaluation. */
129 static void
130 free_data_compare(void *data);
131 /** Evaluates comparison expressions. */
132 static void
133 evaluate_compare(t_topology *top, t_trxframe *fr, t_pbc *pbc,
134                  gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data);
135
136 /** Parameters for comparison expression evaluation. */
137 static gmx_ana_selparam_t smparams_compare[] = {
138     {"int1",  {INT_VALUE,  -1, {NULL}}, NULL,
139      SPAR_OPTIONAL | SPAR_DYNAMIC | SPAR_ATOMVAL},
140     {"real1", {REAL_VALUE, -1, {NULL}}, NULL,
141      SPAR_OPTIONAL | SPAR_DYNAMIC | SPAR_ATOMVAL},
142     {"op",    {STR_VALUE,   1, {NULL}}, NULL, 0},
143     {"int2",  {INT_VALUE,  -1, {NULL}}, NULL,
144      SPAR_OPTIONAL | SPAR_DYNAMIC | SPAR_ATOMVAL},
145     {"real2", {REAL_VALUE, -1, {NULL}}, NULL,
146      SPAR_OPTIONAL | SPAR_DYNAMIC | SPAR_ATOMVAL},
147 };
148
149 /** \internal Selection method data for comparison expression evaluation. */
150 gmx_ana_selmethod_t sm_compare = {
151     "cmp", GROUP_VALUE, SMETH_SINGLEVAL,
152     asize(smparams_compare), smparams_compare,
153     &init_data_compare,
154     NULL,
155     &init_compare,
156     NULL,
157     &free_data_compare,
158     NULL,
159     &evaluate_compare,
160     NULL,
161     {NULL, 0, NULL},
162 };
163
164 /*! \brief
165  * Returns a \c e_comparison_t value corresponding to an operator.
166  *
167  * \param[in] str  String to process.
168  * \returns   The comparison type corresponding to the first one or two
169  *   characters of \p str.
170  *
171  * \p str can contain any number of characters; only the first two
172  * are used.
173  * If the beginning of \p str does not match any of the recognized types,
174  * \ref CMP_INVALID is returned.
175  */
176 static e_comparison_t
177 comparison_type(char *str)
178 {
179     switch (str[0])
180     {
181         case '<': return (str[1] == '=') ? CMP_LEQ   : CMP_LESS;
182         case '>': return (str[1] == '=') ? CMP_GEQ   : CMP_GTR;
183         case '=': return (str[1] == '=') ? CMP_EQUAL : CMP_INVALID;
184         case '!': return (str[1] == '=') ? CMP_NEQ   : CMP_INVALID;
185     }
186     return CMP_INVALID;
187 }
188
189 /*! \brief
190  * Returns a string corresponding to a \c e_comparison_t value.
191  *
192  * \param[in] cmpt  Comparison type to convert.
193  * \returns   Pointer to a string that corresponds to \p cmpt.
194  *
195  * The return value points to a string constant and should not be \p free'd.
196  *
197  * The function returns NULL if \p cmpt is not one of the valid values.
198  */
199 static const char *
200 comparison_type_str(e_comparison_t cmpt)
201 {
202     switch (cmpt)
203     {
204         case CMP_INVALID: return "INVALID"; break;
205         case CMP_LESS:    return "<";  break;
206         case CMP_LEQ:     return "<="; break;
207         case CMP_GTR:     return ">";  break;
208         case CMP_GEQ:     return ">="; break;
209         case CMP_EQUAL:   return "=="; break;
210         case CMP_NEQ:     return "!="; break;
211     }
212     return NULL;
213 }
214
215 /*!
216  * \param[in] fp    File to receive the output.
217  * \param[in] data  Should point to a \c t_methoddata_compare.
218  */
219 void
220 _gmx_selelem_print_compare_info(FILE *fp, void *data)
221 {
222     t_methoddata_compare *d = (t_methoddata_compare *)data;
223
224     fprintf(fp, " \"");
225     /* Print the left value */
226     if ((d->left.flags & CMP_SINGLEVAL) && !(d->left.flags & CMP_DYNAMICVAL))
227     {
228         if (d->left.flags & CMP_REALVAL)
229         {
230             fprintf(fp, "%f ", d->left.r[0]);
231         }
232         else
233         {
234             fprintf(fp, "%d ", d->left.i[0]);
235         }
236     }
237     /* Print the operator */
238     if (d->cmpt != CMP_INVALID)
239     {
240         fprintf(fp, "%s", comparison_type_str(d->cmpt));
241     }
242     else
243     {
244         fprintf(fp, "%s", d->cmpop);
245     }
246     /* Print the right value */
247     if ((d->right.flags & CMP_SINGLEVAL) && !(d->right.flags & CMP_DYNAMICVAL))
248     {
249         if (d->right.flags & CMP_REALVAL)
250         {
251             fprintf(fp, " %f", d->right.r[0]);
252         }
253         else
254         {
255             fprintf(fp, " %d", d->right.i[0]);
256         }
257     }
258     fprintf(fp, "\"");
259 }
260
261 static void *
262 init_data_compare(int /* npar */, gmx_ana_selparam_t *param)
263 {
264     t_methoddata_compare *data;
265
266     snew(data, 1);
267     param[2].val.u.s = &data->cmpop;
268     return data;
269 }
270
271 /*! \brief
272  * Reverses a comparison operator.
273  *
274  * \param[in] type  Comparison operator to reverse.
275  * \returns   The correct comparison operator that equals \p type when the
276  *   left and right sides are interchanged.
277  */
278 static e_comparison_t
279 reverse_comparison_type(e_comparison_t type)
280 {
281     switch (type)
282     {
283         case CMP_LESS: return CMP_GTR;
284         case CMP_LEQ:  return CMP_GEQ;
285         case CMP_GTR:  return CMP_LESS;
286         case CMP_GEQ:  return CMP_LEQ;
287         default:       break;
288     }
289     return type;
290 }
291
292 /*! \brief
293  * Initializes the value storage for comparison expression.
294  *
295  * \param[out] val   Value structure to initialize.
296  * \param[in]  param Parameters to use for initialization.
297  * \returns    The number of values provided for the value, 0 on error.
298  */
299 static int
300 init_comparison_value(t_compare_value *val, gmx_ana_selparam_t param[2])
301 {
302     int  n;
303
304     val->flags = 0;
305     if (param[0].flags & SPAR_SET)
306     {
307         val->flags |=  (param[0].flags & SPAR_DYNAMIC) ? CMP_DYNAMICVAL : 0;
308         val->flags |= !(param[0].flags & SPAR_ATOMVAL) ? CMP_SINGLEVAL  : 0;
309         n           = param[0].val.nr;
310         val->i      = param[0].val.u.i;
311     }
312     else if (param[1].flags & SPAR_SET)
313     {
314         val->flags |=  (param[1].flags & SPAR_DYNAMIC) ? CMP_DYNAMICVAL : 0;
315         val->flags |= !(param[1].flags & SPAR_ATOMVAL) ? CMP_SINGLEVAL  : 0;
316         val->flags |= CMP_REALVAL;
317         n           = param[1].val.nr;
318         val->r      = param[1].val.u.r;
319     }
320     else
321     {
322         n           = 0;
323         val->i      = NULL;
324         val->r      = NULL;
325     }
326     return n;
327 }
328
329 /*! \brief
330  * Converts an integer value to floating point.
331  *
332  * \param[in]     n   Number of values in the \p val->u array.
333  * \param[in,out] val Value to convert.
334  */
335 static void
336 convert_int_real(int n, t_compare_value *val)
337 {
338     int   i;
339     real *rv;
340
341     snew(rv, n);
342     for (i = 0; i < n; ++i)
343     {
344         rv[i] = (real)val->i[i];
345     }
346     /* Free the previous value if one is present. */
347     sfree(val->r);
348     val->r      = rv;
349     val->flags |= CMP_REALVAL | CMP_ALLOCREAL;
350 }
351
352 /*! \brief
353  * Converts a floating point value to integer.
354  *
355  * \param[in]     n      Number of values in the \p val->u array.
356  * \param[in,out] val    Value to convert.
357  * \param[in]     cmpt   Comparison operator type.
358  * \param[in]     bRight true if \p val appears on the right hand size of
359  *   \p cmpt.
360  * \returns       0 on success, EINVAL on error.
361  *
362  * The values are rounded such that the same comparison operator can be used.
363  */
364 static void
365 convert_real_int(int n, t_compare_value *val, e_comparison_t cmpt, bool bRight)
366 {
367     int   i;
368     int  *iv;
369
370     if (!bRight)
371     {
372         cmpt = reverse_comparison_type(cmpt);
373     }
374     snew(iv, n);
375     /* Round according to the comparison type */
376     for (i = 0; i < n; ++i)
377     {
378         switch (cmpt)
379         {
380             case CMP_LESS:
381             case CMP_GEQ:
382                 iv[i] = static_cast<int>(std::ceil(val->r[i]));
383                 break;
384             case CMP_GTR:
385             case CMP_LEQ:
386                 iv[i] = static_cast<int>(std::floor(val->r[i]));
387                 break;
388             case CMP_EQUAL:
389             case CMP_NEQ:
390                 sfree(iv);
391                 /* TODO: Implement, although it isn't typically very useful.
392                  * Implementation is only a matter of proper initialization,
393                  * the evaluation function can already handle this case with
394                  * proper preparations. */
395                 GMX_THROW(gmx::NotImplementedError("Equality comparison between dynamic integer and static real expressions not implemented"));
396             case CMP_INVALID: /* Should not be reached */
397                 sfree(iv);
398                 GMX_THROW(gmx::InternalError("Invalid comparison type"));
399         }
400     }
401     /* Free the previous value if one is present. */
402     sfree(val->i);
403     val->i      = iv;
404     val->flags &= ~CMP_REALVAL;
405     val->flags |= CMP_ALLOCINT;
406 }
407
408 static void
409 init_compare(t_topology * /* top */, int /* npar */, gmx_ana_selparam_t *param, void *data)
410 {
411     t_methoddata_compare *d = (t_methoddata_compare *)data;
412     int                   n1, n2;
413
414     /* Store the values */
415     n1 = init_comparison_value(&d->left, &param[0]);
416     n2 = init_comparison_value(&d->right, &param[3]);
417     /* Store the comparison type */
418     d->cmpt = comparison_type(d->cmpop);
419     if (d->cmpt == CMP_INVALID)
420     {
421         GMX_THROW(gmx::InternalError("Invalid comparison type"));
422     }
423     /* Convert the values to the same type */
424     /* TODO: Currently, there are no dynamic integer-valued selection methods,
425      * which means that only the branches with convert_int_real() will ever be
426      * taken. It should be considered whether it is necessary to support these
427      * other cases at all.
428      */
429     if ((d->left.flags & CMP_REALVAL) && !(d->right.flags & CMP_REALVAL))
430     {
431         if (d->left.flags & d->right.flags & CMP_DYNAMICVAL)
432         {
433             /* Nothing can be done */
434         }
435         else if (!(d->right.flags & CMP_DYNAMICVAL))
436         {
437             convert_int_real(n2, &d->right);
438         }
439         else /* d->left is static */
440         {
441             convert_real_int(n1, &d->left, d->cmpt, false);
442         }
443     }
444     else if (!(d->left.flags & CMP_REALVAL) && (d->right.flags & CMP_REALVAL))
445     {
446         if (d->left.flags & d->right.flags & CMP_DYNAMICVAL)
447         {
448             /* Reverse the sides to place the integer on the right */
449             int    flags;
450             d->left.r      = d->right.r;
451             d->right.r     = NULL;
452             d->right.i     = d->left.i;
453             d->left.i      = NULL;
454             flags          = d->left.flags;
455             d->left.flags  = d->right.flags;
456             d->right.flags = flags;
457             d->cmpt        = reverse_comparison_type(d->cmpt);
458         }
459         else if (!(d->left.flags & CMP_DYNAMICVAL))
460         {
461             convert_int_real(n1, &d->left);
462         }
463         else /* d->right is static */
464         {
465             convert_real_int(n2, &d->right, d->cmpt, true);
466         }
467     }
468 }
469
470 /*!
471  * \param data Data to free (should point to a \c t_methoddata_compare).
472  *
473  * Frees the memory allocated for \c t_methoddata_compare.
474  */
475 static void
476 free_data_compare(void *data)
477 {
478     t_methoddata_compare *d = (t_methoddata_compare *)data;
479
480     sfree(d->cmpop);
481     if (d->left.flags & CMP_ALLOCINT)
482     {
483         sfree(d->left.i);
484     }
485     if (d->left.flags & CMP_ALLOCREAL)
486     {
487         sfree(d->left.r);
488     }
489     if (d->right.flags & CMP_ALLOCINT)
490     {
491         sfree(d->right.i);
492     }
493     if (d->right.flags & CMP_ALLOCREAL)
494     {
495         sfree(d->right.r);
496     }
497     sfree(d);
498 }
499
500 /*! \brief
501  * Implementation for evaluate_compare() for integer values.
502  *
503  * \param[in]  top   Not used.
504  * \param[in]  fr    Not used.
505  * \param[in]  pbc   Not used.
506  * \param[in]  g     Evaluation index group.
507  * \param[out] out   Output data structure (\p out->u.g is used).
508  * \param[in]  data  Should point to a \c t_methoddata_compare.
509  */
510 static void
511 evaluate_compare_int(t_topology *top, t_trxframe *fr, t_pbc *pbc,
512                      gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data)
513 {
514     t_methoddata_compare *d = (t_methoddata_compare *)data;
515     int                   i, i1, i2, ig;
516     int                   a, b;
517     bool                  bAccept;
518
519     GMX_UNUSED_VALUE(top);
520     GMX_UNUSED_VALUE(fr);
521     GMX_UNUSED_VALUE(pbc);
522     for (i = i1 = i2 = ig = 0; i < g->isize; ++i)
523     {
524         a       = d->left.i[i1];
525         b       = d->right.i[i2];
526         bAccept = false;
527         switch (d->cmpt)
528         {
529             case CMP_INVALID: break;
530             case CMP_LESS:    bAccept = a <  b; break;
531             case CMP_LEQ:     bAccept = a <= b; break;
532             case CMP_GTR:     bAccept = a >  b; break;
533             case CMP_GEQ:     bAccept = a >= b; break;
534             case CMP_EQUAL:   bAccept = a == b; break;
535             case CMP_NEQ:     bAccept = a != b; break;
536         }
537         if (bAccept)
538         {
539             out->u.g->index[ig++] = g->index[i];
540         }
541         if (!(d->left.flags & CMP_SINGLEVAL))
542         {
543             ++i1;
544         }
545         if (!(d->right.flags & CMP_SINGLEVAL))
546         {
547             ++i2;
548         }
549     }
550     out->u.g->isize = ig;
551 }
552
553 /*! \brief
554  * Implementation for evaluate_compare() if either value is non-integer.
555  *
556  * \param[in]  top   Not used.
557  * \param[in]  fr    Not used.
558  * \param[in]  pbc   Not used.
559  * \param[in]  g     Evaluation index group.
560  * \param[out] out   Output data structure (\p out->u.g is used).
561  * \param[in]  data  Should point to a \c t_methoddata_compare.
562  *
563  * Left value is assumed to be real-valued; right value can be either.
564  * This is ensured by the initialization method.
565  */
566 static void
567 evaluate_compare_real(t_topology *top, t_trxframe *fr, t_pbc *pbc,
568                       gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data)
569 {
570     t_methoddata_compare *d = (t_methoddata_compare *)data;
571     int                   i, i1, i2, ig;
572     real                  a, b;
573     bool                  bAccept;
574
575     GMX_UNUSED_VALUE(top);
576     GMX_UNUSED_VALUE(fr);
577     GMX_UNUSED_VALUE(pbc);
578     for (i = i1 = i2 = ig = 0; i < g->isize; ++i)
579     {
580         a       = d->left.r[i1];
581         b       = (d->right.flags & CMP_REALVAL) ? d->right.r[i2] : d->right.i[i2];
582         bAccept = false;
583         switch (d->cmpt)
584         {
585             case CMP_INVALID: break;
586             case CMP_LESS:    bAccept = a <  b; break;
587             case CMP_LEQ:     bAccept = a <= b; break;
588             case CMP_GTR:     bAccept = a >  b; break;
589             case CMP_GEQ:     bAccept = a >= b; break;
590             case CMP_EQUAL:   bAccept =  gmx_within_tol(a, b, GMX_REAL_EPS); break;
591             case CMP_NEQ:     bAccept = !gmx_within_tol(a, b, GMX_REAL_EPS); break;
592         }
593         if (bAccept)
594         {
595             out->u.g->index[ig++] = g->index[i];
596         }
597         if (!(d->left.flags & CMP_SINGLEVAL))
598         {
599             ++i1;
600         }
601         if (!(d->right.flags & CMP_SINGLEVAL))
602         {
603             ++i2;
604         }
605     }
606     out->u.g->isize = ig;
607 }
608
609 /*!
610  * \param[in]  top   Not used.
611  * \param[in]  fr    Not used.
612  * \param[in]  pbc   Not used.
613  * \param[in]  g     Evaluation index group.
614  * \param[out] out   Output data structure (\p out->u.g is used).
615  * \param[in]  data  Should point to a \c t_methoddata_compare.
616  */
617 static void
618 evaluate_compare(t_topology *top, t_trxframe *fr, t_pbc *pbc,
619                  gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data)
620 {
621     t_methoddata_compare *d = (t_methoddata_compare *)data;
622
623     if (!((d->left.flags | d->right.flags) & CMP_REALVAL))
624     {
625         evaluate_compare_int(top, fr, pbc, g, out, data);
626     }
627     else
628     {
629         evaluate_compare_real(top, fr, pbc, g, out, data);
630     }
631 }