NEML2 1.4.0
Loading...
Searching...
No Matches
Model.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/Model.h"
26
27namespace neml2
28{
30
33{
37
38 options.section() = "Models";
39
40 options.set<bool>("_use_AD_first_derivative") = false;
41 options.set<bool>("_use_AD_second_derivative") = false;
42 options.set<int>("_extra_derivative_order") = 0;
43 options.set<bool>("_nonlinear_system") = false;
44
45 options.set("_extra_derivative_order").suppressed() = true;
46 options.set("_nonlinear_system").suppressed() = true;
47 options.set("_use_AD_first_derivative").suppressed() = true;
48 options.set("_use_AD_second_derivative").suppressed() = true;
49
50 return options;
51}
52
53Model::Model(const OptionSet & options)
54 : Data(options),
55 ParameterStore(options, this),
56 VariableStore(options, this),
57 NonlinearSystem(options),
58 _AD_1st_deriv(options.get<bool>("_use_AD_first_derivative")),
59 _AD_2nd_deriv(options.get<bool>("_use_AD_second_derivative")),
60 _options(default_tensor_options()),
61 _deriv_order(-1),
62 _extra_deriv_order(options.get<int>("_extra_derivative_order")),
63 _nonlinear_system(options.get<bool>("_nonlinear_system"))
64{
66}
67
68std::vector<Diagnosis>
70{
71 neml_assert(host() == this, "This method should only be called on the host model.");
72
73 std::vector<Diagnosis> errors;
74
75 // Check for statefulness
76 if (input_axis().has_subaxis("old_state"))
77 {
78 if (!output_axis().has_subaxis("state"))
79 errors.push_back(
81 ": input axis has sub-axis 'old_state', but output axis does not "
82 "have sub-axis 'state'."));
83 else
84 {
85 auto s_vars = output_axis().subaxis("state").variable_accessors(/*recursive=*/true);
86 for (auto var : input_axis().subaxis("old_state").variable_accessors(/*recursive=*/true))
87 if (!s_vars.count(var))
88 errors.push_back(make_diagnosis(name(),
89 ": input axis has old state named ",
90 var,
91 ", but it doesn't exist on the output axis."));
92 }
93 }
94
95 return errors;
96}
97
98void
100{
101 // Declare nonlinear parameters as additional input variables
102 for (const auto & [name, param] : nl_params())
103 declare_input_variable(param->base_storage(), VariableName(param->name()));
104
105 // Setup input and output axes
106 setup_layout();
107
108 // Setup functional dependence for each output variable
109 for (auto && [y_name, y_var] : output_views())
110 {
111 y_var.clear_args();
112 for (auto && [x_name, x_var] : input_views())
113 y_var.add_arg(x_var);
114 }
115
116 // Setup variable views
119}
120
121void
123 int deriv_order,
124 const torch::Device & device,
125 const torch::Dtype & dtype)
126{
127 neml_assert(host() == this, "This method should only be called on the host model.");
128
129 // Re-cache batch shape if it has changed
130 const auto batch_shape_promoted = batch_shape.empty() ? TorchShape{1} : batch_shape.vec();
134
135 // Re-cache tensor options if they have changed
136 const bool device_changed = device != options().device();
137 const bool dtype_changed = dtype != options().dtype();
139 if (options_changed)
140 {
141 const auto options = default_tensor_options().device(device).dtype(dtype);
142 cache(options);
145 }
146
147 // Reallocate variable storage when necessary
149}
150
151void
153{
154 reinit(tensor.batch_sizes(), deriv_order, tensor.device(), tensor.scalar_type());
155}
156
157void
159{
160 const auto deriv_order_old = _deriv_order;
161 const auto deriv_order_new = deriv_order + _extra_deriv_order;
162
163 bool in = false;
164 bool out = false;
165 bool dout_din = false;
166 bool d2out_din2 = false;
167
168 if (options_changed)
169 {
170 in = true;
171 out = deriv_order_new >= 0;
174 }
175 else
176 {
177 out = deriv_order_new >= 0 && deriv_order_old < 0;
180 }
181
182 _deriv_order = std::max(deriv_order_new, deriv_order_old);
184
185 for (auto submodel : registered_models())
186 submodel->allocate_variables(_deriv_order, options_changed);
187}
188
189void
195
196void
198{
199 for (auto submodel : registered_models())
200 {
201 for (auto && [name, var] : submodel->input_views())
202 var.setup_views(input_view(var.name()));
203
204 submodel->setup_submodel_input_views();
205 }
206}
207
208void
214
215void
217{
218 for (auto submodel : registered_models())
219 {
220 for (auto && [name, var] : submodel->output_views())
221 var.setup_views(&submodel->output_storage(),
222 &submodel->derivative_storage(),
223 &submodel->second_derivative_storage());
224
225 submodel->setup_submodel_output_views();
226 }
227}
228
229void
240
241void
243{
246
248 {
249 if (out)
250 _residual = output_storage()("residual");
251 if (dout_din && requires_grad())
252 _Jacobian = derivative_storage()("residual", "state");
253 }
254}
255
256void
265
266void
268{
270
271 // Also update the model input variables
272 LabeledVector sol(x, {&output_axis().subaxis("residual")});
273 host<VariableStore>()->input_storage().slice("state").fill(sol);
274}
275
276void
278{
279 _batch_sizes = batch_shape.vec();
281 for (auto submodel : registered_models())
282 submodel->cache(batch_shape);
283}
284
285void
286Model::cache(const torch::TensorOptions & options)
287{
288 _options = options;
289 for (auto submodel : registered_models())
290 submodel->cache(options);
291}
292
293void
295{
297 throw NEMLException("AD derivative is requested, but AD second derivative is not requested.");
298}
299
300void
302{
303 for (auto && [name, var] : input_views())
304 var.requires_grad_(req);
305}
306
307void
314
315void
317{
319 neml_assert_dbg(in.axis(0) == input_axis(),
320 "Incompatible input axis. The model has input axis: \n",
321 input_axis(),
322 "The input vector has axis: \n",
323 in.axis(0));
324
325 input_storage().copy_(in.tensor().batch_expand(batch_sizes()));
326}
327
330{
331 return output_storage().clone();
332}
333
339
345
348{
349 set_input(in);
350 detach_and_zero(true, false, false);
351 value();
352 return get_output();
353}
354
355std::tuple<LabeledVector, LabeledMatrix>
357{
358 set_input(in);
359 detach_and_zero(true, true, false);
361 return {get_output(), get_doutput_dinput()};
362}
363
364std::tuple<LabeledVector, LabeledMatrix, LabeledTensor3D>
372
373void
375{
376 set_value(true, false, false);
377}
378
379void
381{
383 "value_and_dvalue() is called but derivative storage hasn't been allocated.");
384
385 if (!_AD_1st_deriv)
386 set_value(true, true, false);
387 else
388 {
390 set_value(true, false, false);
391 extract_derivatives(/*retain_graph=*/true, /*create_graph=*/false, /*allow_unused=*/true);
392 }
393}
394
395void
397{
399 "value_and_dvalue_and_d2value() is called but second derivative storage hasn't "
400 "been allocated.");
401
402 if (!_AD_2nd_deriv)
403 set_value(true, true, true);
404 else
405 {
407
408 if (!_AD_1st_deriv)
409 set_value(true, true, false);
410 else
411 {
412 set_value(true, false, false);
413 extract_derivatives(/*retain_graph=*/true, /*create_graph=*/true, /*allow_unused=*/true);
414 }
415
416 extract_second_derivatives(
417 /*retain_graph=*/true, /*create_graph=*/false, /*allow_unused=*/true);
418 }
419}
420
421Model *
422Model::registered_model(const std::string & name) const
423{
424 for (auto submodel : _registered_models)
425 if (submodel->name() == name)
426 return submodel;
427
428 throw NEMLException("There is no registered model named '" + name + "' in '" + this->name() +
429 "'");
430}
431
432const std::set<VariableName>
434{
435 return input_axis().variable_accessors(true);
436}
437
438const std::set<VariableName>
440{
441 return output_axis().variable_accessors(true);
442}
443
444void
445Model::assemble(bool residual, bool Jacobian)
446{
447 if (residual && !Jacobian)
448 {
449 detach_and_zero(true, false, false);
450 value();
451 }
452 else if (Jacobian)
453 {
454 detach_and_zero(true, true, false);
456 }
457}
458
459void
460Model::extract_derivatives(bool retain_graph, bool create_graph, bool allow_unused)
461{
462 // Loop over rows to retrieve the derivatives
463 if (output_storage().tensor().requires_grad())
464 for (TorchSize i = 0; i < output_storage().base_sizes()[0]; i++)
465 {
467 grad_outputs.index_put_({torch::indexing::Ellipsis, i}, 1.0);
468 for (auto && [name, var] : input_views())
469 {
470 auto dyi_dvar = torch::autograd::grad({output_storage()},
471 {var.tensor()},
472 {grad_outputs},
473 retain_graph,
474 create_graph,
475 allow_unused)[0];
476
477 if (dyi_dvar.defined())
478 {
480 {i, input_axis().indices(name)},
481 dyi_dvar.reshape(utils::add_shapes(batch_sizes(), var.base_storage())));
482 }
483 }
484 }
485}
486
487void
488Model::extract_second_derivatives(bool retain_graph, bool create_graph, bool allow_unused)
489{
490 // Loop over rows to retrieve the second derivatives
491 if (derivative_storage().tensor().requires_grad())
492 for (TorchSize i = 0; i < derivative_storage().base_sizes()[0]; i++)
493 for (TorchSize j = 0; j < derivative_storage().base_sizes()[1]; j++)
494 {
495 auto grad_outputs = torch::zeros_like(derivative_storage());
496 grad_outputs.index_put_({torch::indexing::Ellipsis, i, j}, 1.0);
497 for (auto && [name, var] : input_views())
498 {
499 auto dydxij_dvar = torch::autograd::grad({derivative_storage()},
500 {var.tensor()},
501 {grad_outputs},
502 retain_graph,
503 create_graph,
504 allow_unused)[0];
505 if (dydxij_dvar.defined())
507 {i, j, input_axis().indices(name)},
508 dydxij_dvar.reshape(utils::add_shapes(batch_sizes(), var.base_storage())));
509 }
510 }
511}
512} // namespace neml2
TorchShapeRef batch_sizes() const
Return the batch size.
Definition BatchTensorBase.cxx:149
static BatchTensor zeros_like(const BatchTensor &other)
Zero tensor like another, i.e. same batch and base shapes, same tensor options, etc.
Definition BatchTensorBase.cxx:59
Definition BatchTensor.h:32
static BatchTensor empty(const TorchShapeRef &base_shape, const torch::TensorOptions &options=default_tensor_options())
Unbatched empty tensor given base shape.
Definition BatchTensor.cxx:30
virtual void send_buffers_to(const torch::TensorOptions &options)
Send all buffers to options.
Definition BufferStore.cxx:44
The wrapper (decorator) for cross-referencing unresolved values at parse time.
Definition CrossRef.h:52
Definition Data.h:36
static OptionSet expected_options()
Definition Data.cxx:30
std::set< LabeledAxisAccessor > variable_accessors(bool recursive=false, const LabeledAxisAccessor &subaxis={}) const
Get the variable accessors.
Definition LabeledAxis.cxx:333
const LabeledAxis & subaxis(const std::string &name) const
Get a sub-axis.
Definition LabeledAxis.cxx:365
TorchIndex indices(const LabeledAxisAccessor &accessor) const
Get the indices of a specific item by a LabeledAxisAccessor
Definition LabeledAxis.cxx:240
TorchSize storage_size() const
Get the (total) storage size of this axis.
Definition LabeledAxis.h:142
A single-batched, logically 2D LabeledTensor.
Definition LabeledMatrix.h:38
A single-batched, logically 3D LabeledTensor.
Definition LabeledTensor3D.h:38
Derived clone(torch::MemoryFormat memory_format=torch::MemoryFormat::Contiguous) const
Clone this LabeledTensor.
Definition LabeledTensor.cxx:131
TorchShapeRef base_sizes() const
Return the base size.
Definition LabeledTensor.cxx:180
void base_index_put(TorchSlice indices, const torch::Tensor &other)
Set a index sliced on the batch dimensions to a value.
Definition LabeledTensor.cxx:228
const BatchTensor & tensor() const
Definition LabeledTensor.h:107
void copy_(const T &other)
Copy the value from another tensor.
Definition LabeledTensor.h:235
A single-batched, logically 1D LabeledTensor.
Definition LabeledVector.h:38
The base class for all constitutive models.
Definition Model.h:53
virtual void detach_and_zero(bool out, bool dout_din=true, bool d2out_din2=true) override
Call VariableStore::detach_and_zero recursively on all submodels.
Definition Model.cxx:257
virtual LabeledMatrix get_doutput_dinput()
Definition Model.cxx:335
virtual void setup_submodel_output_views()
Definition Model.cxx:216
virtual void assemble(bool residual, bool Jacobian) override
Compute the residual and Jacobian.
Definition Model.cxx:445
void input_requires_grad_(bool req=true)
Set requires_grad for the input variables.
Definition Model.cxx:301
void use_AD_derivatives(bool first=true, bool second=true)
Tell this model to use AD to get derivatives.
Definition Model.cxx:308
virtual LabeledVector get_output()
Definition Model.cxx:329
bool requires_grad() const
Whether derivative has been requested for this model.
Definition Model.h:104
virtual LabeledTensor3D get_d2output_dinput2()
Definition Model.cxx:341
TorchShapeRef batch_sizes() const
This model's batch shape.
Definition Model.h:113
virtual void value_and_dvalue_and_d2value()
Definition Model.cxx:396
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 cache(TorchShapeRef batch_shape) override
Cache the variable's batch shape.
Definition Model.cxx:277
virtual void setup_submodel_input_views()
Definition Model.cxx:197
virtual void allocate_variables(int deriv_order, bool options_changed)
Call VariableStore::allocate_variables recursively on all submodels.
Definition Model.cxx:158
bool requires_2nd_grad() const
Whether 2nd derivative has been requested for this model.
Definition Model.h:107
virtual void set_value(bool out, bool dout_din, bool d2out_din2)=0
The map between input -> output, and optionally its derivatives.
virtual void set_solution(const BatchTensor &x) override
Set x as the current solution of the nonlinear system.
Definition Model.cxx:267
virtual void reinit_output_views(bool out, bool dout_din=true, bool d2out_din2=true) override
Call VariableStore::reinit_output_views recursively on all submodels.
Definition Model.cxx:242
const torch::TensorOptions & options() const
This model's tensor options.
Definition Model.h:116
virtual void setup_input_views() override
Call VariableStore::setup_input_views recursively on all submodels.
Definition Model.cxx:190
virtual void reinit(TorchShapeRef batch_shape, int deriv_order=0, const torch::Device &device=torch::kCPU, const torch::Dtype &dtype=NEML2_DTYPE)
Allocate storage and setup views for all the variables of this model and recursively all of the sub-m...
Definition Model.cxx:122
bool _AD_1st_deriv
Whether to use AD to compute 1st derivatives.
Definition Model.h:278
virtual bool is_nonlinear_system() const
Whether this model defines one or more nonlinear equations to be solved.
Definition Model.h:75
virtual void check_AD_limitation() const
Definition Model.cxx:294
virtual void set_input(const LabeledVector &in)
Set in to be the input of this model.
Definition Model.cxx:316
virtual void setup() override
Setup this model.
Definition Model.cxx:99
virtual void value()
Definition Model.cxx:374
virtual void reinit_input_views() override
Call VariableStore::reinit_input_views recursively on all submodels.
Definition Model.cxx:230
Stage
Definition Model.h:188
static OptionSet expected_options()
Definition Model.cxx:32
virtual void value_and_dvalue()
Definition Model.cxx:380
virtual void setup_output_views() override
Call VariableStore::setup_output_views recursively on all submodels.
Definition Model.cxx:209
virtual std::vector< Diagnosis > preflight() const
Check for common problems.
Definition Model.cxx:69
virtual const std::set< VariableName > consumed_items() const override
The variables that this model depends on.
Definition Model.cxx:433
virtual const std::set< VariableName > provided_items() const override
The variables that this model defines as part of its output.
Definition Model.cxx:439
static enum neml2::Model::Stage stage
Definition Model.cxx:29
Model(const OptionSet &options)
Construct a new Model object.
Definition Model.cxx:53
Model * registered_model(const std::string &name) const
Get a registered model by its name.
Definition Model.cxx:422
const std::string & name() const
A readonly reference to the object's name.
Definition NEML2Object.h:66
const T * host() const
Get a readonly pointer to the host.
Definition NEML2Object.h:91
Definition error.h:33
Definition of a nonlinear system of equations.
Definition NonlinearSystem.h:37
static void disable_automatic_scaling(OptionSet &options)
Definition NonlinearSystem.cxx:54
BatchTensor _Jacobian
View for the Jacobian of this nonlinear system.
Definition NonlinearSystem.h:108
virtual void set_solution(const BatchTensor &x)
Set the solution vector.
Definition NonlinearSystem.cxx:161
void residual()
Convenient shortcut to assemble and return the system residual.
Definition NonlinearSystem.cxx:175
BatchTensor _solution
View for the solution of this nonlinear system.
Definition NonlinearSystem.h:102
TorchSize _ndof
Number of degrees of freedom.
Definition NonlinearSystem.h:99
BatchTensor _residual
View for the residual of this nonlinear system.
Definition NonlinearSystem.h:105
void Jacobian()
Convenient shortcut to assemble and return the system Jacobian.
Definition NonlinearSystem.cxx:192
static OptionSet expected_options()
Definition NonlinearSystem.cxx:31
A custom map-like data structure. The keys are strings, and the values can be nonhomogeneously typed.
Definition OptionSet.h:59
Interface for object which can store parameters.
Definition ParameterStore.h:38
virtual void send_parameters_to(const torch::TensorOptions &options)
Send parameters to options.
Definition ParameterStore.cxx:47
const std::map< std::string, const VariableBase * > & nl_params() const
Get all nonlinear parameters.
Definition ParameterStore.h:60
Definition VariableStore.h:37
virtual void setup_input_views()
Tell each input variable view which tensor storage(s) to view into.
Definition VariableStore.cxx:107
LabeledMatrix & derivative_storage()
Definition VariableStore.h:127
Storage< VariableName, VariableBase > & output_views()
Definition VariableStore.h:109
LabeledVector & output_storage()
Definition VariableStore.h:121
const Variable< T > & declare_input_variable(S &&... name)
Declare an input variable.
Definition VariableStore.h:180
virtual void reinit_output_views(bool out, bool dout_din=true, bool d2out_din2=true)
Create the views for output variables, and optionally for the derivative and second derivatives.
Definition VariableStore.cxx:128
LabeledAxis & output_axis()
Definition VariableStore.h:97
VariableBase * input_view(const VariableName &)
Get the view of an input variable.
Definition VariableStore.cxx:57
virtual void setup_output_views()
Tell each output variable view which tensor storage(s) to view into.
Definition VariableStore.cxx:114
virtual void allocate_variables(TorchShapeRef batch_shape, const torch::TensorOptions &options, bool in, bool out, bool dout_din, bool d2out_din2)
Allocate variable storages given the batch shape and tensor options.
Definition VariableStore.cxx:78
virtual void detach_and_zero(bool out, bool dout_din=true, bool d2out_din2=true)
Detach the tensor storages and set each element in the tensor to 0.
Definition VariableStore.cxx:135
Storage< VariableName, VariableBase > & input_views()
Definition VariableStore.h:103
virtual void setup_layout()
Setup the layouts of all the registered axes.
Definition VariableStore.cxx:50
LabeledVector & input_storage()
Definition VariableStore.h:115
virtual void cache(TorchShapeRef batch_shape)
Cache the variable's batch shape.
Definition VariableStore.cxx:69
LabeledTensor3D & second_derivative_storage()
Definition VariableStore.h:133
LabeledAxis & input_axis()
Definition VariableStore.h:91
virtual void reinit_input_views()
Create the views for input variables.
Definition VariableStore.cxx:121
TorchShape add_shapes(S &&... shape)
Definition utils.h:294
Definition CrossRef.cxx:32
const torch::TensorOptions default_tensor_options()
Definition types.cxx:30
void neml_assert_dbg(bool assertion, Args &&... args)
Definition error.h:85
int64_t TorchSize
Definition types.h:33
std::vector< TorchSize > TorchShape
Definition types.h:34
Diagnosis make_diagnosis(Args &&... args)
Definition error.h:94
void neml_assert_batch_broadcastable(T &&...)
A helper function to assert that all tensors are batch-broadcastable.
torch::IntArrayRef TorchShapeRef
Definition types.h:35
LabeledAxisAccessor VariableName
Definition Variable.h:35
void neml_assert(bool assertion, Args &&... args)
Definition error.h:73