https://mooseframework.inl.gov
Loading...
Searching...
No Matches
LibtorchRLTrajectoryBuffer.C
Go to the documentation of this file.
1//* This file is part of the MOOSE framework
2//* https://mooseframework.inl.gov
3//*
4//* All rights reserved, see COPYRIGHT for full restrictions
5//* https://github.com/idaholab/moose/blob/master/COPYRIGHT
6//*
7//* Licensed under LGPL 2.1, please see LICENSE for details
8//* https://www.gnu.org/licenses/lgpl-2.1.html
9
10#ifdef MOOSE_LIBTORCH_ENABLED
11
13
14#include "MooseError.h"
15
16#include "libmesh/utility.h"
17
18#include <cmath>
19#include <functional>
20#include <numeric>
21
22namespace
23{
24
25torch::Tensor
26bufferMatrixToTensor(const std::vector<std::vector<Real>> & rows)
27{
28 if (rows.empty())
29 return {};
30
31 const auto num_columns = rows.front().size();
32 auto tensor = torch::zeros({static_cast<long>(rows.size()), static_cast<long>(num_columns)},
33 torch::TensorOptions().dtype(torch::kDouble));
34 auto accessor = tensor.accessor<Real, 2>();
35
36 for (const auto row_i : make_range(rows.size()))
37 {
38 if (rows[row_i].size() != num_columns)
39 mooseError("All rows must have the same number of entries when flattening an RL batch.");
40
41 for (const auto column_i : make_range(num_columns))
42 accessor[row_i][column_i] = rows[row_i][column_i];
43 }
44
45 return tensor;
46}
47
48torch::Tensor
49bufferVectorToColumnTensor(const std::vector<Real> & values)
50{
51 if (values.empty())
52 return {};
53
54 auto tensor = torch::zeros({static_cast<long>(values.size()), 1},
55 torch::TensorOptions().dtype(torch::kDouble));
56 auto accessor = tensor.accessor<Real, 2>();
57
58 for (const auto value_i : make_range(values.size()))
59 accessor[value_i][0] = values[value_i];
60
61 return tensor;
62}
63
64} // namespace
65
66void
68{
69 validateTrajectory(trajectory);
70 _trajectories.push_back(std::move(trajectory));
71}
72
73void
78
79std::size_t
81{
82 std::size_t transitions = 0;
83 for (const auto & trajectory : _trajectories)
84 transitions += trajectory.rewards.size();
85 return transitions;
86}
87
90{
91 TensorBatch batch;
92
93 if (_trajectories.empty())
94 return batch;
95
96 std::vector<std::vector<Real>> observations;
97 std::vector<std::vector<Real>> next_observations;
98 std::vector<std::vector<Real>> actions;
99 std::vector<std::vector<Real>> log_probabilities;
100 std::vector<Real> rewards;
101 std::vector<Real> value_targets;
102 std::vector<Real> advantages;
103
104 observations.reserve(numTransitions());
105 next_observations.reserve(numTransitions());
106 actions.reserve(numTransitions());
107 log_probabilities.reserve(numTransitions());
108 rewards.reserve(numTransitions());
109 value_targets.reserve(numTransitions());
110 advantages.reserve(numTransitions());
111
112 for (const auto & trajectory : _trajectories)
113 {
114 if (!trajectory.value_targets.empty() &&
115 trajectory.value_targets.size() != trajectory.rewards.size())
116 mooseError("Value targets must match the reward length of the trajectory.");
117 if (!trajectory.advantages.empty() && trajectory.advantages.size() != trajectory.rewards.size())
118 mooseError("Advantages must match the reward length of the trajectory.");
119
120 observations.insert(
121 observations.end(), trajectory.observations.begin(), trajectory.observations.end());
122 next_observations.insert(next_observations.end(),
123 trajectory.next_observations.begin(),
124 trajectory.next_observations.end());
125 actions.insert(actions.end(), trajectory.actions.begin(), trajectory.actions.end());
126 log_probabilities.insert(log_probabilities.end(),
127 trajectory.log_probabilities.begin(),
128 trajectory.log_probabilities.end());
129 rewards.insert(rewards.end(), trajectory.rewards.begin(), trajectory.rewards.end());
130 value_targets.insert(
131 value_targets.end(), trajectory.value_targets.begin(), trajectory.value_targets.end());
132 advantages.insert(advantages.end(), trajectory.advantages.begin(), trajectory.advantages.end());
133 }
134
135 batch.observations = bufferMatrixToTensor(observations);
136 batch.next_observations = bufferMatrixToTensor(next_observations);
137 batch.actions = bufferMatrixToTensor(actions);
138 batch.log_probabilities = bufferMatrixToTensor(log_probabilities);
139 batch.rewards = bufferVectorToColumnTensor(rewards);
140 batch.value_targets = bufferVectorToColumnTensor(value_targets);
141 batch.advantages = bufferVectorToColumnTensor(advantages);
142
143 return batch;
144}
145
148{
149 RewardStatistics statistics;
150
151 const auto num_transitions = numTransitions();
152 if (!num_transitions)
153 return statistics;
154
155 const auto squared_deviation_sum = [](const std::vector<Real> & values, const Real mean)
156 {
157 return std::transform_reduce(values.begin(),
158 values.end(),
159 0.0,
160 std::plus<>(),
161 [mean](const Real value)
162 { return (value - mean) * (value - mean); });
163 };
164
165 Real reward_sum = 0.0;
166 for (const auto & trajectory : _trajectories)
167 {
168 const auto & rewards = trajectory.rewards;
169 if (rewards.empty())
170 continue;
171
172 const Real trajectory_sum = std::accumulate(rewards.begin(), rewards.end(), 0.0);
173 const Real trajectory_mean = trajectory_sum / rewards.size();
174 statistics.trajectory_means.push_back(trajectory_mean);
175 statistics.trajectory_stds.push_back(
176 std::sqrt(squared_deviation_sum(rewards, trajectory_mean) / rewards.size()));
177 reward_sum += trajectory_sum;
178 }
179 statistics.mean = reward_sum / num_transitions;
180
181 // The deviations are taken about the overall mean so the spread between trajectories counts.
182 Real overall_squared_deviation_sum = 0.0;
183 for (const auto & trajectory : _trajectories)
184 overall_squared_deviation_sum += squared_deviation_sum(trajectory.rewards, statistics.mean);
185 statistics.std = std::sqrt(overall_squared_deviation_sum / num_transitions);
186
187 return statistics;
188}
189
190void
192{
193 const auto num_steps = trajectory.rewards.size();
194
195 if (trajectory.observations.size() != num_steps)
196 mooseError("RL trajectory observations must match the reward sequence length.");
197
198 if (trajectory.next_observations.size() != num_steps)
199 mooseError("RL trajectory next observations must match the reward sequence length.");
200
201 if (trajectory.actions.size() != num_steps)
202 mooseError("RL trajectory actions must match the reward sequence length.");
203
204 if (trajectory.log_probabilities.size() != num_steps)
205 mooseError("RL trajectory log probabilities must match the reward sequence length.");
206
207 if (!trajectory.terminals.empty() && trajectory.terminals.size() != num_steps)
208 mooseError("RL trajectory terminal flags must match the reward sequence length.");
209
210 if (!trajectory.value_targets.empty() && trajectory.value_targets.size() != num_steps)
211 mooseError("RL trajectory value targets must match the reward sequence length.");
212
213 if (!trajectory.advantages.empty() && trajectory.advantages.size() != num_steps)
214 mooseError("RL trajectory advantages must match the reward sequence length.");
215}
216
217#endif
void mooseError(Args &&... args)
std::array< Real, 2 > values
void addTrajectory(Trajectory trajectory)
Append one trajectory to the on-policy buffer.
TensorBatch flatten() const
Flatten every stored trajectory into one tensor batch.
RewardStatistics rewardStatistics() const
Compute reward statistics over the stored trajectories.
static void validateTrajectory(const Trajectory &trajectory)
Validate a trajectory before it is stored.
std::vector< Trajectory > _trajectories
Stored on-policy trajectories.
void clear()
Clear every stored trajectory.
std::size_t numTransitions() const
Count the total number of transitions stored across every trajectory.
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
IntRange< T > make_range(T beg, T end)
Reward statistics over the stored trajectories.
std::vector< Real > trajectory_stds
Reward standard deviation of each non-empty trajectory about its own mean.
Real std
Standard deviation of every stored transition reward about the overall mean.
std::vector< Real > trajectory_means
Mean reward of each non-empty trajectory.
Real mean
Mean reward over every stored transition.
Flattened tensor data assembled from the stored trajectories.
torch::Tensor actions
Flattened action matrix.
torch::Tensor value_targets
Flattened critic targets.
torch::Tensor next_observations
Flattened next-observation matrix.
torch::Tensor observations
Flattened observation matrix.
torch::Tensor advantages
Flattened advantages.
torch::Tensor log_probabilities
Flattened action log-probabilities.
Vector data for one on-policy trajectory.
std::vector< Real > advantages
Advantage estimates aligned with each transition.
std::vector< Real > rewards
Scalar rewards for each transition.
std::vector< std::vector< Real > > observations
Observations for each transition.
std::vector< std::vector< Real > > actions
Actions applied at each transition.
std::vector< std::vector< Real > > next_observations
Next observations for each transition.
std::vector< Real > value_targets
Critic targets aligned with each transition.
std::vector< std::vector< Real > > log_probabilities
Action log-probabilities recorded during rollout.
std::vector< bool > terminals
Terminal flags for each transition. A true value cuts value bootstrapping at that step.