LCOV - code coverage report
Current view: top level - models - LinearCombination.cxx (source / functions) Coverage Total Hit
Test: coverage.info Lines: 100.0 % 64 64
Test Date: 2025-10-02 16:03:03 Functions: 66.7 % 12 8

            Line data    Source code
       1              : // Copyright 2024, UChicago Argonne, LLC
       2              : // All Rights Reserved
       3              : // Software Name: NEML2 -- the New Engineering material Model Library, version 2
       4              : // By: Argonne National Laboratory
       5              : // OPEN SOURCE LICENSE (MIT)
       6              : //
       7              : // Permission is hereby granted, free of charge, to any person obtaining a copy
       8              : // of this software and associated documentation files (the "Software"), to deal
       9              : // in the Software without restriction, including without limitation the rights
      10              : // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
      11              : // copies of the Software, and to permit persons to whom the Software is
      12              : // furnished to do so, subject to the following conditions:
      13              : //
      14              : // The above copyright notice and this permission notice shall be included in
      15              : // all copies or substantial portions of the Software.
      16              : //
      17              : // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
      18              : // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      19              : // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
      20              : // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      21              : // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
      22              : // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
      23              : // THE SOFTWARE.
      24              : 
      25              : #include "neml2/models/LinearCombination.h"
      26              : #include "neml2/tensors/Scalar.h"
      27              : #include "neml2/tensors/Vec.h"
      28              : #include "neml2/tensors/R2.h"
      29              : #include "neml2/tensors/SR2.h"
      30              : #include "neml2/tensors/SSR4.h"
      31              : #include "neml2/misc/assertions.h"
      32              : 
      33              : namespace neml2
      34              : {
      35              : template <typename T>
      36              : OptionSet
      37            9 : LinearCombination<T>::expected_options()
      38              : {
      39              :   // This is the only way of getting tensor type in a static method like this...
      40              :   // Trim 6 chars to remove 'neml2::'
      41            9 :   auto tensor_type = utils::demangle(typeid(T).name()).substr(7);
      42              : 
      43            9 :   OptionSet options = Model::expected_options();
      44            9 :   options.doc() =
      45              :       "Calculate linear combination of multiple " + tensor_type +
      46              :       " tensors as \\f$ u = c_i v_i + s \\f$ (Einstein summation assumed), where \\f$ c_i "
      47              :       "\\f$ are the coefficients, and \\f$ v_i \\f$ are the variables to be summed. \\f$ s \\f$ is "
      48              :       "a constant offset.";
      49              : 
      50           18 :   options.set<bool>("define_second_derivatives") = true;
      51              : 
      52            9 :   options.set<std::vector<VariableName>>("from_var");
      53           27 :   options.set("from_var").doc() = tensor_type + " tensors to be summed";
      54              : 
      55           18 :   options.set_output("to_var");
      56            9 :   options.set("to_var").doc() = "The sum";
      57              : 
      58           27 :   options.set_parameter<std::vector<TensorName<Scalar>>>("coefficients") = {
      59              :       TensorName<Scalar>("1")};
      60           18 :   options.set("coefficients").doc() =
      61              :       "Weights associated with each variable. This option takes a list of weights, one for each "
      62              :       "coefficient. When the length of this list is 1, the same weight applies to all "
      63              :       "coefficients.";
      64              : 
      65           36 :   options.set_parameter<TensorName<Scalar>>("constant_coefficient") = {TensorName<Scalar>("0")};
      66           18 :   options.set("constant_coefficient").doc() =
      67              :       "The constant coefficient added to the final summation";
      68              : 
      69           18 :   options.set<bool>("constant_coefficient_as_parameter") = false;
      70            9 :   options.set("constant_coefficient_as_parameter").doc() =
      71              :       "By default, the constant_coefficient are declared as buffers. Set this option to true to "
      72              :       "declare "
      73              :       "them as (trainable) parameters.";
      74              : 
      75           27 :   options.set<std::vector<bool>>("coefficient_as_parameter") = {false};
      76            9 :   options.set("coefficient_as_parameter").doc() =
      77              :       "By default, the coefficients are declared as buffers. Set this option to true to declare "
      78              :       "them as (trainable) parameters. This option takes a list of booleans, one for each "
      79              :       "coefficient. When the length of this list is 1, the boolean applies to all coefficients.";
      80              : 
      81           18 :   return options;
      82           45 : }
      83              : 
      84              : template <typename T>
      85           25 : LinearCombination<T>::LinearCombination(const OptionSet & options)
      86              :   : Model(options),
      87           25 :     _to(declare_output_variable<T>("to_var"))
      88              : {
      89          101 :   for (const auto & fv : options.get<std::vector<VariableName>>("from_var"))
      90           51 :     _from.push_back(&declare_input_variable<T>(fv));
      91              : 
      92           25 :   auto coef_as_param = options.get<std::vector<bool>>("coefficient_as_parameter");
      93           25 :   neml_assert(coef_as_param.size() == 1 || coef_as_param.size() == _from.size(),
      94              :               "Expected 1 or ",
      95           25 :               _from.size(),
      96              :               " entries in coefficient_as_parameter, got ",
      97           25 :               coef_as_param.size(),
      98              :               ".");
      99              : 
     100              :   // Expand the list of booleans to match the number of coefficients
     101           25 :   if (coef_as_param.size() == 1)
     102           19 :     coef_as_param = std::vector<bool>(_from.size(), coef_as_param[0]);
     103              : 
     104           25 :   const auto coef_refs = options.get<std::vector<TensorName<Scalar>>>("coefficients");
     105           25 :   neml_assert(coef_refs.size() == 1 || coef_refs.size() == _from.size(),
     106              :               "Expected 1 or ",
     107           25 :               _from.size(),
     108              :               " coefficients, got ",
     109           25 :               coef_refs.size(),
     110              :               ".");
     111              : 
     112              :   // Declare parameters or buffers
     113           25 :   _coefs.resize(_from.size());
     114           76 :   for (std::size_t i = 0; i < _from.size(); i++)
     115              :   {
     116           51 :     const auto & coef_ref = coef_refs.size() == 1 ? coef_refs[0] : coef_refs[i];
     117           51 :     if (coef_as_param[i])
     118           24 :       _coefs[i] =
     119           12 :           &declare_parameter<Scalar>("c_" + std::to_string(i), coef_ref, /*allow_nonlinear=*/true);
     120              :     else
     121           39 :       _coefs[i] = &declare_buffer<Scalar>("c_" + std::to_string(i), coef_ref);
     122              :   }
     123              : 
     124           50 :   if (options.user_specified("constant_coefficient"))
     125              :   {
     126            4 :     auto s_as_param = options.get<bool>("constant_coefficient_as_parameter");
     127            4 :     if (s_as_param)
     128           10 :       _s = &declare_parameter<Scalar>("s", "constant_coefficient", /*allow_nonlinear=*/true);
     129              :     else
     130           10 :       _s = &declare_buffer<Scalar>("s", "constant_coefficient");
     131              :   }
     132           25 : }
     133              : 
     134              : template <typename T>
     135              : void
     136           44 : LinearCombination<T>::set_value(bool out, bool dout_din, bool d2out_din2)
     137              : {
     138           44 :   if (out)
     139              :   {
     140           41 :     auto value = _s ? (*_s) + (*_coefs[0]) * (*_from[0]) : (*_coefs[0]) * (*_from[0]);
     141           82 :     for (std::size_t i = 1; i < _from.size(); i++)
     142           41 :       value = value + (*_coefs[i]) * (*_from[i]);
     143           41 :     _to = value;
     144           41 :   }
     145              : 
     146           44 :   if (dout_din)
     147              :   {
     148           25 :     const auto I = T::identity_map(_from[0]->options());
     149           75 :     for (std::size_t i = 0; i < _from.size(); i++)
     150              :     {
     151           50 :       if (_from[i]->is_dependent())
     152           48 :         _to.d(*_from[i]) = (*_coefs[i]) * I;
     153              : 
     154           50 :       if (const auto * const pi = nl_param("c_" + std::to_string(i)))
     155            7 :         _to.d(*pi) = (*_from[i]);
     156              :     }
     157           75 :     if (const auto * const s = nl_param("s"))
     158            2 :       _to.d(*s) = neml2::Scalar::full(1.0, _from[0]->options());
     159           25 :   }
     160              : 
     161              :   if (d2out_din2)
     162              :   {
     163              :     // zero
     164              :   }
     165           44 : }
     166              : 
     167              : #define REGISTER(T)                                                                                \
     168              :   using T##LinearCombination = LinearCombination<T>;                                               \
     169              :   register_NEML2_object(T##LinearCombination);                                                     \
     170              :   template class LinearCombination<T>
     171              : REGISTER(Scalar);
     172              : REGISTER(Vec);
     173              : REGISTER(SR2);
     174              : REGISTER(R2);
     175              : } // namespace neml2
        

Generated by: LCOV version 2.0-1