ilqgames
A new real-time solver for large-scale differential games.
solver_log.cpp
1 /*
2  * Copyright (c) 2019, The Regents of the University of California (Regents).
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  * notice, this list of conditions and the following disclaimer.
11  *
12  * 2. Redistributions in binary form must reproduce the above
13  * copyright notice, this list of conditions and the following
14  * disclaimer in the documentation and/or other materials provided
15  * with the distribution.
16  *
17  * 3. Neither the name of the copyright holder nor the names of its
18  * contributors may be used to endorse or promote products derived
19  * from this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS
22  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
25  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31  * POSSIBILITY OF SUCH DAMAGE.
32  *
33  * Please contact the author(s) of this library if you have any questions.
34  * Authors: David Fridovich-Keil ( dfk@eecs.berkeley.edu )
35  */
36 
37 ///////////////////////////////////////////////////////////////////////////////
38 //
39 // Container to store solver logs.
40 //
41 ///////////////////////////////////////////////////////////////////////////////
42 
43 #include <ilqgames/utils/make_directory.h>
44 #include <ilqgames/utils/operating_point.h>
45 #include <ilqgames/utils/solver_log.h>
46 #include <ilqgames/utils/strategy.h>
47 #include <ilqgames/utils/types.h>
48 #include <ilqgames/utils/uncopyable.h>
49 
50 #include <glog/logging.h>
51 #include <vector>
52 
53 #include <sys/stat.h>
54 #include <sys/types.h>
55 #include <chrono>
56 #include <ctime>
57 #include <fstream>
58 #include <iostream>
59 #include <regex>
60 
61 namespace ilqgames {
62 
63 VectorXf SolverLog::InterpolateState(size_t iterate, Time t) const {
64  const OperatingPoint& op = operating_points_[iterate];
65 
66  // Low and high indices between which to interpolate.
67  const size_t lo = TimeToIndex(t);
68  const size_t hi = std::min(lo + 1, op.xs.size() - 1);
69 
70  // Fraction of the way between lo and hi.
71  const float frac = (t - IndexToTime(lo)) / time::kTimeStep;
72  return (1.0 - frac) * op.xs[lo] + frac * op.xs[hi];
73 }
74 
75 float SolverLog::InterpolateState(size_t iterate, Time t, Dimension dim) const {
76  const OperatingPoint& op = operating_points_[iterate];
77 
78  // Low and high indices between which to interpolate.
79  const size_t lo = TimeToIndex(t);
80  const size_t hi = std::min(lo + 1, op.xs.size() - 1);
81 
82  // Fraction of the way between lo and hi.
83  const float frac = (t - IndexToTime(lo)) / time::kTimeStep;
84  return (1.0 - frac) * op.xs[lo](dim) + frac * op.xs[hi](dim);
85 }
86 
87 VectorXf SolverLog::InterpolateControl(size_t iterate, Time t,
88  PlayerIndex player) const {
89  const OperatingPoint& op = operating_points_[iterate];
90 
91  // Low and high indices between which to interpolate.
92  const size_t lo = TimeToIndex(t);
93  const size_t hi = std::min(lo + 1, op.xs.size() - 1);
94 
95  // Fraction of the way between lo and hi.
96  const float frac = (t - IndexToTime(lo)) / time::kTimeStep;
97  return (1.0 - frac) * op.us[lo][player] + frac * op.us[hi][player];
98 }
99 
100 float SolverLog::InterpolateControl(size_t iterate, Time t, PlayerIndex player,
101  Dimension dim) const {
102  const OperatingPoint& op = operating_points_[iterate];
103 
104  // Low and high indices between which to interpolate.
105  const size_t lo = TimeToIndex(t);
106  const size_t hi = std::min(lo + 1, op.xs.size() - 1);
107 
108  // Fraction of the way between lo and hi.
109  const float frac = (t - IndexToTime(lo)) / time::kTimeStep;
110  return (1.0 - frac) * op.us[lo][player](dim) + frac * op.us[hi][player](dim);
111 }
112 
113 bool SolverLog::Save(bool only_last_trajectory,
114  const std::string& experiment_name) const {
115  // Making top-level directory
116  const std::string dir_name =
117  std::string(ILQGAMES_LOG_DIR) + "/" + experiment_name;
118  if (!MakeDirectory(dir_name)) return false;
119  LOG(INFO) << "Saving to directory: " << dir_name;
120 
121  size_t start = 0;
122  if (only_last_trajectory) start = operating_points_.size() - 1;
123 
124  for (size_t ii = start; ii < operating_points_.size(); ii++) {
125  const auto& op = operating_points_[ii];
126  const std::string sub_dir_name = dir_name + "/" + std::to_string(ii);
127  if (!MakeDirectory(sub_dir_name)) return false;
128 
129  // Dump initial time.
130  std::ofstream file;
131  file.open(sub_dir_name + "/t0.txt");
132  file << op.t0 << std::endl;
133  file.close();
134 
135  // Dump xs.
136  file.open(sub_dir_name + "/xs.txt");
137  for (const auto& x : op.xs) {
138  file << x.transpose() << std::endl;
139  }
140  file.close();
141 
142  // Dump total costs.
143  file.open(sub_dir_name + "/costs.txt");
144  for (const auto& c : total_player_costs_[ii]) {
145  file << c << std::endl;
146  }
147  file.close();
148 
149  // Dump cumulative runtimes.
150  file.open(sub_dir_name + "/cumulative_runtimes.txt");
151  file << cumulative_runtimes_[ii] << std::endl;
152  file.close();
153 
154  // Dump us.
155  std::vector<std::ofstream> files(NumPlayers());
156  for (size_t jj = 0; jj < files.size(); jj++) {
157  files[jj].open(sub_dir_name + "/u" + std::to_string(jj) + ".txt");
158  }
159  for (size_t kk = 0; kk < op.us.size(); kk++) {
160  CHECK_EQ(files.size(), op.us[kk].size());
161  for (size_t jj = 0; jj < files.size(); jj++) {
162  files[jj] << op.us[kk][jj].transpose() << std::endl;
163  }
164  }
165  for (size_t jj = 0; jj < files.size(); jj++) {
166  files[jj].close();
167  }
168  }
169 
170  return true;
171 }
172 
173 inline std::vector<MatrixXf> SolverLog::Ps(size_t iterate,
174  size_t time_index) const {
175  std::vector<MatrixXf> Ps(strategies_[iterate].size());
176  for (PlayerIndex ii = 0; ii < Ps.size(); ii++)
177  Ps[ii] = P(iterate, time_index, ii);
178  return Ps;
179 }
180 
181 inline std::vector<VectorXf> SolverLog::alphas(size_t iterate,
182  size_t time_index) const {
183  std::vector<VectorXf> alphas(strategies_[iterate].size());
184  for (PlayerIndex ii = 0; ii < alphas.size(); ii++)
185  alphas[ii] = alpha(iterate, time_index, ii);
186  return alphas;
187 }
188 
189 inline MatrixXf SolverLog::P(size_t iterate, size_t time_index,
190  PlayerIndex player) const {
191  return strategies_[iterate][player].Ps[time_index];
192 }
193 
194 inline VectorXf SolverLog::alpha(size_t iterate, size_t time_index,
195  PlayerIndex player) const {
196  return strategies_[iterate][player].alphas[time_index];
197 }
198 
199 std::string DefaultExperimentName() {
200  const auto date =
201  std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
202  std::string name = std::string(std::ctime(&date));
203  std::transform(name.begin(), name.end(), name.begin(),
204  [](char ch) { return (ch == ' ' || ch == ':') ? '_' : ch; });
205  return std::regex_replace(name, std::regex("( |\n)+$"), "");
206 }
207 
208 bool SaveLogs(const std::vector<SolverLog>& logs, bool only_last_trajectory,
209  const std::string& experiment_name) {
210  const std::string dir_name =
211  std::string(ILQGAMES_LOG_DIR) + "/" + experiment_name;
212  if (!MakeDirectory(dir_name)) return false;
213 
214  for (size_t ii = 0; ii < logs.size(); ii++) {
215  const auto& log = logs[ii];
216 
217  if (!log.Save(only_last_trajectory,
218  experiment_name + "/" + std::to_string(ii)))
219  return false;
220  }
221 
222  return true;
223 }
224 
225 bool SaveLogs(const std::vector<std::shared_ptr<const SolverLog>>& logs,
226  bool only_last_trajectory, const std::string& experiment_name) {
227  const std::string dir_name =
228  std::string(ILQGAMES_LOG_DIR) + "/" + experiment_name;
229  if (!MakeDirectory(dir_name)) return false;
230 
231  for (size_t ii = 0; ii < logs.size(); ii++) {
232  const auto& log = logs[ii];
233 
234  if (!log->Save(only_last_trajectory,
235  experiment_name + "/" + std::to_string(ii)))
236  return false;
237  }
238 
239  return true;
240 }
241 
242 } // namespace ilqgames