vector_vertex.h
Go to the documentation of this file.
1 /*********************************************************************
2  *
3  * Software License Agreement
4  *
5  * Copyright (c) 2020,
6  * TU Dortmund - Institute of Control Theory and Systems Engineering.
7  * All rights reserved.
8  *
9  * This program is free software: you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation, either version 3 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program. If not, see <https://www.gnu.org/licenses/>.
21  *
22  * Authors: Christoph Rösmann
23  *********************************************************************/
24 
25 #ifndef SRC_OPTIMIZATION_INCLUDE_CORBO_OPTIMIZATION_HYPER_GRAPH_VECTOR_VERTEX_H_
26 #define SRC_OPTIMIZATION_INCLUDE_CORBO_OPTIMIZATION_HYPER_GRAPH_VECTOR_VERTEX_H_
27 
29 
30 #include <corbo-core/types.h>
31 
32 #include <memory>
33 #include <vector>
34 
35 namespace corbo {
36 
51 class VectorVertex : public VertexInterface
52 {
53  public:
54  using Ptr = std::shared_ptr<VectorVertex>;
55  using UPtr = std::unique_ptr<VectorVertex>;
56 
58  VectorVertex() = default;
59 
60  explicit VectorVertex(bool fixed) : _fixed(fixed) {}
61 
63  explicit VectorVertex(int dimension, bool fixed = false)
64  : _values(Eigen::VectorXd::Zero(dimension)),
65  _lb(Eigen::VectorXd::Constant(dimension, -CORBO_INF_DBL)),
66  _ub(Eigen::VectorXd::Constant(dimension, CORBO_INF_DBL)),
67  _finite_lb_bounds(false),
68  _finite_ub_bounds(false),
69  _fixed(fixed)
70  {
71  }
72 
74  explicit VectorVertex(const Eigen::Ref<const Eigen::VectorXd>& values, bool fixed = false)
75  : _values(values),
76  _lb(Eigen::VectorXd::Constant(values.size(), -CORBO_INF_DBL)),
77  _ub(Eigen::VectorXd::Constant(values.size(), CORBO_INF_DBL)),
78  _finite_lb_bounds(false),
79  _finite_ub_bounds(false),
80  _fixed(fixed)
81  {
82  }
83 
86  const Eigen::Ref<const Eigen::VectorXd>& ub, bool fixed = false)
87  : _values(values), _fixed(fixed)
88  {
89  assert(values.size() == lb.size());
90  assert(values.size() == ub.size());
91  setLowerBounds(lb);
92  setUpperBounds(ub);
93  }
94 
95  // implements interface method
96  int getDimension() const override { return _values.rows(); }
97  // implements interface method
98  int getDimensionUnfixed() const override { return _fixed ? 0 : _values.rows(); }
99 
101  virtual void setDimension(int dim)
102  {
103  _values = Eigen::VectorXd::Zero(dim);
104  _lb = Eigen::VectorXd::Constant(dim, -CORBO_INF_DBL);
105  _ub = Eigen::VectorXd::Constant(dim, CORBO_INF_DBL);
107  }
108 
109  // implements interface method
110  void plus(int idx, double inc) override { _values[idx] += inc; }
111  // implements interface method
112  void plus(const double* inc) override { _values += Eigen::Map<const Eigen::VectorXd>(inc, getDimension()); }
113  // implements interface method
114  void plusUnfixed(const double* inc) override { plus(inc); }
115 
116  // implements interface method
117  const double* getData() const override { return _values.data(); }
118  // implements interface method
119 
120  double* getDataRaw() override { return _values.data(); }
121 
122  // implements interface method
123  void setData(int idx, double data) override { _values[idx] = data; }
124 
127  const Eigen::Ref<const Eigen::VectorXd>& ub, bool fixed = false)
128  {
129  assert(values.size() == lb.size());
130  assert(values.size() == ub.size());
131  _values = values;
132  setLowerBounds(lb);
133  setUpperBounds(ub);
134 
135  setFixed(false);
136  }
137 
138  // implements interface method
139  bool hasFixedComponents() const override { return _fixed; }
140  // implements interface method
141  bool isFixedComponent(int /*idx*/) const override { return _fixed; }
142 
143  // implements interface method
144  void setLowerBounds(const Eigen::Ref<const Eigen::VectorXd>& lb) override
145  {
146  _lb = lb;
147  _finite_lb_bounds = (_lb.array() > -CORBO_INF_DBL).any();
148  }
149  // implements interface method
150  void setLowerBound(int idx, double lb) override
151  {
152  _lb[idx] = lb;
153  _finite_lb_bounds = (_lb.array() > -CORBO_INF_DBL).any();
154  }
155  // implements interface method
157  {
158  _ub = ub;
159  _finite_ub_bounds = (_ub.array() < CORBO_INF_DBL).any();
160  }
161  // implements interface method
162  void setUpperBound(int idx, double ub) override
163  {
164  _ub[idx] = ub;
165  _finite_ub_bounds = (_ub.array() < CORBO_INF_DBL).any();
166  }
167  // implements interface method
168  bool hasFiniteBounds() const override { return _finite_lb_bounds || _finite_ub_bounds; }
169  // implements interface method
170  bool hasFiniteLowerBounds() const override { return _finite_lb_bounds; }
171  // implements interface method
172  bool hasFiniteUpperBounds() const override { return _finite_ub_bounds; }
173  // implements interface method
174  bool hasFiniteLowerBound(int idx) const override
175  {
176  assert(idx < _lb.size());
177  return _lb[idx] > -CORBO_INF_DBL;
178  }
179  // implements interface method
180  bool hasFiniteUpperBound(int idx) const override
181  {
182  assert(idx < _lb.size());
183  return _ub[idx] < CORBO_INF_DBL;
184  }
185  // implements interface method
186  int getNumberFiniteLowerBounds(bool unfixed_only) const override
187  {
188  if (unfixed_only && _fixed)
189  return 0;
190  else
191  return (_lb.array() > -CORBO_INF_DBL).count();
192  }
193  // implements interface method
194  int getNumberFiniteUpperBounds(bool unfixed_only) const override
195  {
196  if (unfixed_only && _fixed)
197  return 0;
198  else
199  return (_ub.array() < CORBO_INF_DBL).count();
200  }
201  int getNumberFiniteBounds(bool unfixed_only) const override
202  {
203  if (unfixed_only && _fixed)
204  return 0;
205  else
206  return (_ub.array() < CORBO_INF_DBL || _lb.array() > -CORBO_INF_DBL).count();
207  }
208 
210  virtual void setFixed(bool fixed) { _fixed = fixed; }
211 
212  // implements interface method
213  const double* getLowerBounds() const override { return _lb.data(); }
214  // implements interface method
215  const double* getUpperBounds() const override { return _ub.data(); }
216 
217  // Backup values
218  // implements interface method
219  void push() override { _backup.push_back(_values); }
220  // implements interface method
221  void pop() override
222  {
223  top();
224  _backup.pop_back();
225  }
226  // implements interface method
227  void top() override
228  {
229  assert(!_backup.empty());
230  _values = _backup.back();
231  }
232  // implements interface method
233  void discardTop() override { _backup.pop_back(); }
234  // implements interface method
235  void clear() override { _backup.clear(); }
236  // implements interface method
237  int getNumBackups() const override { return (int)_backup.size(); }
238 
240  const Eigen::VectorXd& values() const { return _values; }
242  Eigen::VectorXd& values() { return _values; }
243 
245  const Eigen::VectorXd& lowerBound() const { return _lb; }
247  const Eigen::VectorXd& upperBound() const { return _ub; }
248 
249  protected:
250  Eigen::VectorXd _values;
251  Eigen::VectorXd _lb;
252  Eigen::VectorXd _ub;
253  bool _finite_lb_bounds = false;
254  bool _finite_ub_bounds = false;
255 
256  bool _fixed = false;
257 
258  std::vector<Eigen::VectorXd> _backup;
259 
260  public:
262 };
263 
277 {
278  public:
279  using Ptr = std::shared_ptr<PartiallyFixedVectorVertex>;
280  using UPtr = std::unique_ptr<PartiallyFixedVectorVertex>;
281 
283  PartiallyFixedVectorVertex() = default;
284 
285  explicit PartiallyFixedVectorVertex(int dimension)
286  : VectorVertex(dimension), _fixed(Eigen::Array<bool, -1, 1>::Constant(dimension, false)), _num_unfixed(dimension)
287  {
288  }
289 
291  explicit PartiallyFixedVectorVertex(int dimension, const Eigen::Ref<const Eigen::Array<bool, -1, 1>>& fixed)
292  : VectorVertex(dimension), _fixed(fixed), _num_unfixed(dimension)
293  {
294  }
295 
298  : VectorVertex(values), _fixed(Eigen::Array<bool, -1, 1>::Constant(values.size(), false)), _num_unfixed(values.size())
299  {
300  }
301 
304  : VectorVertex(values), _fixed(fixed), _num_unfixed(fixed.size() - fixed.count())
305  {
306  }
307 
311  : VectorVertex(values, lb, ub), _fixed(Eigen::Array<bool, -1, 1>::Constant(values.size(), false)), _num_unfixed(values.size())
312  {
313  }
314 
315  // implements interface method
316  int getDimensionUnfixed() const override { return _num_unfixed; }
317 
318  // implements parent method
319  void setDimension(int dim) override
320  {
322  _fixed.setConstant(dim, false);
323  _num_unfixed = dim;
324  }
325 
328  const Eigen::Ref<const Eigen::VectorXd>& ub, bool fixed = false) override
329  {
330  assert(values.size() == lb.size());
331  assert(values.size() == ub.size());
332  _values = values;
333  setLowerBounds(lb);
334  setUpperBounds(ub);
335 
336  setFixed(fixed);
337  }
338 
342  {
343  assert(values.size() == lb.size());
344  assert(values.size() == ub.size());
345  _values = values;
346  setLowerBounds(lb);
347  setUpperBounds(ub);
348 
349  setFixed(fixed);
350  }
351 
353  void setFixed(int idx, bool fixed)
354  {
355  _fixed[idx] = fixed;
356  _num_unfixed = getDimension() - _fixed.count();
357  }
358 
360  void setFixed(const Eigen::Ref<const Eigen::Array<bool, -1, 1>>& fixed)
361  {
362  _fixed = fixed;
363  _num_unfixed = getDimension() - _fixed.count();
364  }
365 
366  // implements interface method
367  void setFixed(bool fixed) override
368  {
369  _fixed.setConstant(_values.size(), fixed);
370  _num_unfixed = fixed ? 0 : getDimension();
371  }
372 
373  // implements interface method
374  void plusUnfixed(const double* inc) override
375  {
376  int idx = 0;
377  for (int i = 0; i < getDimension(); ++i)
378  {
379  if (!_fixed(i))
380  {
381  plus(i, inc[idx]);
382  ++idx;
383  }
384  }
385  }
386 
387  // implements interface method
388  bool hasFixedComponents() const override { return _num_unfixed < getDimension(); }
389  // implements interface method
390  bool isFixedComponent(int idx) const override { return _fixed[idx]; }
391 
392  // implements interface method
393  int getNumberFiniteLowerBounds(bool unfixed_only) const override
394  {
395  if (unfixed_only && _num_unfixed > 0)
396  {
397  int num = 0;
398  for (int i = 0; i < getDimension(); ++i)
399  {
400  if (!_fixed[i] && _lb[i] > -CORBO_INF_DBL) num += 1;
401  }
402  return num;
403  }
404  else
405  return (_lb.array() > -CORBO_INF_DBL).count();
406  }
407 
408  // implements interface method
409  int getNumberFiniteUpperBounds(bool unfixed_only) const override
410  {
411  if (unfixed_only && _num_unfixed > 0)
412  {
413  int num = 0;
414  for (int i = 0; i < getDimension(); ++i)
415  {
416  if (!_fixed[i] && _ub[i] < CORBO_INF_DBL) num += 1;
417  }
418  return num;
419  }
420  else
421  return (_ub.array() < CORBO_INF_DBL).count();
422  }
423 
424  // implements interface method
425  int getNumberFiniteBounds(bool unfixed_only) const override
426  {
427  if (unfixed_only && _num_unfixed > 0)
428  {
429  int num = 0;
430  for (int i = 0; i < getDimension(); ++i)
431  {
432  if (!_fixed[i] && (_ub[i] < CORBO_INF_DBL || _lb[i] > -CORBO_INF_DBL)) num += 1;
433  }
434  return num;
435  }
436  else
437  return (_ub.array() < CORBO_INF_DBL || _lb.array() > -CORBO_INF_DBL).count();
438  }
439 
441  const Eigen::Array<bool, -1, 1> fixedArray() const { return _fixed; }
442 
443  protected:
444  Eigen::Array<bool, -1, 1> _fixed;
445  int _num_unfixed;
446 };
447 
448 } // namespace corbo
449 
450 #endif // SRC_OPTIMIZATION_INCLUDE_CORBO_OPTIMIZATION_HYPER_GRAPH_VECTOR_VERTEX_H_
corbo::PartiallyFixedVectorVertex::getNumberFiniteLowerBounds
int getNumberFiniteLowerBounds(bool unfixed_only) const override
Get number of finite lower bounds.
Definition: vector_vertex.h:415
corbo::VectorVertex::setUpperBounds
void setUpperBounds(const Eigen::Ref< const Eigen::VectorXd > &ub) override
Define upper bounds on the vertex values [getDimension() x 1].
Definition: vector_vertex.h:200
corbo::PartiallyFixedVectorVertex::isFixedComponent
bool isFixedComponent(int idx) const override
Check if individual components are fixed or unfixed.
Definition: vector_vertex.h:412
corbo::PartiallyFixedVectorVertex::setFixed
void setFixed(int idx, bool fixed)
Set component with idx (0 <= idx < dimension()) to (un)fixed.
Definition: vector_vertex.h:375
Eigen
Definition: common.h:73
corbo::PartiallyFixedVectorVertex::getNumberFiniteUpperBounds
int getNumberFiniteUpperBounds(bool unfixed_only) const override
Get number of finite upper bounds.
Definition: vector_vertex.h:431
corbo::VectorVertex::plusUnfixed
void plusUnfixed(const double *inc) override
Define the increment for the unfixed components of the vertex: x += inc with dim(inc)=getDimensionUnf...
Definition: vector_vertex.h:158
corbo::VectorVertex::getDimension
int getDimension() const override
Return number of elements/values/components stored in this vertex.
Definition: vector_vertex.h:140
corbo::VectorVertex::_values
Eigen::VectorXd _values
Definition: vector_vertex.h:294
corbo::VectorVertex::hasFiniteBounds
bool hasFiniteBounds() const override
Check if finite bounds (lower or upper) are provided.
Definition: vector_vertex.h:212
corbo
Definition: communication/include/corbo-communication/utilities.h:37
corbo::VectorVertex::upperBound
const Eigen::VectorXd & upperBound() const
Read-access to the underlying upper bound vector.
Definition: vector_vertex.h:291
corbo::VectorVertex::pop
void pop() override
Restore the previously stored values of the backup stack and remove them from the stack.
Definition: vector_vertex.h:265
corbo::VectorVertex::hasFiniteUpperBound
bool hasFiniteUpperBound(int idx) const override
Check if finite upper bound for a single component is provided.
Definition: vector_vertex.h:224
corbo::PartiallyFixedVectorVertex::setDimension
void setDimension(int dim) override
Change the dimension of the vertex (lower and upper bounds needs to be redefined)
Definition: vector_vertex.h:341
Eigen::Array< bool, -1, 1 >
corbo::VectorVertex::hasFiniteLowerBound
bool hasFiniteLowerBound(int idx) const override
Check if finite lower bound for a single component is provided.
Definition: vector_vertex.h:218
corbo::VectorVertex::getDimensionUnfixed
int getDimensionUnfixed() const override
Return number of unfixed elements (unfixed elements are skipped as parameters in the Hessian and Jaco...
Definition: vector_vertex.h:142
corbo::CORBO_INF_DBL
constexpr const double CORBO_INF_DBL
Representation for infinity (double version)
Definition: core/include/corbo-core/types.h:75
corbo::PartiallyFixedVectorVertex::hasFixedComponents
bool hasFixedComponents() const override
Check if the vertex has fixed components.
Definition: vector_vertex.h:410
corbo::VectorVertex::VectorVertex
VectorVertex()=default
Default constructor.
corbo::VectorVertex::_fixed
bool _fixed
Definition: vector_vertex.h:300
corbo::VectorVertex::getNumBackups
int getNumBackups() const override
Return the current size/number of backups of the backup stack.
Definition: vector_vertex.h:281
corbo::VectorVertex::values
const Eigen::VectorXd & values() const
Read-access to the underlying value vector.
Definition: vector_vertex.h:284
Eigen::PlainObjectBase::setConstant
EIGEN_DEVICE_FUNC Derived & setConstant(Index size, const Scalar &val)
Definition: CwiseNullaryOp.h:341
corbo::VectorVertex::setData
void setData(int idx, double data) override
Write data to to a specific component.
Definition: vector_vertex.h:167
corbo::VectorVertex::_ub
Eigen::VectorXd _ub
Definition: vector_vertex.h:296
corbo::VectorVertex::Ptr
std::shared_ptr< VectorVertex > Ptr
Definition: vector_vertex.h:98
corbo::VectorVertex::getNumberFiniteUpperBounds
int getNumberFiniteUpperBounds(bool unfixed_only) const override
Get number of finite upper bounds.
Definition: vector_vertex.h:238
corbo::PartiallyFixedVectorVertex::PartiallyFixedVectorVertex
PartiallyFixedVectorVertex()=default
Default constructor.
corbo::VectorVertex::setLowerBound
void setLowerBound(int idx, double lb) override
Define lower bound on a single component of the vertex (0 <= idx < getDimension())
Definition: vector_vertex.h:194
corbo::VectorVertex::plus
void plus(int idx, double inc) override
Add value to a specific component of the vertex: x[idx] += inc.
Definition: vector_vertex.h:154
corbo::VectorVertex::getData
const double * getData() const override
Get read-only raw access to the values of the vertex.
Definition: vector_vertex.h:161
corbo::VectorVertex::hasFiniteLowerBounds
bool hasFiniteLowerBounds() const override
Check if finite lower bounds are provided.
Definition: vector_vertex.h:214
corbo::VectorVertex::clear
void clear() override
Clear complete backup container.
Definition: vector_vertex.h:279
corbo::PartiallyFixedVectorVertex::getDimensionUnfixed
int getDimensionUnfixed() const override
Return number of unfixed elements (unfixed elements are skipped as parameters in the Hessian and Jaco...
Definition: vector_vertex.h:338
corbo::PartiallyFixedVectorVertex::plusUnfixed
void plusUnfixed(const double *inc) override
Define the increment for the unfixed components of the vertex: x += inc with dim(inc)=getDimensionUnf...
Definition: vector_vertex.h:396
corbo::VectorVertex::getNumberFiniteBounds
int getNumberFiniteBounds(bool unfixed_only) const override
Get number of finite upper bounds (either upper or lower must be finite)
Definition: vector_vertex.h:245
corbo::VectorVertex::setDimension
virtual void setDimension(int dim)
Change the dimension of the vertex (lower and upper bounds needs to be redefined)
Definition: vector_vertex.h:145
EIGEN_MAKE_ALIGNED_OPERATOR_NEW
#define EIGEN_MAKE_ALIGNED_OPERATOR_NEW
Definition: Memory.h:690
corbo::PartiallyFixedVectorVertex::_num_unfixed
int _num_unfixed
Definition: vector_vertex.h:467
corbo::PartiallyFixedVectorVertex::fixedArray
const Eigen::Array< bool, -1, 1 > fixedArray() const
Read-only access to the underlying logical array for fixed components.
Definition: vector_vertex.h:463
corbo::VectorVertex::_lb
Eigen::VectorXd _lb
Definition: vector_vertex.h:295
corbo::VectorVertex::_finite_ub_bounds
bool _finite_ub_bounds
Definition: vector_vertex.h:298
corbo::VectorVertex::getUpperBounds
const double * getUpperBounds() const override
Read-only raw access to upper bounds [getDimension() x 1].
Definition: vector_vertex.h:259
Eigen::Map
A matrix or vector expression mapping an existing array of data.
Definition: Map.h:94
corbo::VectorVertex::getLowerBounds
const double * getLowerBounds() const override
Read-only raw access to lower bounds [getDimension() x 1].
Definition: vector_vertex.h:257
corbo::VectorVertex::isFixedComponent
bool isFixedComponent(int) const override
Check if individual components are fixed or unfixed.
Definition: vector_vertex.h:185
Eigen::Ref
A matrix or vector expression mapping an existing expression.
Definition: Ref.h:192
corbo::VectorVertex::set
virtual void set(const Eigen::Ref< const Eigen::VectorXd > &values, const Eigen::Ref< const Eigen::VectorXd > &lb, const Eigen::Ref< const Eigen::VectorXd > &ub, bool fixed=false)
Set values and bounds at once.
Definition: vector_vertex.h:170
corbo::VectorVertex::setLowerBounds
void setLowerBounds(const Eigen::Ref< const Eigen::VectorXd > &lb) override
Define lower bounds on the vertex values [getDimension() x 1].
Definition: vector_vertex.h:188
corbo::PartiallyFixedVectorVertex::set
void set(const Eigen::Ref< const Eigen::VectorXd > &values, const Eigen::Ref< const Eigen::VectorXd > &lb, const Eigen::Ref< const Eigen::VectorXd > &ub, bool fixed=false) override
Set values and bounds at once.
Definition: vector_vertex.h:349
corbo::VectorVertex::UPtr
std::unique_ptr< VectorVertex > UPtr
Definition: vector_vertex.h:99
corbo::VectorVertex::getDataRaw
double * getDataRaw() override
Get write access to the values of the vertex.
Definition: vector_vertex.h:164
corbo::VectorVertex::push
void push() override
Store all values into a internal backup stack.
Definition: vector_vertex.h:263
corbo::VectorVertex::hasFiniteUpperBounds
bool hasFiniteUpperBounds() const override
Check if finite upper bounds are provided.
Definition: vector_vertex.h:216
corbo::VectorVertex::discardTop
void discardTop() override
Delete the previously made backup from the stack without restoring it.
Definition: vector_vertex.h:277
corbo::VectorVertex::setUpperBound
void setUpperBound(int idx, double ub) override
Define upper bound on a single component of the vertex (0 <= idx < getDimension())
Definition: vector_vertex.h:206
types.h
vertex_interface.h
utility::tuple::size
static constexpr size_t size(Tuple< Args... > &)
Provides access to the number of elements in a tuple as a compile-time constant expression.
Definition: TensorSyclTuple.h:143
corbo::VectorVertex::getNumberFiniteLowerBounds
int getNumberFiniteLowerBounds(bool unfixed_only) const override
Get number of finite lower bounds.
Definition: vector_vertex.h:230
corbo::VectorVertex::_finite_lb_bounds
bool _finite_lb_bounds
Definition: vector_vertex.h:297
corbo::VectorVertex::lowerBound
const Eigen::VectorXd & lowerBound() const
Read-access to the underlying lower bound vector.
Definition: vector_vertex.h:289
corbo::VectorVertex
Vertex implementation that stores an Eigen::VectorXd (dynamic dimension)
Definition: vector_vertex.h:73
corbo::VectorVertex::_backup
std::vector< Eigen::VectorXd > _backup
Definition: vector_vertex.h:302
corbo::VectorVertex::setFixed
virtual void setFixed(bool fixed)
Set complete vertex to fixed (and hence skip during optimization)
Definition: vector_vertex.h:254
corbo::VectorVertex::top
void top() override
Restore the previously stored values of the backup stack WITHOUT removing them from the stack.
Definition: vector_vertex.h:271
corbo::VectorVertex::hasFixedComponents
bool hasFixedComponents() const override
Check if the vertex has fixed components.
Definition: vector_vertex.h:183
corbo::PartiallyFixedVectorVertex::getNumberFiniteBounds
int getNumberFiniteBounds(bool unfixed_only) const override
Get number of finite upper bounds (either upper or lower must be finite)
Definition: vector_vertex.h:447
corbo::PartiallyFixedVectorVertex
Vector based vertex with support for partially fixed components.
Definition: vector_vertex.h:298
corbo::PartiallyFixedVectorVertex::_fixed
Eigen::Array< bool, -1, 1 > _fixed
Definition: vector_vertex.h:466


control_box_rst
Author(s): Christoph Rösmann
autogenerated on Wed Mar 2 2022 00:07:14