LCOV - code coverage report
Current view: top level - pathbias - ConditionalPath.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 38 43 88.4 %
Date: 2026-11-05 10:19:04 Functions: 3 4 75.0 %

          Line data    Source code
       1             : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
       2             :    Copyright (c) 2026 Pengchao Zhang
       3             : 
       4             :    See http://www.plumed.org for more information.
       5             : 
       6             :    This file is part of plumed, version 2.
       7             : 
       8             :    plumed is free software: you can redistribute it and/or modify
       9             :    it under the terms of the GNU Lesser General Public License as published by
      10             :    the Free Software Foundation, either version 3 of the License, or
      11             :    (at your option) any later version.
      12             : 
      13             :    plumed is distributed in the hope that it will be useful,
      14             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      15             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      16             :    GNU Lesser General Public License for more details.
      17             : 
      18             :    You should have received a copy of the GNU Lesser General Public License
      19             :    along with plumed.  If not, see <http://www.gnu.org/licenses/>.
      20             : +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
      21             : #include "function/Function.h"
      22             : #include "core/ActionRegister.h"
      23             : #include <algorithm>
      24             : #include <cmath>
      25             : 
      26             : namespace PLMD {
      27             : namespace pathbias {
      28             : 
      29             : //+PLUMEDOC FUNCTION CONDITIONAL_PATH
      30             : /*
      31             : Evaluate a fixed conditional path log mixture, with its full chain rule.
      32             : 
      33             : For positive path score a and positive conditional normalizer m(c), this
      34             : function returns log R = log[(1-lambda)+lambda*a/m(c)]. ARG must contain
      35             : exactly three nonperiodic scalars: c, log(a), log(m(c)), in that order.
      36             : COUPLING is lambda in [0,1). LOWER and UPPER bound the admitted domain of c;
      37             : evaluation outside this closed interval fails instead of extrapolating.
      38             : 
      39             : The derivatives with respect to the three arguments are (0,chi,-chi),
      40             : where chi=lambda*a/(m*R). The explicit c argument only checks the domain.
      41             : The normalizer argument MUST retain its dependence on c: freezing a fitted
      42             : function does not mean dropping its derivative. All functions must be fixed
      43             : during production. This action does not learn m or preserve external file
      44             : identity across restarts. Verify frozen input/model hashes before restarting.
      45             : 
      46             : A bias V=B_c-kBT*log R is built using CUSTOM and BIASVALUE. kBT must be in
      47             : the same energy units as B_c. With an exact m=E_0[a|c], the selected c marginal
      48             : of the fixed B_c ensemble is preserved. Approximate m still defines a fixed
      49             : potential with full-path weight exp(beta*V), but does not imply that marginal
      50             : identity or any efficiency improvement. Every bead in one path shares the
      51             : same path weight; beads are not independent samples.
      52             : 
      53             : The following two-replica example uses a mean Cartesian distance component,
      54             : then squares it, rather than averaging the squared components. It assumes
      55             : consistent nonperiodic coordinate images across the replicas. This scalar
      56             : example does not implement arbitrary molecular Cartesian-centroid CVs.
      57             : 
      58             : ```plumed
      59             : #SETTINGS NREPLICAS=2
      60             : d: DISTANCE ATOMS=1,2 COMPONENTS NOPBC
      61             : mean: ENSEMBLE ARG=d.x
      62             : c: CUSTOM ARG=mean.d.x FUNC=x*x PERIODIC=NO
      63             : h: CUSTOM ARG=d.x FUNC=exp(-x*x/2) PERIODIC=NO
      64             : fraction: ENSEMBLE ARG=h
      65             : loga: CUSTOM ARG=fraction.h FUNC=log(0.1+x) PERIODIC=NO
      66             : logm: CUSTOM ARG=c FUNC=-1+0.2*x PERIODIC=NO
      67             : ratio: CONDITIONAL_PATH ARG=c,loga,logm COUPLING=0.5 LOWER=0 UPPER=4
      68             : correction: CUSTOM ARG=ratio FUNC=-2.5*x PERIODIC=NO
      69             : bias: BIASVALUE ARG=correction
      70             : PRINT ARG=c,loga,logm,ratio,bias.* FILE=COLVAR
      71             : ```
      72             : 
      73             : The illustrative logm above is not a fitted conditional reference. Supply a
      74             : qualified frozen normalizer and a separate fixed centroid bias for production.
      75             : The molecular dynamics adapter must provide consistent dynamical force
      76             : scaling and single-path energy accounting. This action does not apply
      77             : additional factors of the bead count.
      78             : */
      79             : //+ENDPLUMEDOC
      80             : 
      81             : class ConditionalPath : public function::Function {
      82             :   double coupling;
      83             :   double lower;
      84             :   double upper;
      85             : public:
      86             :   explicit ConditionalPath(const ActionOptions&);
      87             :   static void registerKeywords(Keywords& keys);
      88             :   void calculate() override;
      89             : };
      90             : 
      91             : PLUMED_REGISTER_ACTION(ConditionalPath,"CONDITIONAL_PATH")
      92             : 
      93          92 : void ConditionalPath::registerKeywords(Keywords& keys) {
      94          92 :   Function::registerKeywords(keys);
      95          92 :   keys.add("compulsory","COUPLING","mixture fraction in the half-open interval [0,1)");
      96          92 :   keys.add("compulsory","LOWER","inclusive lower limit of the conditioning argument");
      97          92 :   keys.add("compulsory","UPPER","inclusive upper limit of the conditioning argument");
      98         184 :   keys.setValueDescription("scalar","the dimensionless conditional path log mixture");
      99          92 : }
     100             : 
     101          88 : ConditionalPath::ConditionalPath(const ActionOptions& ao):
     102             :   Action(ao),
     103             :   Function(ao),
     104          88 :   coupling(0),
     105          88 :   lower(0),
     106          88 :   upper(0) {
     107          88 :   parse("COUPLING",coupling);
     108          88 :   parse("LOWER",lower);
     109         176 :   parse("UPPER",upper);
     110             :   if(getNumberOfArguments()!=3) {
     111           0 :     error("ARG must contain conditioning coordinate, log score, and log normalizer");
     112             :   }
     113         352 :   for(unsigned i=0; i<3; ++i) {
     114         264 :     if(getPntrToArgument(i)->getRank()!=0 || getPntrToArgument(i)->isPeriodic()) {
     115           0 :       error("arguments must be nonperiodic scalars");
     116             :     }
     117             :   }
     118          88 :   if(!std::isfinite(coupling) || coupling<0 || coupling>=1) {
     119          16 :     error("COUPLING must be finite and in [0,1)");
     120             :   }
     121          72 :   if(!std::isfinite(lower) || !std::isfinite(upper) || lower>=upper) {
     122           0 :     error("LOWER and UPPER must be finite and strictly increasing");
     123             :   }
     124          72 :   checkRead();
     125          72 :   addValueWithDerivatives();
     126          72 :   setNotPeriodic();
     127          72 :   log.printf("  fixed conditional path mixture with coupling %g in [%g,%g]\n",
     128             :              coupling,lower,upper);
     129         104 : }
     130             : 
     131         240 : void ConditionalPath::calculate() {
     132         240 :   const double c=getArgument(0), loga=getArgument(1), logm=getArgument(2);
     133         240 :   if(!std::isfinite(c) || !std::isfinite(loga) || !std::isfinite(logm)) {
     134           0 :     error("conditional path arguments must be finite");
     135             :   }
     136         240 :   if(c<lower || c>upper) {
     137           8 :     error("conditioning coordinate is outside the frozen model domain");
     138             :   }
     139         232 :   double logratio=0, chi=0;
     140         232 :   if(coupling>0) {
     141         160 :     const double lhs=std::log1p(-coupling);
     142         160 :     const double rhs=std::log(coupling)+loga-logm;
     143         160 :     if(!std::isfinite(rhs)) {
     144           0 :       error("conditional log ratio exceeds floating-point range");
     145             :     }
     146         160 :     logratio=std::max(lhs,rhs)+std::log1p(std::exp(-std::abs(lhs-rhs)));
     147         160 :     chi=std::exp(rhs-logratio);
     148             :   }
     149         232 :   setValue(logratio);
     150             :   setDerivative(0,0);
     151             :   setDerivative(1,chi);
     152             :   setDerivative(2,-chi);
     153         232 : }
     154             : 
     155             : }
     156             : }

Generated by: LCOV version 1.16