View Javadoc
1   // ******************************************************************************
2   //
3   // Title:       Force Field X.
4   // Description: Force Field X - Software for Molecular Biophysics.
5   // Copyright:   Copyright (c) Michael J. Schnieders 2001-2024.
6   //
7   // This file is part of Force Field X.
8   //
9   // Force Field X is free software; you can redistribute it and/or modify it
10  // under the terms of the GNU General Public License version 3 as published by
11  // the Free Software Foundation.
12  //
13  // Force Field X is distributed in the hope that it will be useful, but WITHOUT
14  // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15  // FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
16  // details.
17  //
18  // You should have received a copy of the GNU General Public License along with
19  // Force Field X; if not, write to the Free Software Foundation, Inc., 59 Temple
20  // Place, Suite 330, Boston, MA 02111-1307 USA
21  //
22  // Linking this library statically or dynamically with other modules is making a
23  // combined work based on this library. Thus, the terms and conditions of the
24  // GNU General Public License cover the whole combination.
25  //
26  // As a special exception, the copyright holders of this library give you
27  // permission to link this library with independent modules to produce an
28  // executable, regardless of the license terms of these independent modules, and
29  // to copy and distribute the resulting executable under terms of your choice,
30  // provided that you also meet, for each linked independent module, the terms
31  // and conditions of the license of that module. An independent module is a
32  // module which is not derived from or based on this library. If you modify this
33  // library, you may extend this exception to your version of the library, but
34  // you are not obligated to do so. If you do not wish to do so, delete this
35  // exception statement from your version.
36  //
37  // ******************************************************************************
38  package ffx.numerics.integrate;
39  
40  import javax.annotation.Nullable;
41  
42  import static java.lang.String.format;
43  import static java.util.Arrays.asList;
44  import static java.util.Arrays.copyOf;
45  import static java.util.Arrays.fill;
46  import static java.util.Arrays.stream;
47  
48  import java.util.List;
49  
50  /**
51   * A CompositeCurve represents points along a sum of functions which also extend FunctionDataCurve.
52   *
53   * @author Jacob M. Litman
54   */
55  public class CompositeCurve extends FunctionDataCurve {
56  
57    /** Array of FunctionDataCurve instances. */
58    private final FunctionDataCurve[] curves;
59    /** Array of coefficients for each curve. */
60    private final double[] coefficients;
61    /** Number of curves. */
62    private final int nCurves;
63  
64    /**
65     * Constructs a CompositeCurve that aggregates multiple FunctionDataCurves with variable weights to
66     * each component FunctionDataCurve.
67     *
68     * @param componentCurves Underlying FunctionDataCurves
69     * @param coefficients Weight to each component curve
70     */
71    public CompositeCurve(List<FunctionDataCurve> componentCurves, @Nullable List<Double> coefficients) {
72      assert !componentCurves.isEmpty();
73  
74      nCurves = componentCurves.size();
75      this.curves = new FunctionDataCurve[nCurves];
76      componentCurves.toArray(this.curves);
77      assert (coefficients == null || nCurves == coefficients.size());
78  
79      if (coefficients == null) {
80        this.coefficients = new double[nCurves];
81        fill(this.coefficients, 1.0);
82      } else {
83        this.coefficients = coefficients.stream().mapToDouble(Double::doubleValue).toArray();
84      }
85  
86      FunctionDataCurve curve0 = curves[0];
87      lb = curve0.lowerBound();
88      ub = curve0.upperBound();
89      halfWidthEnd = curve0.halfWidthEnds();
90      x = curve0.getX();
91      double sep = curve0.binWidth();
92  
93      boolean isInvalid =
94          stream(curves)
95              .anyMatch(
96                  (FunctionDataCurve c) -> {
97                    if (lb != c.lowerBound()) {
98                      return true;
99                    }
100                   if (ub != c.upperBound()) {
101                     return true;
102                   }
103                   if (halfWidthEnd != c.halfWidthEnds()) {
104                     return true;
105                   }
106                   return sep != c.binWidth();
107                 });
108     if (isInvalid) {
109       throw new IllegalArgumentException(
110           " Not all curves passed to CompositeCurve had the same x[] points!");
111     }
112 
113     int nPoints = curve0.numPoints();
114     points = new double[nPoints];
115     for (int i = 0; i < nPoints; i++) {
116       points[i] = 0.0;
117       for (int j = 0; j < nCurves; j++) {
118         points[i] += (this.coefficients[j] * curves[j].getFxPoint(i));
119       }
120     }
121   }
122 
123   /** {@inheritDoc} */
124   @Override
125   public double fX(double x) {
126     return valAt(x);
127   }
128 
129   /**
130    * Gets the component FunctionDataCurves of this CompositeCurve.
131    *
132    * @return List of component FunctionDataCurves.
133    */
134   public List<FunctionDataCurve> getSubCurves() {
135     return asList(curves);
136   }
137 
138   /**
139    * Gets the weights to the corresponding component curves.
140    *
141    * @return Constant weights
142    */
143   public double[] getWeights() {
144     return copyOf(coefficients, coefficients.length);
145   }
146 
147   /** {@inheritDoc} */
148   @Override
149   public double integralAt(double x) {
150     double val = 0.0;
151     for (int i = 0; i < nCurves; i++) {
152       val += (curves[i].integralAt(x) * coefficients[i]);
153     }
154     return val;
155   }
156 
157   /** {@inheritDoc} */
158   @Override
159   public String toString() {
160     StringBuilder sb =
161         new StringBuilder(
162             format(
163                 "Composite curve with %d points from lower bound %9.3g and upper bound %9.3g",
164                 points.length, lb, ub));
165     if (halfWidthEnd) {
166       sb.append(" and half-width start/end bins.\nComponent curves:\n");
167     }
168     for (FunctionDataCurve curve : curves) {
169       sb.append(curve.toString());
170     }
171     return sb.toString();
172   }
173 
174   // Private, non-overrideable method for use in the constructor.
175   private double valAt(double x) {
176     double val = 0.0;
177     for (int i = 0; i < nCurves; i++) {
178       val += (curves[i].fX(x) * coefficients[i]);
179     }
180     return val;
181   }
182 }