NEML2 1.4.0
Loading...
Searching...
No Matches
ComposedModel.cxx
1// Copyright 2023, 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/ComposedModel.h"
26
27namespace neml2
28{
29register_NEML2_object(ComposedModel);
30
33{
35 options.doc() =
36 "Compose multiple models together to form a single model. The composed model can then be "
37 "treated as a new model and composed with others. The [system documentation](@ref "
38 "model-composition) provides in-depth explanation on how the models are composed together.";
39
41
42 options.set<std::vector<std::string>>("models");
43 options.set("models").doc() = "Models being composed together";
44
45 options.set<std::vector<VariableName>>("additional_outputs");
46 options.set("additional_outputs").doc() =
47 "Extra output variables to be extracted from the composed model in addition to the ones "
48 "identified through dependency resolution.";
49
50 options.set<std::vector<std::string>>("priority");
51 options.set("priority").doc() =
52 "Priorities of models in decreasing order. A model with higher priority will be evaluated "
53 "first. This is useful for breaking cyclic dependency.";
54
55 return options;
56}
57
59 : Model(options),
60 _additional_outputs(options.get<std::vector<VariableName>>("additional_outputs"))
61{
62 for (const auto & model_name : options.get<std::vector<std::string>>("models"))
63 {
64 // Each sub-model shall have _independent_ output storage. This is because the same model could
65 // be registered as a sub-model by different models, and it could be evaluated with _different_
66 // input, and hence yields _different_ output values.
67 const auto & submodel =
68 register_model<Model>(model_name, 0, /*nonlinear=*/false, /*merge_input=*/false);
70
71 // Each sub-model may have nonlinear parameters. In our design, nonlinear parameters _are_
72 // models. Since we do not want to put the burden of adding nonlinear parameters in the input
73 // file through the option 'models', we should do more behind the scenes to register them for
74 // the user.
75 //
76 // Registering nonlinear parameters here ensures dependency resolution. And if a nonlinear
77 // parameter is registered by multiple models (which is very possible), we won't have to
78 // evaluate the nonlinar parameter over and over again!
80 }
81
82 // Add registered models as nodes in the dependency resolver
83 for (auto submodel : registered_models())
84 _dependency.add_node(submodel);
85 for (const auto & var : _additional_outputs)
86 _dependency.add_additional_outbound_item(var);
87
88 // Define priority in the event of cyclic dependency
89 auto priority_order = options.get<std::vector<std::string>>("priority");
90 size_t priority = priority_order.empty() ? 0 : priority_order.size() - 1;
91 for (const auto & model_name : priority_order)
92 _dependency.set_priority(registered_model(model_name), priority--);
93
94 // Resolve the dependency
95 _dependency.unique_item_provider() = true;
96 _dependency.unique_item_consumer() = false;
97 _dependency.resolve();
98
99 // Sort the registered models by dependency resolution
100 _registered_models = _dependency.resolution();
101
102 // Register input variables
103 for (const auto & item : _dependency.inbound_items())
104 {
105 auto var = item.value;
106 auto sz = item.parent->input_axis().storage_size(var);
107
110 "Multiple sub-models in a ComposedModel define the same input variable ",
111 var,
112 ", but with different shape/storage size.");
113 else
115 }
116
117 // Register output variables
118 for (const auto & item : _dependency.outbound_items())
119 {
120 auto var = item.value;
121 auto sz = item.parent->output_axis().storage_size(var);
122 neml_assert(!output_axis().has_variable(var),
123 "Multiple sub-models in a ComposedModel define the same output variable ",
124 var);
125
127 }
128}
129
130void
132{
133 for (auto && [pname, param] : m.nl_params())
134 {
135 neml_assert_dbg(param->name().size() == 1, "Internal parameter name error");
136
137 auto submodel = Factory::get_object_ptr<Model>("Models", param->name().vec()[0]);
138 _registered_models.push_back(submodel.get());
139
140 // Nonlinear parameters could be nested...
142 }
143}
144
145void
147{
149 throw NEMLException(
150 "ComposedModel does not use automatic differentiation. _use_AD_first_derivative and "
151 "_use_AD_second_derivative should be set to false.");
152}
153
154void
162
163void
165{
166 for (auto submodel : registered_models())
167 {
168 for (const auto & item : _dependency.inbound_items())
169 if (item.parent == submodel)
170 item.parent->input_view(item.value)->setup_views(input_view(item.value));
171
172 for (const auto & [item, providers] : _dependency.item_providers())
173 if (item.parent == submodel)
174 item.parent->input_view(item.value)
175 ->setup_views(&providers.begin()->parent->output_storage());
176
177 submodel->setup_submodel_input_views();
178 }
179}
180
181void
183{
184 clear_chain_rule_cache();
185
186 for (auto i : registered_models())
187 {
188 if (out && !dout_din && !d2out_din2)
189 i->value();
190 else if (out && dout_din && !d2out_din2)
191 i->value_and_dvalue();
192 else if (out && dout_din && d2out_din2)
193 i->value_and_dvalue_and_d2value();
194 else
195 throw NEMLException("Unsupported call signature to set_value");
196
197 if (dout_din && !d2out_din2)
198 apply_chain_rule(i);
199
200 if (d2out_din2)
201 apply_second_order_chain_rule(i);
202 }
203
204 for (auto model : _dependency.end_nodes())
205 {
206 if (out)
207 output_storage().fill(model->output_storage());
208
209 if (dout_din)
210 derivative_storage().fill(_dpout_din[model]);
211
212 if (d2out_din2)
213 second_derivative_storage().fill(_d2pout_din2[model]);
214 }
215
216 clear_chain_rule_cache();
217}
218
219void
220ComposedModel::apply_chain_rule(Model * i)
221{
222 auto dpin_din = LabeledMatrix::empty(batch_sizes(), {&i->input_axis(), &input_axis()}, options());
223 dpin_din.fill(_din_din);
224
225 if (_dependency.node_providers().count(i))
226 for (auto dep : _dependency.node_providers().at(i))
227 dpin_din.fill(_dpout_din[dep]);
228
229 _dpout_din[i] = i->derivative_storage().chain(dpin_din);
230}
231
232void
233ComposedModel::apply_second_order_chain_rule(Model * i)
234{
235 auto dpin_din = LabeledMatrix::empty(batch_sizes(), {&i->input_axis(), &input_axis()}, options());
236 auto d2pin_din2 = LabeledTensor3D::zeros(
237 batch_sizes(), {&i->input_axis(), &input_axis(), &input_axis()}, options());
238 dpin_din.fill(_din_din);
239
240 if (_dependency.node_providers().count(i))
241 for (auto dep : _dependency.node_providers().at(i))
242 {
243 dpin_din.fill(_dpout_din[dep]);
244 d2pin_din2.fill(_d2pout_din2[dep]);
245 }
246
247 _dpout_din[i] = i->derivative_storage().chain(dpin_din);
248 _d2pout_din2[i] =
249 i->second_derivative_storage().chain(d2pin_din2, i->derivative_storage(), dpin_din);
250}
251
252} // namespace neml2
Definition ComposedModel.h:33
virtual void setup_submodel_input_views() override
Definition ComposedModel.cxx:164
virtual void check_AD_limitation() const override
Definition ComposedModel.cxx:146
virtual void allocate_variables(int deriv_order, bool options_changed) override
Call VariableStore::allocate_variables recursively on all submodels.
Definition ComposedModel.cxx:155
void register_nonlinear_params(const Model &m)
Recursively register sub-model's nonlinar parameters.
Definition ComposedModel.cxx:131
static OptionSet expected_options()
Definition ComposedModel.cxx:32
void set_value(bool, bool, bool) override
The map between input -> output, and optionally its derivatives.
Definition ComposedModel.cxx:182
The wrapper (decorator) for cross-referencing unresolved values at parse time.
Definition CrossRef.h:52
The accessor containing all the information needed to access an item in a LabeledAxis.
Definition LabeledAxisAccessor.h:44
TorchSize storage_size() const
Get the (total) storage size of this axis.
Definition LabeledAxis.h:142
bool has_variable(const LabeledAxisAccessor &var) const
Does the variable of a given primitive type exist?
Definition LabeledAxis.h:126
LabeledMatrix chain(const LabeledMatrix &other) const
Chain rule product of two derivatives.
Definition LabeledMatrix.cxx:61
static LabeledMatrix identity(TorchShapeRef batch_size, const LabeledAxis &axis, const torch::TensorOptions &options=default_tensor_options())
Create a labeled identity tensor.
Definition LabeledMatrix.cxx:34
void fill(const LabeledMatrix &other, bool recursive=true)
Definition LabeledMatrix.cxx:52
void fill(const LabeledTensor3D &other, bool recursive=true)
Definition LabeledTensor3D.cxx:43
static LabeledTensor3D zeros(TorchShapeRef batch_shape, const std::vector< const LabeledAxis * > &axes, const torch::TensorOptions &options=default_tensor_options())
Setup new storage with zeros.
Definition LabeledTensor.cxx:109
static LabeledMatrix empty(TorchShapeRef batch_shape, const std::vector< const LabeledAxis * > &axes, const torch::TensorOptions &options=default_tensor_options())
Setup new empty storage.
Definition LabeledTensor.cxx:87
void fill(const LabeledVector &other, bool recursive=true)
Definition LabeledVector.cxx:45
The base class for all constitutive models.
Definition Model.h:53
friend class ComposedModel
ComposedModel's set_value need to call submodel's set_value.
Definition Model.h:194
TorchShapeRef batch_sizes() const
This model's batch shape.
Definition Model.h:113
const std::vector< Model * > & registered_models() const
The models that may be used during the evaluation of this model.
Definition Model.h:119
std::vector< Model * > _registered_models
Models this model may use during its evaluation.
Definition Model.h:275
bool _AD_2nd_deriv
Whether to use AD to compute 2nd derivatives.
Definition Model.h:281
virtual void allocate_variables(int deriv_order, bool options_changed)
Call VariableStore::allocate_variables recursively on all submodels.
Definition Model.cxx:159
const torch::TensorOptions & options() const
This model's tensor options.
Definition Model.h:116
bool _AD_1st_deriv
Whether to use AD to compute 1st derivatives.
Definition Model.h:278
static OptionSet expected_options()
Definition Model.cxx:33
Model * registered_model(const std::string &name) const
Get a registered model by its name.
Definition Model.cxx:423
Definition error.h:33
static void enable_automatic_scaling(OptionSet &options)
Definition NonlinearSystem.cxx:62
A custom map-like data structure. The keys are strings, and the values can be nonhomogeneously typed.
Definition OptionSet.h:59
LabeledMatrix & derivative_storage()
Definition VariableStore.h:127
Variable< T > & declare_output_variable(S &&... name)
Declare an output variable.
Definition VariableStore.h:205
LabeledVector & output_storage()
Definition VariableStore.h:121
const Variable< T > & declare_input_variable(S &&... name)
Declare an input variable.
Definition VariableStore.h:180
LabeledAxis & output_axis()
Definition VariableStore.h:97
VariableBase * input_view(const VariableName &)
Get the view of an input variable.
Definition VariableStore.cxx:57
LabeledTensor3D & second_derivative_storage()
Definition VariableStore.h:133
LabeledAxis & input_axis()
Definition VariableStore.h:91
Definition CrossRef.cxx:32
void neml_assert_dbg(bool assertion, Args &&... args)
Definition error.h:85
void neml_assert(bool assertion, Args &&... args)
Definition error.h:73