ilqgames
A new real-time solver for large-scale differential games.
concatenated_flat_system.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 // Multi-player dynamical system comprised of several single player subsystems.
40 //
41 ///////////////////////////////////////////////////////////////////////////////
42 
43 #include <ilqgames/dynamics/concatenated_flat_system.h>
44 #include <ilqgames/utils/linear_dynamics_approximation.h>
45 #include <ilqgames/utils/types.h>
46 
47 #include <glog/logging.h>
48 
49 namespace ilqgames {
50 
51 ConcatenatedFlatSystem::ConcatenatedFlatSystem(
52  const FlatSubsystemList& subsystems)
53  : MultiPlayerFlatSystem(std::accumulate(
54  subsystems.begin(), subsystems.end(), 0,
55  [](Dimension total,
56  const std::shared_ptr<SinglePlayerFlatSystem>& subsystem) {
57  CHECK_NOTNULL(subsystem.get());
58  return total + subsystem->XDim();
59  })),
60  subsystems_(subsystems) {
61  // Populate subsystem start dimensions.
62  subsystem_start_dims_.push_back(0);
63  for (const auto& subsystem : subsystems_) {
64  subsystem_start_dims_.push_back(subsystem_start_dims_.back() +
65  subsystem->XDim());
66  }
67 }
68 
69 VectorXf ConcatenatedFlatSystem::Evaluate(
70  const VectorXf& x, const std::vector<VectorXf>& us) const {
71  CHECK_EQ(us.size(), NumPlayers());
72 
73  // Populate 'xdot' one subsystem at a time.
74  VectorXf xdot(xdim_);
75  Dimension dims_so_far = 0;
76  for (size_t ii = 0; ii < NumPlayers(); ii++) {
77  const auto& subsystem = subsystems_[ii];
78  xdot.segment(dims_so_far, subsystem->XDim()) =
79  subsystem->Evaluate(x.segment(dims_so_far, subsystem->XDim()), us[ii]);
80  dims_so_far += subsystem->XDim();
81  }
82 
83  return xdot;
84 }
85 
86 void ConcatenatedFlatSystem::ComputeLinearizedSystem() const {
87  // Populate a block-diagonal A, as well as Bs.
88  LinearDynamicsApproximation linearization(*this);
89 
90  Dimension dims_so_far = 0;
91  for (size_t ii = 0; ii < NumPlayers(); ii++) {
92  const auto& subsystem = subsystems_[ii];
93  const Dimension xdim = subsystem->XDim();
94  const Dimension udim = subsystem->UDim();
95  subsystem->LinearizedSystem(
96  linearization.A.block(dims_so_far, dims_so_far, xdim, xdim),
97  linearization.Bs[ii].block(dims_so_far, 0, xdim, udim));
98 
99  dims_so_far += xdim;
100  }
101 
102  discrete_linear_system_.reset(new LinearDynamicsApproximation(linearization));
103 
104  // Reconstruct the continuous system.
105  linearization.A -= MatrixXf::Identity(xdim_, xdim_);
106  linearization.A /= time::kTimeStep;
107  for (size_t ii = 0; ii < NumPlayers(); ii++)
108  linearization.Bs[ii] /= time::kTimeStep;
109  continuous_linear_system_.reset(
110  new LinearDynamicsApproximation(linearization));
111 }
112 
113 MatrixXf ConcatenatedFlatSystem::InverseDecouplingMatrix(
114  const VectorXf& x) const {
115  // Populate a block-diagonal inverse decoupling matrix M.
116  MatrixXf M = MatrixXf::Zero(TotalUDim(), TotalUDim());
117 
118  Dimension x_dims_so_far = 0;
119  Dimension u_dims_so_far = 0;
120  for (size_t ii = 0; ii < NumPlayers(); ii++) {
121  const auto& subsystem = subsystems_[ii];
122  const Dimension xdim = subsystem->XDim();
123  const Dimension udim = subsystem->UDim();
124  M.block(u_dims_so_far, u_dims_so_far, udim, udim) =
125  subsystem->InverseDecouplingMatrix(x.segment(x_dims_so_far, xdim));
126 
127  x_dims_so_far += xdim;
128  u_dims_so_far += udim;
129  }
130 
131  return M;
132 }
133 
134 VectorXf ConcatenatedFlatSystem::AffineTerm(const VectorXf& x) const {
135  // Populate the affine term m for each subsystem.
136  VectorXf m(TotalUDim());
137 
138  Dimension x_dims_so_far = 0;
139  Dimension u_dims_so_far = 0;
140  for (size_t ii = 0; ii < NumPlayers(); ii++) {
141  const auto& subsystem = subsystems_[ii];
142  const Dimension xdim = subsystem->XDim();
143  const Dimension udim = subsystem->UDim();
144  m.segment(u_dims_so_far, udim) =
145  subsystem->AffineTerm(x.segment(x_dims_so_far, xdim));
146 
147  x_dims_so_far += xdim;
148  u_dims_so_far += udim;
149  }
150 
151  return m;
152 }
153 
154 std::vector<VectorXf> ConcatenatedFlatSystem::LinearizingControls(
155  const VectorXf& x, const std::vector<VectorXf>& vs) const {
156  std::vector<VectorXf> us(NumPlayers());
157 
158  Dimension x_dims_so_far = 0;
159  for (size_t ii = 0; ii < NumPlayers(); ii++) {
160  const auto& subsystem = subsystems_[ii];
161  const Dimension xdim = subsystem->XDim();
162  us[ii] =
163  subsystem->LinearizingControl(x.segment(x_dims_so_far, xdim), vs[ii]);
164 
165  x_dims_so_far += xdim;
166  }
167 
168  return us;
169 }
170 
171 VectorXf ConcatenatedFlatSystem::LinearizingControl(const VectorXf& x,
172  const VectorXf& v,
173  PlayerIndex player) const {
174  return subsystems_[player]->LinearizingControl(
175  x.segment(subsystem_start_dims_[player], SubsystemXDim(player)), v);
176 }
177 
178 VectorXf ConcatenatedFlatSystem::ToLinearSystemState(const VectorXf& x) const {
179  VectorXf xi(xdim_);
180 
181  Dimension x_dims_so_far = 0;
182  for (size_t ii = 0; ii < NumPlayers(); ii++) {
183  const auto& subsystem = subsystems_[ii];
184  const Dimension xdim = subsystem->XDim();
185  xi.segment(x_dims_so_far, xdim) =
186  subsystem->ToLinearSystemState(x.segment(x_dims_so_far, xdim));
187 
188  x_dims_so_far += xdim;
189  }
190 
191  return xi;
192 }
193 
194 VectorXf ConcatenatedFlatSystem::ToLinearSystemState(
195  const VectorXf& x, PlayerIndex subsystem_idx) const {
196  CHECK_LT(subsystem_idx, subsystems_.size());
197  return subsystems_[subsystem_idx]->ToLinearSystemState(x.segment(
198  subsystem_start_dims_[subsystem_idx], SubsystemXDim(subsystem_idx)));
199 }
200 
201 VectorXf ConcatenatedFlatSystem::FromLinearSystemState(
202  const VectorXf& xi) const {
203  VectorXf x(xdim_);
204 
205  Dimension x_dims_so_far = 0;
206  for (size_t ii = 0; ii < NumPlayers(); ii++) {
207  const auto& subsystem = subsystems_[ii];
208  const Dimension xdim = subsystem->XDim();
209  x.segment(x_dims_so_far, xdim) =
210  subsystem->FromLinearSystemState(xi.segment(x_dims_so_far, xdim));
211 
212  x_dims_so_far += xdim;
213  }
214 
215  return x;
216 }
217 
218 VectorXf ConcatenatedFlatSystem::FromLinearSystemState(
219  const VectorXf& xi, PlayerIndex subsystem_idx) const {
220  CHECK_LT(subsystem_idx, subsystems_.size());
221  return subsystems_[subsystem_idx]->FromLinearSystemState(xi.segment(
222  subsystem_start_dims_[subsystem_idx], SubsystemXDim(subsystem_idx)));
223 }
224 
225 VectorXf ConcatenatedFlatSystem::SubsystemStates(
226  const VectorXf& x, PlayerIndex subsystem_idx) const {
227  CHECK_LT(subsystem_idx, subsystems_.size());
228  return x.segment(subsystem_start_dims_[subsystem_idx],
229  SubsystemXDim(subsystem_idx));
230 }
231 
232 bool ConcatenatedFlatSystem::IsLinearSystemStateSingular(
233  const VectorXf& xi) const {
234  Dimension x_dims_so_far = 0;
235  for (size_t ii = 0; ii < NumPlayers(); ii++) {
236  const auto& subsystem = subsystems_[ii];
237  const Dimension xdim = subsystem->XDim();
238  if (subsystem->IsLinearSystemStateSingular(xi.segment(x_dims_so_far, xdim)))
239  return true;
240  x_dims_so_far += xdim;
241  }
242 
243  return false;
244 }
245 
246 void ConcatenatedFlatSystem::ChangeCostCoordinates(
247  const VectorXf& xi, std::vector<QuadraticCostApproximation>* q) const {
248  CHECK_NOTNULL(q);
249  CHECK_EQ(q->size(), NumPlayers());
250 
251  // For each player we record dx_i/dxi and d2x_i/dxi2.
252  std::vector<std::vector<VectorXf>> first_partials(NumPlayers());
253  std::vector<std::vector<MatrixXf>> second_partials(NumPlayers());
254  Dimension xi_dims_so_far = 0;
255  for (size_t ii = 0; ii < NumPlayers(); ii++) {
256  const auto& subsystem_ii = subsystems_[ii];
257  const Dimension xdim = subsystem_ii->XDim();
258 
259  first_partials[ii] = std::vector<VectorXf>(xdim, VectorXf::Zero(xdim));
260  second_partials[ii] =
261  std::vector<MatrixXf>(xdim, MatrixXf::Zero(xdim, xdim));
262 
263  DCHECK_LT(xi_dims_so_far + xdim, xi.size());
264  subsystem_ii->Partial(xi.segment(xi_dims_so_far, xdim), &first_partials[ii],
265  &second_partials[ii]);
266  xi_dims_so_far += xdim;
267  }
268 
269  // For loop for hessian.
270  // Vector that is going to contain all the cost hessians for each player.
271  std::vector<MatrixXf> hess_xs(NumPlayers(), MatrixXf::Zero(xdim_, xdim_));
272  Dimension rows_so_far = 0;
273 
274  // Iterating over primary player indexes.
275  for (PlayerIndex pp = 0; pp < NumPlayers(); pp++) {
276  // Iterating over that player's number of dimensions.
277  for (Dimension ii = 0; ii < SubsystemXDim(pp); ii++) {
278  const Dimension ii_rows = ii + rows_so_far;
279 
280  // Iterating over secondary player indexes.
281  Dimension cols_so_far = rows_so_far;
282  for (PlayerIndex qq = pp; qq < NumPlayers(); qq++) {
283  // Iterating over that player's number of dimensions.
284  for (Dimension jj = 0; jj < SubsystemXDim(qq); jj++) {
285  const Dimension jj_cols = jj + cols_so_far;
286 
287  // Iterating over each player's cost.
288  for (PlayerIndex rr = 0; rr < NumPlayers(); rr++) {
289  const MatrixXf& Q = (*q)[rr].state.hess;
290  const VectorXf& l = (*q)[rr].state.grad;
291  MatrixXf& xhess = hess_xs[rr];
292  for (Dimension kk = 0; kk < SubsystemXDim(pp); kk++) {
293  float total = 0.0;
294 
295  if (pp == qq) {
296  total += l(kk + rows_so_far) * second_partials[pp][kk](ii, jj);
297  }
298 
299  float temp = 0.0;
300  for (Dimension ll = 0; ll < SubsystemXDim(qq); ll++) {
301  temp += Q(kk + rows_so_far, ll + cols_so_far) *
302  first_partials[qq][ll](jj);
303  }
304 
305  total += temp * first_partials[pp][kk](ii);
306 
307  DCHECK_LT(ii_rows, xdim_);
308  DCHECK_LT(jj_cols, xdim_);
309  xhess(ii_rows, jj_cols) += total;
310  xhess(jj_cols, ii_rows) += total;
311  }
312  }
313  }
314 
315  // Increment columns so far to track next subsystem.
316  cols_so_far += SubsystemXDim(qq);
317  }
318  }
319 
320  // Increment rows so far to track next subsystem.
321  rows_so_far += SubsystemXDim(pp);
322  }
323 
324  // Update Qs to match these hessians.
325  for (PlayerIndex pp = 0; pp < NumPlayers(); pp++) {
326  // std::cout << "pp: " << pp << "\n" << hess_xs[pp] << std::endl <<
327  // "-----------------\n";
328  // if (pp == 1) {
329  // std::cout << "pp: " << pp << "\n"
330  // << hess_xs[pp] << std::endl
331  // << "-----------------\n";
332  // }
333  (*q)[pp].state.hess.swap(hess_xs[pp]);
334  }
335 
336  // For loop for gradient.
337  // Vector that is going to contain all the cost gradients for each player.
338  std::vector<VectorXf> grad_xs(NumPlayers(), VectorXf::Zero(xdim_));
339  rows_so_far = 0;
340 
341  // Iterating over primary player indexes.
342  for (PlayerIndex pp = 0; pp < NumPlayers(); pp++) {
343  const VectorXf& l = (*q)[pp].state.grad;
344 
345  // Iterating over that player's number of dimensions.
346  for (Dimension ii = 0; ii < SubsystemXDim(pp); ii++) {
347  // Iterating over each player's cost.
348  for (Dimension jj = 0; jj < SubsystemXDim(pp); jj++) {
349  DCHECK_LT(jj + rows_so_far, l.size());
350  grad_xs[pp](ii + rows_so_far) +=
351  l(jj + rows_so_far) * first_partials[pp][jj](ii);
352  }
353  }
354 
355  // Increment rows so far to track next subsystem.
356  rows_so_far += SubsystemXDim(pp);
357  }
358 
359  // Update ls to match these gradients.
360  for (PlayerIndex pp = 0; pp < NumPlayers(); pp++) {
361  // if (pp == 1) {
362  // std::cout << "pp: " << pp << "computed" << "\n"
363  // << grad_xs[pp] << std::endl
364  // << "-----------------\n";
365  // std::cout << "pp: " << pp << "original \n"
366  // << (*q)[pp].l << std::endl
367  // << "-----------------\n";
368  // }
369  // CHECK_LT(std::abs(grad_xs[pp](0) - (*q)[pp].l(0)), 1e-4);
370  // CHECK_LT(std::abs(grad_xs[pp](1) - (*q)[pp].l(1)), 1e-4);
371 
372  (*q)[pp].state.grad.swap(grad_xs[pp]);
373  }
374 
375  // Now modify the cost hessians, i.e. 'Rs'.
376  // NOTE: this depends only on the decoupling matrix.
377  ChangeControlCostCoordinates(xi, q);
378 }
379 
380 void ConcatenatedFlatSystem::ChangeControlCostCoordinates(
381  const VectorXf& xi, std::vector<QuadraticCostApproximation>* q) const {
382  CHECK_NOTNULL(q);
383 
384  // Get nonlinear system state and all decoupling matrices.
385  const VectorXf x = FromLinearSystemState(xi);
386  std::vector<MatrixXf> M_invs(NumPlayers());
387 
388  Dimension x_dims_so_far = 0;
389  for (size_t ii = 0; ii < NumPlayers(); ii++) {
390  const auto& subsystem_ii = subsystems_[ii];
391  const Dimension xdim = subsystem_ii->XDim();
392  M_invs[ii] =
393  subsystem_ii->InverseDecouplingMatrix(x.segment(x_dims_so_far, xdim));
394  x_dims_so_far += xdim;
395  }
396 
397  // Convert Rs.
398  for (size_t ii = 0; ii < NumPlayers(); ii++) {
399  for (auto& element : (*q)[ii].control) {
400  element.second.hess = M_invs[element.first].transpose() *
401  element.second.hess * M_invs[element.first];
402  }
403  }
404 }
405 
406 float ConcatenatedFlatSystem::DistanceBetween(const VectorXf& x0,
407  const VectorXf& x1) const {
408  Dimension dims_so_far = 0;
409  float total = 0.0;
410 
411  // Accumulate total across all subsystems.
412  for (const auto& subsystem : subsystems_) {
413  const Dimension xdim = subsystem->XDim();
414  total += subsystem->DistanceBetween(x0.segment(dims_so_far, xdim),
415  x1.segment(dims_so_far, xdim));
416 
417  dims_so_far += xdim;
418  }
419 
420  return total;
421 }
422 
423 std::vector<Dimension> ConcatenatedFlatSystem::PositionDimensions() const {
424  std::vector<Dimension> dims;
425 
426  for (const auto& s : subsystems_) {
427  const std::vector<Dimension> sub_dims = s->PositionDimensions();
428  dims.insert(dims.end(), sub_dims.begin(), sub_dims.end());
429  }
430 
431  return dims;
432 }
433 
434 } // namespace ilqgames