10#ifdef MOOSE_LIBTORCH_ENABLED
15#include "libmesh/utility.h"
20template <
typename NamedTensorList>
22captureTensorShapes(
const NamedTensorList & tensors)
24 std::vector<std::pair<std::string, std::vector<int64_t>>> shapes;
25 shapes.reserve(tensors.size());
27 for (
const auto & tensor : tensors)
30 std::vector<int64_t>(tensor.
value().sizes().begin(), tensor.
value().sizes().end()));
35template <
typename NamedTensorList>
37verifyTensorShapes(
const NamedTensorList & tensors,
38 const std::vector<std::pair<std::string, std::vector<int64_t>>> & expected,
39 const char * tensor_kind)
41 if (tensors.size() != expected.size())
42 mooseError(
"The loaded DRL actor ", tensor_kind,
" count does not match the generated schema.");
44 for (
const auto tensor_i :
make_range(tensors.size()))
46 const auto actual_shape = std::vector<int64_t>(tensors[tensor_i].
value().sizes().begin(),
47 tensors[tensor_i].
value().sizes().end());
49 if (tensors[tensor_i].key() != expected[tensor_i].first ||
50 actual_shape != expected[tensor_i].second)
54 tensors[tensor_i].key(),
55 "' does not match the generated schema.");
65 const std::string
name,
66 const unsigned int num_inputs,
67 const unsigned int num_outputs,
68 const std::vector<unsigned int> & num_neurons_per_layer,
69 const std::vector<std::string> & activation_function,
70 const std::vector<Real> & minimum_values,
71 const std::vector<Real> & maximum_values,
72 const torch::DeviceType device_type,
73 const torch::ScalarType data_type,
74 const bool build_on_construct,
75 const std::vector<Real> & input_shift_factors,
76 const std::vector<Real> & input_scaling_factors,
77 const std::vector<Real> & output_scaling_factors,
78 const bool state_independent_std)
82 num_neurons_per_layer,
88 input_scaling_factors,
89 output_scaling_factors),
90 _minimum_values(minimum_values),
91 _maximum_values(maximum_values),
92 _state_independent_std(state_independent_std)
96 if (has_minimum_values != has_maximum_values)
97 mooseError(
"Bounded action distributions require both minimum_values and maximum_values.");
99 if (has_minimum_values)
102 mooseError(
"The number of minimum_values and maximum_values entries must match the number "
103 "of action outputs.");
107 mooseError(
"maximum_values entries must be strictly greater than minimum_values entries.");
110 if (build_on_construct)
115 const bool build_on_construct)
117 _minimum_values(nn.minValues()),
118 _maximum_values(nn.maxValues()),
119 _state_independent_std(nn.stateIndependentStd())
122 if (build_on_construct)
126 const auto & from_params = nn.named_parameters();
127 auto to_params = this->named_parameters();
128 for (
unsigned int param_i : make_range(from_params.size()))
129 to_params[param_i].value().data() = from_params[param_i].value().data().clone();
131 const auto & from_buffers = nn.named_buffers();
132 auto to_buffers = this->named_buffers();
133 for (
unsigned int buffer_i : make_range(from_buffers.size()))
134 to_buffers[buffer_i].value().data() = from_buffers[buffer_i].value().data().clone();
143 const auto & activation =
147 torch::nn::init::zeros_(
_weights[i]->bias);
160 std::unordered_map<std::string, unsigned int> parameters = {
162 addLayer(
"hidden_layer_" + std::to_string(i + 1), parameters);
171 std::make_shared<LibtorchGaussianActionDistribution>(
"action_distribution",
181 std::make_shared<LibtorchBetaActionDistribution>(
"action_distribution",
218 mooseError(
"Requested a Gaussian action distribution from a bounded actor.");
219 return *distribution;
227 mooseError(
"Requested a Gaussian action distribution from a bounded actor.");
228 return *distribution;
248 mooseError(
"Requested a Beta action distribution from an unbounded actor.");
249 return *distribution;
257 mooseError(
"Requested a Beta action distribution from an unbounded actor.");
258 return *distribution;
272 for (
unsigned int i = 0; i <
_weights.size(); ++i)
274 std::string activation =
276 if (activation ==
"relu")
278 else if (activation ==
"sigmoid")
280 else if (activation ==
"tanh")
282 else if (activation ==
"elu")
284 else if (activation ==
"gelu")
286 else if (activation ==
"linear")
287 output =
_weights[i]->forward(output);
296 const c10::optional<at::Generator> generator)
325 const auto expected_parameters = captureTensorShapes(nn.named_parameters());
326 const auto expected_buffers = captureTensorShapes(nn.named_buffers());
328 torch::serialize::InputArchive archive;
329 archive.load_from(filename);
332 verifyTensorShapes(nn.named_parameters(), expected_parameters,
"parameter");
333 verifyTensorShapes(nn.named_buffers(), expected_buffers,
"buffer");
338 catch (
const c10::Error & e)
340 mooseError(
"The requested DRL actor checkpoint could not be loaded as a native libtorch "
341 "archive. Make sure the file exists and matches the generated actor schema.\n",
const std::vector< double > x
void mooseError(Args &&... args)
void synchronizeScalingFactorsFromBuffer()
Sync cached scaling metadata from the registered buffers after loading state.
Feed-forward actor network coupled to a Gaussian or Beta action distribution.
virtual torch::Tensor sample(c10::optional< at::Generator > generator=c10::nullopt)
Sample an action from the already-reset distribution.
const std::vector< Real > _maximum_values
Upper action bounds used by bounded actor distributions.
const LibtorchGaussianActionDistribution * gaussianActionDistributionPtr() const
Return the Gaussian action distribution pointer, or nullptr for bounded actors.
virtual void constructNeuralNetwork() override
Construct the actor body and action-distribution modules.
const LibtorchGaussianActionDistribution & gaussianActionDistribution() const
Return the Gaussian action distribution reference.
virtual torch::Tensor forward(const torch::Tensor &x) override
Run a forward pass through the actor body.
virtual void initializeNeuralNetwork(c10::optional< at::Generator > generator=c10::nullopt) override
Initialize the actor body and action-distribution parameters.
const std::vector< Real > _minimum_values
Lower action bounds used by bounded actor distributions.
std::shared_ptr< LibtorchActionDistribution > _action_distribution
Action-distribution module attached to the actor output.
const LibtorchBetaActionDistribution * betaActionDistributionPtr() const
Return the Beta action distribution pointer, or nullptr for Gaussian actors.
torch::Tensor logProbability(const torch::Tensor &other)
Evaluate the log-probability of an action under the current actor state.
const LibtorchBetaActionDistribution & betaActionDistribution() const
Return the Beta action distribution reference.
virtual torch::Tensor evaluate(torch::Tensor &input, bool sampled, c10::optional< at::Generator > generator=c10::nullopt)
Evaluate the actor and either sample from it or use its deterministic action.
const bool _state_independent_std
Whether the Gaussian std ignores the current actor features.
LibtorchActorNeuralNet(const std::string name, const unsigned int num_inputs, const unsigned int num_outputs, const std::vector< unsigned int > &num_neurons_per_layer, const std::vector< std::string > &activation_function={"relu"}, const std::vector< Real > &minimum_values={}, const std::vector< Real > &maximum_values={}, const torch::DeviceType device_type=torch::kCPU, const torch::ScalarType scalar_type=torch::kDouble, const bool build_on_construct=true, const std::vector< Real > &input_shift_factors={}, const std::vector< Real > &input_scaling_factors={}, const std::vector< Real > &output_scaling_factors={}, const bool state_independent_std=true)
Build an actor network with either a Gaussian or Beta action distribution.
torch::Tensor entropy()
Compute the entropy of the current action distribution.
const LibtorchActionDistribution & actionDistribution() const
Return the active action distribution as the common base type.
void resetDistributionParams(torch::Tensor input)
Refresh the cached distribution parameters from a fresh input tensor.
const unsigned int _num_inputs
Real determineGain(const std::string &activation)
const torch::ScalarType _data_type
void synchronizeAffineFactorsFromBuffers()
MultiMooseEnum _activation_function
std::vector< torch::nn::Linear > _weights
virtual void addLayer(const std::string &layer_name, const std::unordered_map< std::string, unsigned int > ¶meters)
const unsigned int _num_outputs
const torch::DeviceType _device_type
virtual torch::Tensor preprocessInput(const torch::Tensor &x) const
unsigned int numHiddenLayers() const
const std::vector< unsigned int > _num_neurons_per_layer
std::vector< Real > _output_scaling_factors
Beta action distribution for bounded action spaces.
Gaussian action distribution for unbounded action spaces.
unsigned int size() const
void orthogonalInitializeTensor(torch::Tensor &tensor, Real gain=1.0, c10::optional< at::Generator > generator=c10::nullopt)
void loadLibtorchActorNeuralNetState(Moose::LibtorchActorNeuralNet &nn, const std::string &filename)
Load an actor checkpoint written as a native libtorch state archive.
Real value(unsigned n, unsigned alpha, unsigned beta, Real x)
IntRange< T > make_range(T beg, T end)