ZonoOpt v1.0.0
Loading...
Searching...
No Matches
PolymorphicFunctions.hpp
Go to the documentation of this file.
1#ifndef ZONOOPT_POLYMORPHIC_FUNCTIONS_HPP_
2#define ZONOOPT_POLYMORPHIC_FUNCTIONS_HPP_
3
15#include <stdexcept>
16#include <algorithm>
17#include <utility>
18#include <memory>
19#include <cmath>
20
21#include "Zono.hpp"
22#include "ConZono.hpp"
23#include "HybZono.hpp"
24#include "Point.hpp"
25#include "EmptySet.hpp"
27
28namespace ZonoOpt
29{
30
31using namespace detail;
32
33// type checking
34inline bool HybZono::is_point() const
35{
36 const auto PointCast = dynamic_cast<const Point*>(this);
37 return PointCast != nullptr;
38}
39
40inline bool HybZono::is_zono() const
41{
42 const auto ZonoCast = dynamic_cast<const Zono*>(this);
43 const auto PointCast = dynamic_cast<const Point*>(this);
44 return (ZonoCast != nullptr) && (PointCast == nullptr);
45}
46
47inline bool HybZono::is_conzono() const
48{
49 const auto ConZonoCast = dynamic_cast<const ConZono*>(this);
50 const auto ZonoCast = dynamic_cast<const Zono*>(this);
51 const auto EmptySetCast = dynamic_cast<const EmptySet*>(this);
52
53 return (ConZonoCast != nullptr) && (ZonoCast == nullptr) && (EmptySetCast == nullptr);
54}
55
56inline bool HybZono::is_hybzono() const
57{
58 const auto HybZonoCast = dynamic_cast<const HybZono*>(this);
59 const auto ConZonoCast = dynamic_cast<const ConZono*>(this);
60
61 return (HybZonoCast != nullptr) && (ConZonoCast == nullptr);
62}
63
64inline bool HybZono::is_empty_set() const
65{
66 const auto EmptySetCast = dynamic_cast<const EmptySet*>(this);
67 return EmptySetCast != nullptr;
68}
69
70
71inline std::unique_ptr<HybZono> affine_map(const HybZono& Z,
72 const Eigen::SparseMatrix<zono_float>& R, const Eigen::Vector<zono_float, -1>& s)
73{
74 // check dimensions
75 Eigen::Vector<zono_float, -1> s_def;
76 const Eigen::Vector<zono_float, -1> * s_ptr = nullptr;
77 if (s.size() == 0) // default argument
78 {
79 s_def.resize(R.rows());
80 s_def.setZero();
81 s_ptr = &s_def;
82 }
83 else
84 {
85 s_ptr = &s;
86 }
87
88 if (R.cols() != Z.n || R.rows() != s_ptr->size())
89 {
90 throw std::invalid_argument("Linear_map: invalid input dimensions.");
91 }
92
93 // early exit
94 if (Z.is_empty_set()) {
95 return std::make_unique<EmptySet>(static_cast<int>(R.rows()));
96 }
97
98 // apply affine map
99 Eigen::SparseMatrix<zono_float> Gc = R*Z.Gc;
100 Eigen::SparseMatrix<zono_float> Gb = R*Z.Gb;
101 Eigen::Vector<zono_float, -1> c = R*Z.c + *s_ptr;
102
103 // output correct type
104 if (Z.is_hybzono())
105 return std::make_unique<HybZono>(Gc, Gb, c, Z.Ac, Z.Ab, Z.b, Z.zero_one_form);
106 else if (Z.is_conzono())
107 return std::make_unique<ConZono>(Gc, c, Z.A, Z.b, Z.zero_one_form);
108 else if (Z.is_zono())
109 return std::make_unique<Zono>(Gc, c, Z.zero_one_form);
110 else
111 return std::make_unique<Point>(c);
112}
113
114inline std::unique_ptr<HybZono> project_onto_dims(const HybZono& Z, const std::vector<int>& dims)
115{
116 // make sure all dims are >= 0 and < n
117 for (const int dim : dims)
118 {
119 if (dim < 0 || dim >= Z.n)
120 {
121 throw std::invalid_argument("Project onto dims: invalid dimension.");
122 }
123 }
124
125 // early exit
126 if (Z.is_empty_set()) {
127 return std::make_unique<EmptySet>(static_cast<int>(dims.size()));
128 }
129
130 // build affine map matrix
131 Eigen::SparseMatrix<zono_float> R (static_cast<Eigen::Index>(dims.size()), static_cast<Eigen::Index>(Z.n));
132 std::vector<Eigen::Triplet<zono_float>> tripvec;
133 for (int i=0; i<static_cast<int>(dims.size()); i++)
134 {
135 tripvec.emplace_back(i, dims[i], one);
136 }
137 R.setFromTriplets(tripvec.begin(), tripvec.end());
138
139 // apply affine map
140 return affine_map(Z, R);
141}
142
143inline std::unique_ptr<HybZono> minkowski_sum(const HybZono& Z1, HybZono& Z2)
144{
145 // check dimensions
146 if (Z1.n != Z2.n)
147 {
148 throw std::invalid_argument("Minkowski sum: n dimensions must match.");
149 }
150
151 // early exit
152 if (Z1.is_empty_set() && Z2.is_empty_set()) {
153 return std::make_unique<EmptySet>(Z1.n);
154 }
155 else if (Z1.is_empty_set() ) {
156 return std::unique_ptr<HybZono>(Z2.clone());
157 }
158 else if (Z2.is_empty_set()) {
159 return std::unique_ptr<HybZono>(Z1.clone());
160 }
161
162 // make sure Z1 and Z2 both using same generator range
163 if (Z1.zero_one_form != Z2.zero_one_form)
164 {
165 Z2.convert_form();
166 }
167
168 std::vector<Eigen::Triplet<zono_float>> tripvec;
169
170 Eigen::SparseMatrix<zono_float> Gc = hcat<zono_float>(Z1.Gc, Z2.Gc);
171 Eigen::SparseMatrix<zono_float> Gb = hcat<zono_float>(Z1.Gb, Z2.Gb);
172 Eigen::Vector<zono_float, -1> c = Z1.c + Z2.c;
173
174 Eigen::SparseMatrix<zono_float> Ac (Z1.nC + Z2.nC, Z1.nGc + Z2.nGc);
175 get_triplets_offset<zono_float>(Z1.Ac, tripvec, 0, 0);
176 get_triplets_offset<zono_float>(Z2.Ac, tripvec, Z1.nC, Z1.nGc);
177 Ac.setFromTriplets(tripvec.begin(), tripvec.end());
178
179 tripvec.clear();
180 Eigen::SparseMatrix<zono_float> Ab (Z1.nC + Z2.nC, Z1.nGb + Z2.nGb);
181 get_triplets_offset<zono_float>(Z1.Ab, tripvec, 0, 0);
182 get_triplets_offset<zono_float>(Z2.Ab, tripvec, Z1.nC, Z1.nGb);
183 Ab.setFromTriplets(tripvec.begin(), tripvec.end());
184
185 Eigen::Vector<zono_float, -1> b (Z1.nC + Z2.nC);
186 b.segment(0, Z1.nC) = Z1.b;
187 b.segment(Z1.nC, Z2.nC) = Z2.b;
188
189 // return correct output type
190 if (Z1.is_hybzono() || Z2.is_hybzono())
191 return std::make_unique<HybZono>(Gc, Gb, c, Ac, Ab, b, Z1.zero_one_form);
192 else if (Z1.is_conzono() || Z2.is_conzono())
193 return std::make_unique<ConZono>(Gc, c, Ac, b, Z1.zero_one_form);
194 else
195 return std::make_unique<Zono>(Gc, c, Z1.zero_one_form);
196}
197
198inline std::unique_ptr<HybZono> intersection(const HybZono& Z1, HybZono& Z2, const Eigen::SparseMatrix<zono_float>& R)
199{
200 // handle default arguments
201 const Eigen::SparseMatrix<zono_float> * R_ptr = nullptr;
202 Eigen::SparseMatrix<zono_float> R_def;
203 if (R.rows() == 0 && R.cols() == 0)
204 {
205 R_def.resize(Z1.n, Z1.n);
206 R_def.setIdentity();
207 R_ptr = &R_def;
208 }
209 else
210 {
211 R_ptr = &R;
212 }
213
214 // check dimensions
215 if (R_ptr->rows() != Z2.n || R_ptr->cols() != Z1.n)
216 {
217 throw std::invalid_argument("Intersection: inconsistent input dimensions.");
218 }
219
220 // early exit
221 if (Z1.is_empty_set() || Z2.is_empty_set()) {
222 return std::make_unique<EmptySet>(Z1.n);
223 }
224
225 // make sure Z1 and Z2 both using same generator range
226 if (Z1.zero_one_form != Z2.zero_one_form)
227 {
228 Z2.convert_form();
229 }
230
231 // compute intersection
232 Eigen::SparseMatrix<zono_float> Gc = Z1.Gc;
233 Gc.conservativeResize(Z1.n, Z1.nGc + Z2.nGc);
234
235 Eigen::SparseMatrix<zono_float> Gb = Z1.Gb;
236 Gb.conservativeResize(Z1.n, Z1.nGb + Z2.nGb);
237
238 Eigen::Vector<zono_float, -1> c = Z1.c;
239
240 std::vector<Eigen::Triplet<zono_float>> tripvec;
241 Eigen::SparseMatrix<zono_float> Ac (Z1.nC + Z2.nC + R_ptr->rows(), Z1.nGc + Z2.nGc);
242 get_triplets_offset<zono_float>(Z1.Ac, tripvec, 0, 0);
243 get_triplets_offset<zono_float>(Z2.Ac, tripvec, Z1.nC, Z1.nGc);
244 Eigen::SparseMatrix<zono_float> RZ1Gc = (*R_ptr)*Z1.Gc;
245 get_triplets_offset<zono_float>(RZ1Gc, tripvec, Z1.nC + Z2.nC, 0);
246 Eigen::SparseMatrix<zono_float> mZ2Gc = -Z2.Gc;
247 get_triplets_offset<zono_float>(mZ2Gc, tripvec, Z1.nC + Z2.nC, Z1.nGc);
248 Ac.setFromTriplets(tripvec.begin(), tripvec.end());
249
250 tripvec.clear();
251 Eigen::SparseMatrix<zono_float> Ab (Z1.nC + Z2.nC + R_ptr->rows(), Z1.nGb + Z2.nGb);
252 get_triplets_offset<zono_float>(Z1.Ab, tripvec, 0, 0);
253 get_triplets_offset<zono_float>(Z2.Ab, tripvec, Z1.nC, Z1.nGb);
254 Eigen::SparseMatrix<zono_float> RZ1Gb = (*R_ptr)*Z1.Gb;
255 get_triplets_offset<zono_float>(RZ1Gb, tripvec, Z1.nC + Z2.nC, 0);
256 Eigen::SparseMatrix<zono_float> mZ2Gb = -Z2.Gb;
257 get_triplets_offset<zono_float>(mZ2Gb, tripvec, Z1.nC + Z2.nC, Z1.nGb);
258 Ab.setFromTriplets(tripvec.begin(), tripvec.end());
259
260
261 Eigen::Vector<zono_float, -1> b (Z1.nC + Z2.nC + R_ptr->rows());
262 b.segment(0, Z1.nC) = Z1.b;
263 b.segment(Z1.nC, Z2.nC) = Z2.b;
264 b.segment(Z1.nC + Z2.nC, R_ptr->rows()) = Z2.c - (*R_ptr)*Z1.c;
265
266 // return correct output type
267 if (Z1.is_hybzono() || Z2.is_hybzono())
268 return std::make_unique<HybZono>(Gc, Gb, c, Ac, Ab, b, Z1.zero_one_form);
269 else
270 return std::make_unique<ConZono>(Gc, c, Ac, b, Z1.zero_one_form);
271}
272
273inline std::unique_ptr<HybZono> intersection_over_dims(const HybZono& Z1,
274 HybZono& Z2, const std::vector<int>& dims)
275{
276 // check dimensions
277 if (static_cast<size_t>(Z2.n) != dims.size())
278 {
279 throw std::invalid_argument("Intersection over dims: Z2.n must equal number of dimensions.");
280 }
281
282 // make sure dims are >=0 and <Z1.n
283 for (const int dim : dims)
284 {
285 if (dim < 0 || dim >= Z1.n)
286 {
287 throw std::invalid_argument("Intersection over dims: invalid dimension.");
288 }
289 }
290
291 // build projection matrix
292 Eigen::SparseMatrix<zono_float> R (static_cast<Eigen::Index>(dims.size()), static_cast<Eigen::Index>(Z1.n));
293 std::vector<Eigen::Triplet<zono_float>> tripvec;
294 for (int i=0; i<static_cast<int>(dims.size()); ++i)
295 {
296 tripvec.emplace_back(i, dims[i], one);
297 }
298 R.setFromTriplets(tripvec.begin(), tripvec.end());
299
300 // generalized intersection
301 return intersection(Z1, Z2, R);
302}
303
304inline std::unique_ptr<HybZono> halfspace_intersection(HybZono& Z, const Eigen::SparseMatrix<zono_float>& H,
305 const Eigen::Vector<zono_float, -1>& f, const Eigen::SparseMatrix<zono_float>& R)
306{
307 // use the constrain function
308
309 // convert H to row-major to efficiently convert into Inequality form
310 Eigen::SparseMatrix<zono_float, Eigen::RowMajor> H_rm = H;
311 std::vector<Inequality> ineqs;
312 for (int k=0; k<H_rm.rows(); ++k)
313 {
314 // get row of constraint matrix
315 std::vector<Eigen::Triplet<zono_float>> trips_row = get_triplets_row<zono_float>(H_rm, k);
316
317 // build constraint
318 Inequality ineq(static_cast<int>(H_rm.cols()));
319 for (const auto& trip : trips_row)
320 {
321 ineq.add_term(trip.col(), trip.value());
322 }
323 ineq.set_rhs(f(k));
325
326 ineqs.push_back(ineq);
327 }
328
329 // call constrain
330 return constrain(Z, ineqs, R);
331}
332
333// pontryagin difference
334inline std::unique_ptr<HybZono> pontry_diff(HybZono& Z1, HybZono& Z2, bool exact)
335{
336 // check dimensions
337 if (Z1.n != Z2.n)
338 {
339 throw std::invalid_argument("Pontryagin difference: n dimensions must match.");
340 }
341
342 // check empty set inputs
343 if (Z1.is_empty_set() || Z2.is_empty_set())
344 return std::unique_ptr<HybZono>(Z1.clone());
345
346 // check point inputs
347 if (Z1.nG == 0 && !exact)
348 return std::make_unique<EmptySet>(Z1.n);
349 if (Z2.nG == 0)
350 exact = true; // easy to compute exactly
351
352 // check no exact pontry diff with conzono subtrahend
353 if (exact && (Z2.is_conzono() || Z2.is_hybzono()))
354 throw std::invalid_argument("Pontryagin difference: cannot compute exact set when subtrahend is a ConZono or HybZono");
355
356 // require Z1 and Z2 to be in [-1,1] form
357 if (Z1.zero_one_form) Z1.convert_form();
358 if (Z2.zero_one_form) Z2.convert_form();
359
360 // exact case
361 if (exact)
362 {
363 // init Zout
364 std::unique_ptr<HybZono> Z_out (Z1.clone());
365 Z_out->c -= Z2.c;
366
367 // iteratively compute pontryagin difference from columns of Z2 generator matrix
368 const Eigen::Matrix<zono_float, -1, -1> G2 = Z2.G.toDense();
369
370 for (int i=0; i<Z2.nG; ++i)
371 {
372 std::unique_ptr<HybZono> Z_plus (Z_out->clone()), Z_minus (Z_out->clone());
373 Z_plus->c += G2.col(i);
374 Z_minus->c -= G2.col(i);
375 Z_out = intersection(*Z_plus, *Z_minus);
376 }
377
378 return Z_out;
379 }
380
381 // inexact case
382
383 // logic to handle hybrid zonotope cases. Avoiding recursion to prevent redundant get_leaves calculations.
384 if (Z1.is_hybzono() && Z2.is_hybzono())
385 {
386 // get leaves and convert to zonos
387 auto Z1_leaves = Z1.get_leaves();
388 auto Z2_CZ_leaves = Z2.get_leaves();
389 std::vector<Zono> Z2_leaves;
390 for (auto& CZ : Z2_CZ_leaves)
391 {
392 Z2_leaves.push_back(*CZ.to_zono_approx());
393 }
394
395 // take union of pontry diffs for inner approx
396 std::vector<std::unique_ptr<HybZono>> leaf_diffs; // init
397 for (auto& Z1_leaf : Z1_leaves)
398 {
399 std::unique_ptr<HybZono> Z_out; // declare
400 for (auto& Z2_leaf : Z2_leaves)
401 {
402 if (!Z_out) {
403 Z_out = pontry_diff(Z1_leaf, Z2_leaf, false);
404 }
405 else {
406 Z_out = intersection(*Z_out, *pontry_diff(Z1_leaf, Z2_leaf, false));
407 }
408 }
409 leaf_diffs.push_back(std::move(Z_out));
410 }
411 std::vector<HybZono*> leaf_diff_ptrs;
412 for (auto& leaf_diff : leaf_diffs)
413 {
414 leaf_diff_ptrs.emplace_back(leaf_diff.get());
415 }
416 return union_of_many(leaf_diff_ptrs);
417 }
418 else if (Z1.is_hybzono())
419 {
420 // get leaves and convert to zonos
421 auto Z1_leaves = Z1.get_leaves();
422 auto Z2_CZ = dynamic_cast<const ConZono*>(&Z2);
423 Zono Z2_zono = *Z2_CZ->to_zono_approx();
424
425 // take union of pontry diffs for inner approx
426 std::vector<std::unique_ptr<HybZono>> leaf_diffs; // init
427 for (auto& Z1_leaf : Z1_leaves)
428 {
429 leaf_diffs.emplace_back(pontry_diff(Z1_leaf, Z2_zono, false));
430 }
431 std::vector<HybZono*> leaf_diff_ptrs;
432 for (auto& leaf_diff : leaf_diffs)
433 {
434 leaf_diff_ptrs.emplace_back(leaf_diff.get());
435 }
436 return union_of_many(leaf_diff_ptrs);
437 }
438 else if (Z2.is_hybzono())
439 {
440 // get leaves and convert to zonos
441 auto Z2_CZ_leaves = Z2.get_leaves();
442 std::vector<Zono> Z2_leaves;
443 for (auto& CZ : Z2_CZ_leaves)
444 {
445 Z2_leaves.push_back(*CZ.to_zono_approx());
446 }
447
448 // take intersection of pontry diffs
449 std::unique_ptr<HybZono> Z_out; // declare
450 for (auto& Z2_leaf : Z2_leaves)
451 {
452 if (!Z_out) {
453 Z_out = pontry_diff(Z1, Z2_leaf, false);
454 }
455 else {
456 Z_out = intersection(*Z_out, *pontry_diff(Z1, Z2_leaf, false));
457 }
458 }
459 return Z_out;
460 }
461 else
462 {
463 // convert to zono subtrahend (do nothing if already zono)
464 auto Z2_CZ = dynamic_cast<const ConZono*>(&Z2);
465 Zono Z2_zono = *Z2_CZ->to_zono_approx();
466
467 // get [G; A] matrices
468 const Eigen::SparseMatrix<zono_float> GA1 = vcat<zono_float>(Z1.G, Z1.A);
469 Eigen::SparseMatrix<zono_float> GA2 = Z2_zono.G;
470 GA2.conservativeResize(Z2_zono.n + Z1.nC, Z2_zono.nG);
471
472 Eigen::SparseMatrix<zono_float> M;
473 if (Z1.n + Z1.nC == Z1.nG)
474 {
475 // solve GA1^{-1}*GA2
476 Eigen::SparseLU<Eigen::SparseMatrix<zono_float>> lu(GA1);
477 if (lu.info() != Eigen::Success)
478 {
479 throw std::runtime_error("Pontryagin difference: failed to peform LU decomposition. Most likely [G; A] is not full row rank");
480 }
481 M = lu.solve(GA2);
482 }
483 else
484 {
485 // solve pinv(GA1)*GA2 where pinv is right pseudoinverse
486 Eigen::SimplicialLDLT<Eigen::SparseMatrix<zono_float>> ldlt(GA1*GA1.transpose());
487 if (ldlt.info() != Eigen::Success)
488 {
489 throw std::runtime_error("Pontryagin difference: failed to perform LDLT decomposition. Most likely [G; A] is not full row rank");
490 }
491 const Eigen::SparseMatrix<zono_float> ldlt_sol = ldlt.solve(GA2);
492 M = GA1.transpose()*ldlt_sol;
493 }
494
495 std::vector<Eigen::Triplet<zono_float>> triplets;
496 triplets.reserve(Z1.nG);
497 Eigen::Vector<zono_float, -1> e (Z1.nG);
498 for (int i=0; i < Z1.nG; ++i)
499 {
500 e.setZero();
501 e(i) = 1.0;
502 const zono_float d = 1-(e.transpose()*M).cwiseAbs().sum();
503 if (d < 0)
504 {
505 return std::make_unique<EmptySet>(Z1.n);
506 }
507 triplets.emplace_back(i, i, d);
508 }
509 Eigen::SparseMatrix<zono_float> D (Z1.nG, Z1.nG);
510 D.setFromSortedTriplets(triplets.begin(), triplets.end());
511
512 return std::make_unique<ConZono>(Z1.G*D, Z1.c - Z2_zono.c, Z1.A*D, Z1.b, false);
513 }
514}
515
516inline std::unique_ptr<HybZono> union_of_many(const std::vector<HybZono*>& Zs_in, const bool preserve_sharpness, const bool expose_indicators)
517{
518 // remove empty sets from sets to be unioned
519 std::vector<HybZono*> Zs;
520 for (HybZono* Z : Zs_in) {
521 if (!Z->is_empty_set()) {
522 Zs.push_back(Z);
523 }
524 }
525
526 // check we are taking a union of at least one zonotope
527 if (Zs.empty())
528 {
529 throw std::invalid_argument("Union: empty input vector.");
530 }
531
532 // check dimensions
533 const int n = Zs[0]->n;
534 for (const auto & Z : Zs)
535 {
536 if (Z->n != n)
537 {
538 throw std::invalid_argument("Union: inconsistent dimensions.");
539 }
540 }
541
542 // make sure all Zs are using [0,1] generators
543 for (const auto & Z : Zs)
544 {
545 if (!Z->zero_one_form)
546 {
547 Z->convert_form();
548 }
549 }
550
551 // declare
552 std::vector<Eigen::Triplet<zono_float>> tripvec;
553 int rows = 0, cols = 0;
554 std::vector<int> idx_sum_to_1;
555 Eigen::SparseMatrix<zono_float> Gc, Gb, Ac, Ab;
556 Eigen::Vector<zono_float, -1> c, b;
557
558 if (preserve_sharpness)
559 {
560 // constraints
561
562 // Ac
563 tripvec.clear();
564 rows = 0;
565 cols = 0;
566 for (const auto & Z : Zs)
567 {
568 // equality constraints
569 get_triplets_offset<zono_float>(Z->Ac, tripvec, rows, cols);
570 rows += Z->nC;
571
572 // identity matrices
573 for (int i=0; i<Z->nGc; i++)
574 {
575 tripvec.emplace_back(rows+i, cols+i, one);
576 }
577 for (int i=0; i<Z->nG; i++)
578 {
579 tripvec.emplace_back(rows+i, cols+Z->nGc+i, one);
580 }
581
582 // increment
583 rows += Z->nG;
584 cols += Z->nGc + Z->nG;
585 }
586 Ac.resize(rows+1, cols); // last row all zeroes
587 Ac.setFromTriplets(tripvec.begin(), tripvec.end());
588
589 // Ab
590 tripvec.clear();
591 rows = 0;
592 cols = 0;
593 for (const auto & Z : Zs)
594 {
595 // equality constraints
596 get_triplets_offset<zono_float>(Z->Ab, tripvec, rows, cols);
597
598 // last column
599 for (int i=0; i<Z->nC; i++)
600 {
601 tripvec.emplace_back(rows+i, cols+Z->nGb, -Z->b(i));
602 }
603 rows += Z->nC;
604
605 // identity matrix
606 for (int i=0; i<Z->nGb; i++)
607 {
608 tripvec.emplace_back(rows+Z->nGc+i, cols+i, one);
609 }
610
611 // last column
612 for (int i=0; i<Z->nG; i++)
613 {
614 tripvec.emplace_back(rows+i, cols+Z->nGb, -one);
615 }
616
617 // increment
618 rows += Z->nG;
619 cols += Z->nGb + 1;
620
621 // track sum-to-1 binaries
622 idx_sum_to_1.push_back(cols-1);
623 }
624 // sum to 1 constraint
625 for (int & it : idx_sum_to_1)
626 {
627 tripvec.emplace_back(rows, it, one);
628 }
629 rows++;
630 Ab.resize(rows, cols);
631 Ab.setFromTriplets(tripvec.begin(), tripvec.end());
632
633 // b
634 b.resize(Ab.rows());
635 b.setZero();
636 b(Ab.rows()-1) = 1.0;
637
638 // generators
639 int n_out = n;
640 if (expose_indicators)
641 n_out += static_cast<int>(Zs.size());
642
643 // Gc
644 tripvec.clear();
645 cols = 0;
646 for (const auto & Z : Zs)
647 {
648 get_triplets_offset<zono_float>(Z->Gc, tripvec, 0, cols);
649 cols += 2*Z->nGc + Z->nGb;
650 }
651 Gc.resize(n_out, cols);
652 Gc.setFromTriplets(tripvec.begin(), tripvec.end());
653
654 // Gb
655 tripvec.clear();
656 cols = 0;
657 for (const auto & Z : Zs)
658 {
659 get_triplets_offset<zono_float>(Z->Gb, tripvec, 0, cols);
660 cols += Z->nGb;
661 for (int i=0; i<Z->n; i++)
662 {
663 tripvec.emplace_back(i, cols, Z->c(i));
664 }
665 cols += 1;
666 }
667 for (int i=n; i<n_out; ++i)
668 {
669 tripvec.emplace_back(i, idx_sum_to_1[i-n], one);
670 }
671 Gb.resize(n_out, cols);
672 Gb.setFromTriplets(tripvec.begin(), tripvec.end());
673
674 // c
675 c.resize(n_out);
676 c.setZero();
677 }
678 else
679 {
680 // constraints
681
682 // Ac
683 tripvec.clear();
684 rows = 0;
685 cols = 0;
686 for (const auto & Z : Zs)
687 {
688 // populate first row
689 for (int i=0; i<Z->nGc; i++)
690 {
691 tripvec.emplace_back(rows, cols+i, one);
692 }
693 tripvec.emplace_back(rows, cols+Z->nGc, static_cast<zono_float>(Z->nG));
694
695 // increment
696 rows++;
697
698 // equality constraints
699 get_triplets_offset<zono_float>(Z->Ac, tripvec, rows, cols);
700
701 // increment
702 rows += Z->nC;
703 cols += Z->nGc + 1;
704 }
705 Ac.resize(rows+1, cols); // last row all zeroes
706 Ac.setFromTriplets(tripvec.begin(), tripvec.end());
707
708 // Ab
709 tripvec.clear();
710 rows = 0;
711 cols = 0;
712 for (const auto & Z : Zs)
713 {
714 // populate first row
715 for (int i=0; i<Z->nGb; i++)
716 {
717 tripvec.emplace_back(rows, cols+i, one);
718 }
719 tripvec.emplace_back(rows, cols+Z->nGb, static_cast<zono_float>(-Z->nG));
720
721 // increment
722 rows++;
723
724 // equality constraints
725 get_triplets_offset<zono_float>(Z->Ab, tripvec, rows, cols);
726
727 // last column
728 for (int i=0; i<Z->nC; i++)
729 {
730 tripvec.emplace_back(rows+i, cols+Z->nGb, -Z->b(i));
731 }
732
733 // increment
734 rows += Z->nC;
735 cols += Z->nGb + 1;
736
737 // track sum-to-1 binaries
738 idx_sum_to_1.push_back(cols-1);
739 }
740 // sum to 1 constraint
741 for (int & it : idx_sum_to_1)
742 {
743 tripvec.emplace_back(rows, it, one);
744 }
745 rows++;
746 Ab.resize(rows, cols);
747 Ab.setFromTriplets(tripvec.begin(), tripvec.end());
748
749 // b
750 b.resize(Ab.rows());
751 b.setZero();
752 b(Ab.rows()-1) = 1.0;
753
754 // generators
755 int n_out = n;
756 if (expose_indicators)
757 n_out += static_cast<int>(Zs.size());
758
759 // Gc
760 tripvec.clear();
761 cols = 0;
762 for (const auto & Z : Zs)
763 {
764 get_triplets_offset<zono_float>(Z->Gc, tripvec, 0, cols);
765 cols += Z->nGc + 1;
766 }
767 Gc.resize(n_out, cols);
768 Gc.setFromTriplets(tripvec.begin(), tripvec.end());
769
770 // Gb
771 tripvec.clear();
772 cols = 0;
773 for (const auto & Z : Zs)
774 {
775 get_triplets_offset<zono_float>(Z->Gb, tripvec, 0, cols);
776 cols += Z->nGb;
777 for (int i=0; i<Z->n; i++)
778 {
779 tripvec.emplace_back(i, cols, Z->c(i));
780 }
781 cols += 1;
782 }
783 for (int i=n; i<n_out; ++i)
784 {
785 tripvec.emplace_back(i, idx_sum_to_1[i-n], one);
786 }
787 Gb.resize(n_out, cols);
788 Gb.setFromTriplets(tripvec.begin(), tripvec.end());
789
790 // c
791 c.resize(n_out);
792 c.setZero();
793 }
794
795 // check if known to be sharp
796 bool sharp = preserve_sharpness;
797 size_t i = 0;
798 while (sharp && i < Zs.size())
799 {
800 sharp = Zs[i]->sharp;
801 ++i;
802 }
803
804 // return hybrid zonotope
805 return std::make_unique<HybZono>(Gc, Gb, c, Ac, Ab, b, true, sharp);
806}
807
808inline std::unique_ptr<HybZono> cartesian_product(const HybZono& Z1, HybZono& Z2)
809{
810 // trivial case
811 if (Z1.is_empty_set() || Z2.is_empty_set()) {
812 return std::make_unique<EmptySet>(Z1.n + Z2.n);
813 }
814
815 // make sure Z1 and Z2 both using same generator range
816 if (Z1.zero_one_form != Z2.zero_one_form)
817 {
818 Z2.convert_form();
819 }
820
821 // declare
822 std::vector<Eigen::Triplet<zono_float>> tripvec;
823
824 // take Cartesian product
825 Eigen::SparseMatrix<zono_float> Gc (Z1.n + Z2.n, Z1.nGc + Z2.nGc);
826 get_triplets_offset<zono_float>(Z1.Gc, tripvec, 0, 0);
827 get_triplets_offset<zono_float>(Z2.Gc, tripvec, Z1.n, Z1.nGc);
828 Gc.setFromTriplets(tripvec.begin(), tripvec.end());
829
830 tripvec.clear();
831 Eigen::SparseMatrix<zono_float> Gb (Z1.n + Z2.n, Z1.nGb + Z2.nGb);
832 get_triplets_offset<zono_float>(Z1.Gb, tripvec, 0, 0);
833 get_triplets_offset<zono_float>(Z2.Gb, tripvec, Z1.n, Z1.nGb);
834 Gb.setFromTriplets(tripvec.begin(), tripvec.end());
835
836 Eigen::Vector<zono_float, -1> c (Z1.n + Z2.n);
837 c.segment(0, Z1.n) = Z1.c;
838 c.segment(Z1.n, Z2.n) = Z2.c;
839
840 tripvec.clear();
841 Eigen::SparseMatrix<zono_float> Ac (Z1.nC + Z2.nC, Z1.nGc + Z2.nGc);
842 get_triplets_offset<zono_float>(Z1.Ac, tripvec, 0, 0);
843 get_triplets_offset<zono_float>(Z2.Ac, tripvec, Z1.nC, Z1.nGc);
844 Ac.setFromTriplets(tripvec.begin(), tripvec.end());
845
846 tripvec.clear();
847 Eigen::SparseMatrix<zono_float> Ab (Z1.nC + Z2.nC, Z1.nGb + Z2.nGb);
848 get_triplets_offset<zono_float>(Z1.Ab, tripvec, 0, 0);
849 get_triplets_offset<zono_float>(Z2.Ab, tripvec, Z1.nC, Z1.nGb);
850 Ab.setFromTriplets(tripvec.begin(), tripvec.end());
851
852 Eigen::Vector<zono_float, -1> b (Z1.nC + Z2.nC);
853 b.segment(0, Z1.nC) = Z1.b;
854 b.segment(Z1.nC, Z2.nC) = Z2.b;
855
856 // return correct output type
857 if (Z1.is_hybzono() || Z2.is_hybzono())
858 return std::make_unique<HybZono>(Gc, Gb, c, Ac, Ab, b, Z1.zero_one_form);
859 else if (Z1.is_conzono() || Z2.is_conzono())
860 return std::make_unique<ConZono>(Gc, c, Ac, b, Z1.zero_one_form);
861 else if (Z1.is_zono() || Z2.is_zono())
862 return std::make_unique<Zono>(Gc, c, Z1.zero_one_form);
863 else
864 return std::make_unique<Point>(c);
865}
866
867inline std::unique_ptr<HybZono> constrain(HybZono& Z, const std::vector<Inequality> &ineqs, const Eigen::SparseMatrix<zono_float>& R)
868{
869 // trivial case
870 if (Z.is_empty_set()) {
871 return std::make_unique<EmptySet>(Z.n);
872 }
873
874 // handle default arguments
875 const Eigen::SparseMatrix<zono_float> * R_ptr = nullptr;
876 Eigen::SparseMatrix<zono_float> R_def;
877 if (R.rows() == 0 && R.cols() == 0)
878 {
879 R_def.resize(Z.n, Z.n);
880 R_def.setIdentity();
881 R_ptr = &R_def;
882 }
883 else
884 {
885 R_ptr = &R;
886 }
887
888 // check that dimensions match
889 for (const auto& ineq : ineqs)
890 {
891 if (R_ptr->rows() != ineq.get_n_dims())
892 throw std::invalid_argument("Inequality does not have the same number of dimensions as set");
893 }
894
895 // make sure Z in 0-1 form
896 if (!Z.is_0_1_form())
897 Z.convert_form();
898
899 // build constraints
900 std::vector<Eigen::Triplet<zono_float>> triplets_Ac, triplets_Ab;
901 Eigen::Vector<zono_float, -1> b_new (ineqs.size());
902 Eigen::Index n_cons = 0, n_slack = 0;
903 Eigen::SparseMatrix<zono_float, Eigen::RowMajor> RGc = (*R_ptr)*Z.Gc;
904 Eigen::SparseMatrix<zono_float, Eigen::RowMajor> RGb = (*R_ptr)*Z.Gb;
905 Eigen::Vector<zono_float, -1> Rc = (*R_ptr)*Z.c;
906
907 for (const auto& ineq : ineqs)
908 {
909 zono_float rhs;
910 switch (ineq.get_ineq_type())
911 {
912 case LESS:
913 rhs = ineq.get_rhs() - zono_eps;
914 break;
915 case GREATER:
916 rhs = ineq.get_rhs() + zono_eps;
917 break;
918 default:
919 rhs = ineq.get_rhs();
920 break;
921 }
922 zono_float gamma = rhs; // slack variable scaling
923 zono_float db = 0;
924 const auto ineq_type = ineq.get_ineq_type();
925
926 for (const auto& [idx, coeff] : ineq.get_terms())
927 {
928 const auto trips_Gc = get_triplets_row<zono_float>(RGc, idx);
929 for (const auto& trip : trips_Gc)
930 {
931 const zono_float val = coeff * trip.value();
932 triplets_Ac.emplace_back(static_cast<int>(n_cons), static_cast<int>(trip.col()), val);
933 if ((val < 0 && (ineq_type==LESS_OR_EQUAL || ineq_type==LESS)) || (val > 0 && (ineq_type==GREATER_OR_EQUAL || ineq_type==GREATER)))
934 gamma -= val;
935 }
936
937 const auto trips_Gb = get_triplets_row<zono_float>(RGb, idx);
938 for (const auto& trip : trips_Gb)
939 {
940 const zono_float val = coeff * trip.value();
941 triplets_Ab.emplace_back(static_cast<int>(n_cons), static_cast<int>(trip.col()), val);
942 if ((val < 0 && (ineq_type==LESS_OR_EQUAL || ineq_type==LESS)) || (val > 0 && (ineq_type==GREATER_OR_EQUAL || ineq_type==GREATER)))
943 gamma -= val;
944 }
945
946 const zono_float ddb = coeff * Rc(idx);
947 db -= ddb;
948 gamma -= ddb;
949 }
950
951 // rhs
952 b_new(n_cons) = rhs + db;
953
954 // add slack variable
955 if (ineq_type!=EQUAL)
956 {
957 triplets_Ac.emplace_back(static_cast<int>(n_cons), Z.nGc + static_cast<int>(n_slack), gamma);
958 ++n_slack; // increment slack variable index
959 }
960
961 // increment
962 ++n_cons;
963 }
964
965 Eigen::SparseMatrix<zono_float> Ac_cons (static_cast<Eigen::Index>(ineqs.size()), Z.nGc + n_slack);
966 Eigen::SparseMatrix<zono_float> Ab_cons (static_cast<Eigen::Index>(ineqs.size()), Z.nGb);
967 Ac_cons.setFromTriplets(triplets_Ac.begin(), triplets_Ac.end());
968 Ab_cons.setFromTriplets(triplets_Ab.begin(), triplets_Ab.end());
969
970 // set matrices / vectors
971 Eigen::SparseMatrix<zono_float> Z_Ac = Z.Ac;
972 Z_Ac.conservativeResize(Z.nC, Z.nGc + n_slack);
973 Eigen::SparseMatrix<zono_float> Ac = vcat(Z_Ac, Ac_cons);
974 Eigen::SparseMatrix<zono_float> Ab = vcat(Z.Ab, Ab_cons);
975 Eigen::Vector<zono_float, -1> b (Z.nC + static_cast<Eigen::Index>(ineqs.size()));
976 b.segment(0, Z.nC) = Z.b;
977 b.segment(Z.nC, static_cast<Eigen::Index>(ineqs.size())) = b_new;
978
979 Eigen::SparseMatrix<zono_float> Z_Gc = Z.Gc;
980 Z_Gc.conservativeResize(Z.n, Z.nGc + n_slack);
981
982 // output correct type
983 if (Z.is_hybzono())
984 return std::make_unique<HybZono>(Z_Gc, Z.Gb, Z.c, Ac, Ab, b, true);
985 else
986 return std::make_unique<ConZono>(Z_Gc, Z.c, Ac, b, true);
987}
988
989inline std::unique_ptr<HybZono> HybZono::do_complement(const zono_float delta_m, const bool remove_redundancy, const OptSettings &settings,
990 OptSolution* solution, const int n_leaves, const int contractor_iter)
991{
992 // make sure set in [-1,1] form
993 if (this->is_0_1_form()) this->convert_form();
994
995 // need to get leaves and do complement for each leaf if Z is a hybzono
996 auto leaves = this->get_leaves(remove_redundancy, settings, solution, n_leaves, contractor_iter);
997 if (leaves.empty())
998 {
999 throw std::runtime_error("HybZono complement: set is empty.");
1000 }
1001 std::vector<std::unique_ptr<HybZono>> complements; // init
1002 for (auto& leaf : leaves)
1003 {
1004 complements.emplace_back(leaf.complement(delta_m));
1005 }
1006 std::unique_ptr<HybZono> Z_out;
1007 for (auto& comp : complements)
1008 {
1009 if (!Z_out)
1010 {
1011 Z_out.reset(comp->clone());
1012 }
1013 else
1014 {
1015 Z_out = intersection(*Z_out, *comp);
1016 }
1017 }
1018 return Z_out;
1019}
1020
1021inline std::unique_ptr<HybZono> ConZono::do_complement(const zono_float delta_m, bool, const OptSettings&, OptSolution*, int, int)
1022{
1023 // make sure in [-1,1] form
1024 if (this->is_0_1_form()) this->convert_form();
1025
1026 // get a value lambda_m such that lambda_m >= max{ ||lambda||_infty : |[G^T A^T] lambda| <= 1 }
1027
1028 // construct the matrix [G^T A^T]
1029 const auto GT = this->G.transpose();
1030 const auto AT = this->A.transpose();
1031 const Eigen::SparseMatrix<zono_float, Eigen::RowMajor> GTAT = hcat<zono_float>(GT, AT); // convert to row-major
1032
1033 // get the smallest non-zero element in the matrix, making sure that all rows have at least one non-zero element
1034 zono_float min_non_zero = std::numeric_limits<zono_float>::max();
1035 for (int row=0; row<GTAT.outerSize(); ++row)
1036 {
1037 bool value_exists = false;
1038 for (Eigen::SparseMatrix<zono_float, Eigen::RowMajor>::InnerIterator it(GTAT, row); it; ++it)
1039 {
1040 if (it.value() < min_non_zero && std::abs(it.value()) > zono_eps)
1041 {
1042 min_non_zero = it.value();
1043 }
1044 value_exists = true;
1045 }
1046 if (!value_exists)
1047 {
1048 std::stringstream ss;
1049 ss << "ConZono complement: row " << row << " of [G^T A^T] has no non-zero elements.";
1050 throw std::runtime_error(ss.str());
1051 }
1052 }
1053
1054 // value for lambda_m
1055 const zono_float lambda_m = 1 / min_non_zero;
1056
1057 // m value
1058 const zono_float m = delta_m + 1;
1059
1060 // interval sets
1061 Eigen::Vector<zono_float, -1> l1 (2*this->nG);
1062 l1.setConstant(-(m + delta_m/2));
1063 Eigen::Vector<zono_float, -1> u1 (2*this->nG);
1064 u1.setConstant(1 + delta_m/2);
1065 auto Zf1 = interval_2_zono(Box(l1, u1));
1066 if (Zf1->is_0_1_form()) Zf1->convert_form();
1067
1068 Eigen::Vector<zono_float, -1> l2 (4*this->nG);
1069 l2.segment(0, 2*this->nG).setConstant(-(m + 3*delta_m/2 + 1));
1070 l2.segment(2*this->nG, 2*this->nG).setConstant(-2);
1071 Eigen::Vector<zono_float, -1> u2 (4*this->nG);
1072 u2.segment(0, 2*this->nG).setConstant(delta_m/2);
1073 u2.segment(2*this->nG, 2*this->nG).setZero();
1074 auto Zf2 = interval_2_zono(Box(l2, u2));
1075 if (Zf2->is_0_1_form()) Zf2->convert_form();
1076
1077 // build complement
1078
1079 // Gc
1080 Eigen::SparseMatrix<zono_float> Gc = m*this->G;
1081 Gc.conservativeResize(this->n, 9*this->nG + this->n + this->nC + 1);
1082
1083 // Gb
1084 Eigen::SparseMatrix<zono_float> Gb (this->n, 2*this->nG);
1085
1086 // c
1087 Eigen::Vector<zono_float, -1> c = this->c;
1088
1089 // helper matrices
1090
1091 // declarations
1092 std::vector<Eigen::Triplet<zono_float>> triplets;
1093 int n_offset = 0;
1094
1095 // AcPF = [mI, -delta_m/2, 0, 0, 0;
1096 // -mI, -delta_m/2, 0, 0, 0]
1097 triplets.clear();
1098 for (int i=0; i<this->nG; ++i)
1099 {
1100 triplets.emplace_back(i, i, m);
1101 }
1102 for (int i=0; i<this->nG; ++i)
1103 {
1104 triplets.emplace_back(i, this->nG, -delta_m/two);
1105 }
1106 for (int i=0; i<this->nG; ++i)
1107 {
1108 triplets.emplace_back(this->nG+i, i, -m);
1109 }
1110 for (int i=0; i<this->nG; ++i)
1111 {
1112 triplets.emplace_back(this->nG+i, this->nG, -delta_m/two);
1113 }
1114 Eigen::SparseMatrix<zono_float> AcPF (2*this->nG, 3*this->nG + 1 + this->nC + this->n);
1115 AcPF.setFromTriplets(triplets.begin(), triplets.end());
1116
1117 // AcDF = [0, 0, lambda_m [G^T A^T], 0.5 I, -0.5 I;
1118 // 0, 0, 0, 0.5 1^T, 0.5 1^T]
1119 triplets.clear();
1121 n_offset = this->nG+1+this->n+this->nC;
1122 for (int i=0; i<this->nG; ++i)
1123 {
1124 triplets.emplace_back(i, n_offset+i, p5);
1125 }
1126 for (int i=0; i<this->nG; ++i)
1127 {
1128 triplets.emplace_back(this->nG, n_offset+i, p5);
1129 }
1130 n_offset += this->nG;
1131 for (int i=0; i<this->nG; ++i)
1132 {
1133 triplets.emplace_back(i, n_offset+i, -p5);
1134 }
1135 for (int i=0; i<this->nG; ++i)
1136 {
1137 triplets.emplace_back(this->nG, n_offset+i, p5);
1138 }
1139 Eigen::SparseMatrix<zono_float> AcDF (this->nG+1, 3*this->nG + 1 + this->nC + this->n);
1140 AcDF.setFromTriplets(triplets.begin(), triplets.end());
1141
1142 // AcCS = [-mI, delta_m/2, 0, 0, 0;
1143 // mI, delta_m/2, 0, 0, 0;
1144 // 0, 0, 0, I, 0;
1145 // 0, 0, 0, 0, I]
1146 triplets.clear();
1147 for (int i=0; i<this->nG; ++i)
1148 {
1149 triplets.emplace_back(i, i, -m);
1150 }
1151 for (int i=0; i<this->nG; ++i)
1152 {
1153 triplets.emplace_back(i, this->nG, delta_m/two);
1154 }
1155 for (int i=0; i<this->nG; ++i)
1156 {
1157 triplets.emplace_back(this->nG+i, i, m);
1158 }
1159 for (int i=0; i<this->nG; ++i)
1160 {
1161 triplets.emplace_back(this->nG+i, this->nG, delta_m/two);
1162 }
1163 n_offset = this->nG+1+this->n+this->nC;
1164 for (int i=0; i<this->nG; ++i)
1165 {
1166 triplets.emplace_back(2*this->nG+i, n_offset+i, one);
1167 }
1168 n_offset += this->nG;
1169 for (int i=0; i<this->nG; ++i)
1170 {
1171 triplets.emplace_back(3*this->nG+i, n_offset+i, one);
1172 }
1173 Eigen::SparseMatrix<zono_float> AcCS (4*this->nG, 3*this->nG + 1 + this->nC + this->n);
1174 AcCS.setFromTriplets(triplets.begin(), triplets.end());
1175
1176 // AbCS = [mI, 0;
1177 // 0, mI;
1178 // -I, 0;
1179 // 0, -I]
1180 triplets.clear();
1181 for (int i=0; i<this->nG; ++i)
1182 {
1183 triplets.emplace_back(i, i, m);
1184 }
1185 for (int i=0; i<this->nG; ++i)
1186 {
1187 triplets.emplace_back(this->nG+i, this->nG+i, m);
1188 }
1189 for (int i=0; i<this->nG; ++i)
1190 {
1191 triplets.emplace_back(2*this->nG+i, i, -one);
1192 }
1193 for (int i=0; i<this->nG; ++i)
1194 {
1195 triplets.emplace_back(3*this->nG+i, this->nG+i, -one);
1196 }
1197 Eigen::SparseMatrix<zono_float> AbCS (4*this->nG, 2*this->nG);
1198 AbCS.setFromTriplets(triplets.begin(), triplets.end());
1199
1200 // bDF = [0;
1201 // 1-nG]
1202 Eigen::Vector<zono_float, -1> bDF (this->nG+1);
1203 bDF.setZero();
1204 bDF(this->nG) = static_cast<zono_float>(1 - this->nG);
1205
1206 // Ac = [mA, 0, 0, 0;
1207 // AcPF, Gf1, 0;
1208 // AcDF, 0, 0;
1209 // AcCS, 0, Gf2]
1210 triplets.clear();
1211 int m_offset = 0;
1212 n_offset = this->nG + 1 + this->n + this->nC + 2*this->nG;
1213 get_triplets_offset<zono_float>(m*this->A, triplets, 0, 0);
1214 m_offset += this->nC;
1217 m_offset += 2*this->nG;
1219 m_offset += this->nG + 1;
1222 Eigen::SparseMatrix<zono_float> Ac (7*this->nG + this->nC + 1, 9*this->nG + this->n + this->nC + 1);
1223 Ac.setFromTriplets(triplets.begin(), triplets.end());
1224
1225 // Ab = [0;
1226 // 0;
1227 // 0;
1228 // AbCS];
1229 triplets.clear();
1230 get_triplets_offset<zono_float>(AbCS, triplets, this->nC + 2*this->nG + this->nG+1, 0);
1231 Eigen::SparseMatrix<zono_float> Ab (7*this->nG + this->nC + 1, 2*this->nG);
1232 Ab.setFromTriplets(triplets.begin(), triplets.end());
1233
1234 // b = [b;
1235 // cf1;
1236 // bDF;
1237 // cf2]
1238 Eigen::Vector<zono_float, -1> b (7*this->nG + this->nC + 1);
1239 b.segment(0, this->nC) = this->b;
1240 b.segment(this->nC, 2*this->nG) = Zf1->c;
1241 b.segment(this->nC + 2*this->nG, this->nG + 1) = bDF;
1242 b.segment(this->nC + 2*this->nG + this->nG + 1, 4*this->nG) = Zf2->c;
1243
1244 // return hybrid zonotope
1245 return std::make_unique<HybZono>(Gc, Gb, c, Ac, Ab, b, false, false);
1246}
1247
1248inline std::unique_ptr<HybZono> set_diff(const HybZono& Z1, HybZono& Z2, const zono_float delta_m, const bool remove_redundancy,
1249 const OptSettings &settings, OptSolution* solution, const int n_leaves, const int contractor_iter)
1250{
1251 // trivial case
1252 if (Z2.is_empty_set()) {
1253 return std::unique_ptr<HybZono>(Z1.clone());
1254 }
1255
1256 // get complement of Z2
1257 auto Z2_comp = Z2.complement(delta_m, remove_redundancy, settings, solution, n_leaves, contractor_iter);
1258
1259 // set difference
1260 return intersection(Z1, *Z2_comp);
1261}
1262
1263// setup functions
1264inline std::unique_ptr<HybZono> zono_union_2_hybzono(std::vector<Zono> &Zs, const bool expose_indicators)
1265{
1266 // can't be empty
1267 if (Zs.empty())
1268 {
1269 throw std::invalid_argument("Zono union: empty input vector.");
1270 }
1271
1272 // zonotope dimension
1273 int n_dims = Zs[0].n;
1274 const int n_zonos = static_cast<int>(Zs.size());
1275
1276 // loop through Zs
1277 for (auto & Z : Zs)
1278 {
1279 // make sure dimensions are consistent
1280 if (Z.n != n_dims)
1281 {
1282 throw std::invalid_argument("Zono union: inconsistent dimensions.");
1283 }
1284
1285 // convert to [0,1] form
1286 if (!Z.zero_one_form)
1287 {
1288 Z.convert_form();
1289 }
1290 }
1291
1292 // get unique generators and incidence matrix
1293
1294 // initialize S and M matrices as std::vectors
1295 // each entry is a row
1296 int n_gens;
1297 std::vector<Eigen::Matrix<zono_float, 1, -1>> M_vec;
1298 std::vector<Eigen::Matrix<zono_float, -1, 1>> S_vec;
1299 Eigen::Matrix<zono_float, 1, -1> M_row (n_zonos);
1300 Eigen::Matrix<zono_float, -1, -1> Gd;
1301
1302 // loop through each polytope
1303 for (int i=0; i<n_zonos; i++)
1304 {
1305 n_gens = Zs[i].nG;
1306 Gd = Zs[i].G.toDense();
1307 for (int j=0; j<n_gens; j++)
1308 {
1309 // check if the generator is already in S_vec
1310 auto generator_equal = [&](const Eigen::Matrix<zono_float, -1, 1> &s) -> bool
1311 {
1312 return (s - Gd.col(j)).norm() < zono_eps;
1313 };
1314
1315 if (auto it_S = std::find_if(S_vec.begin(), S_vec.end(), generator_equal); it_S == S_vec.end())
1316 {
1317 S_vec.emplace_back(Gd.col(j));
1318 M_row.setZero();
1319 M_row(i) = 1;
1320 M_vec.push_back(M_row);
1321 }
1322 else
1323 {
1324 const int idx = static_cast<int>(std::distance(S_vec.begin(), it_S));
1325 M_vec[idx](i) = 1;
1326 }
1327 }
1328 }
1329
1330 const int nG = static_cast<int>(S_vec.size()); // number of unique generators
1331
1332 // convert to Eigen matrices
1333 Eigen::Matrix<zono_float, -1, -1> S (n_dims, nG);
1334 Eigen::Matrix<zono_float, -1, -1> M (nG, n_zonos);
1335 for (int i=0; i<nG; i++)
1336 {
1337 S.col(i) = S_vec[i];
1338 M.row(i) = M_vec[i];
1339 }
1340
1341 // directly build hybzono in [0,1] form
1342
1343 // declare
1344 std::vector<Eigen::Triplet<zono_float>> tripvec;
1345
1346 // output dimension
1347 int n_out = n_dims;
1348 if (expose_indicators)
1349 n_out += static_cast<int>(n_zonos);
1350
1351 // Gc = [S, 0]
1352 Eigen::SparseMatrix<zono_float> Gc = S.sparseView();
1353 Gc.conservativeResize(n_out, 2*nG);
1354
1355 // Gb = [c0, c1, ...]
1356 tripvec.clear();
1357 for (int i=0; i<n_zonos; ++i)
1358 {
1359 for (int j=0; j<n_dims; ++j)
1360 {
1361 tripvec.emplace_back(j, i, Zs[i].c(j));
1362 }
1363 }
1364 for (int i=n_dims; i<n_out; ++i)
1365 {
1366 tripvec.emplace_back(i, i-n_dims, one);
1367 }
1368 Eigen::SparseMatrix<zono_float> Gb (n_out, n_zonos);
1369 Gb.setFromTriplets(tripvec.begin(), tripvec.end());
1370
1371 // c = 0
1372 Eigen::Vector<zono_float, -1> c (n_out);
1373 c.setZero();
1374
1375 // Ac = [0^T, 0^T;
1376 // I, diag[sum(M, 2)]]
1377 tripvec.clear();
1378 Eigen::SparseMatrix<zono_float> Ac (1+nG, 2*nG);
1379 Eigen::SparseMatrix<zono_float> I_ng (nG, nG);
1380 I_ng.setIdentity();
1381 get_triplets_offset<zono_float>(I_ng, tripvec, 1, 0);
1382 Eigen::Vector<zono_float, -1> sum_M = M.rowwise().sum();
1383 for (int i=0; i<nG; i++)
1384 {
1385 tripvec.emplace_back(1+i, nG+i, sum_M(i));
1386 }
1387 Ac.setFromTriplets(tripvec.begin(), tripvec.end());
1388
1389 // Ab = [1^T;
1390 // -M]
1391 Eigen::SparseMatrix<zono_float> Ab (1+nG, n_zonos);
1392 tripvec.clear();
1393 for (int i=0; i<n_zonos; i++)
1394 {
1395 tripvec.emplace_back(0, i, one);
1396 }
1397 Eigen::SparseMatrix<zono_float> mM_sp = -M.sparseView();
1398 get_triplets_offset<zono_float>(mM_sp, tripvec, 1, 0);
1399 Ab.setFromTriplets(tripvec.begin(), tripvec.end());
1400
1401 // b = [1;
1402 // 0]
1403 Eigen::Vector<zono_float, -1> b (1+nG);
1404 b.setZero();
1405 b(0) = 1;
1406
1407 // return hybrid zonotope
1408 return std::make_unique<HybZono>(Gc, Gb, c, Ac, Ab, b, true, true);
1409}
1410
1411inline std::unique_ptr<HybZono> vrep_2_hybzono(const std::vector<Eigen::Matrix<zono_float, -1, -1>> &Vpolys, const bool expose_indicators)
1412{
1413 // error handling
1414 if (Vpolys.empty())
1415 {
1416 throw std::invalid_argument("set_from_vrep: Vpolys must have at least one polytope.");
1417 }
1418
1419 // dimensions
1420 const int n_polys = static_cast<int>(Vpolys.size());
1421 const int n_dims = static_cast<int>(Vpolys[0].cols());
1422 int n_verts; // declare
1423
1424 // check if all polytopes have the same number of dimensions
1425 for (const auto & Vpoly : Vpolys)
1426 {
1427 if (Vpoly.cols() != n_dims)
1428 {
1429 throw std::invalid_argument("set_from_vrep: all polytopes must have the same number of dimensions.");
1430 }
1431 }
1432
1433 // initialize V and M matrices as std::vectors
1434 // each entry is a row
1435 std::vector<Eigen::Matrix<zono_float, 1, -1>> V_vec, M_vec;
1436 Eigen::Matrix<zono_float, 1, -1> M_row (n_polys);
1437
1438 // loop through each polytope
1439 for (int i=0; i<n_polys; i++)
1440 {
1441 n_verts = static_cast<int>(Vpolys[i].rows());
1442 for (int j=0; j<n_verts; j++)
1443 {
1444 // check if the vertex is already in V_vec
1445 auto vertex_equal = [&](const Eigen::Matrix<zono_float, 1, -1> &v) -> bool
1446 {
1447 return (v - Vpolys[i].row(j)).norm() < zono_eps;
1448 };
1449
1450 if (auto it_V = std::find_if(V_vec.begin(), V_vec.end(), vertex_equal); it_V == V_vec.end())
1451 {
1452 V_vec.emplace_back(Vpolys[i].row(j));
1453 M_row.setZero();
1454 M_row(i) = 1;
1455 M_vec.push_back(M_row);
1456 }
1457 else
1458 {
1459 const int idx = static_cast<int>(std::distance(V_vec.begin(), it_V));
1460 M_vec[idx](i) = 1;
1461 }
1462 }
1463 }
1464
1465 const int nV = static_cast<int>(V_vec.size()); // number of unique vertices
1466
1467 // convert to Eigen matrices
1468 Eigen::Matrix<zono_float, -1, -1> V (n_dims, nV);
1469 Eigen::Matrix<zono_float, -1, -1> M (nV, n_polys);
1470 for (int i=0; i<nV; i++)
1471 {
1472 V.col(i) = V_vec[i];
1473 M.row(i) = M_vec[i];
1474 }
1475
1476 // directly build hybzono in [0,1] form
1477
1478 // declare
1479 std::vector<Eigen::Triplet<zono_float>> tripvec;
1480
1481 // output dimension
1482 int n_out = n_dims;
1483 if (expose_indicators)
1484 n_out += static_cast<int>(n_polys);
1485
1486 // Gc = [V, 0]
1487 Eigen::SparseMatrix<zono_float> Gc = V.sparseView();
1488 Gc.conservativeResize(n_out, 2*nV);
1489
1490 // Gb = [0]
1491 tripvec.clear();
1492 for (int i=n_dims; i<n_out; ++i)
1493 {
1494 tripvec.emplace_back(i, i-n_dims, one);
1495 }
1496 Eigen::SparseMatrix<zono_float> Gb (n_out, n_polys);
1497 Gb.setFromSortedTriplets(tripvec.begin(), tripvec.end());
1498
1499 // c = 0
1500 Eigen::Vector<zono_float, -1> c (n_out);
1501 c.setZero();
1502
1503 // Ac = [1^T, 0^T;
1504 // 0^T, 0^T;
1505 // I, diag[sum(M, 2)]]
1506 tripvec.clear();
1507 Eigen::SparseMatrix<zono_float> Ac (2+nV, 2*nV);
1508 Eigen::SparseMatrix<zono_float> I_nv (nV, nV);
1509 I_nv.setIdentity();
1510 for (int i=0; i<nV; i++)
1511 {
1512 tripvec.emplace_back(0, i, one);
1513 }
1514 get_triplets_offset<zono_float>(I_nv, tripvec, 2, 0);
1515 Eigen::Vector<zono_float, -1> sum_M = M.rowwise().sum();
1516 for (int i=0; i<nV; i++)
1517 {
1518 tripvec.emplace_back(2+i, nV+i, sum_M(i));
1519 }
1520 Ac.setFromTriplets(tripvec.begin(), tripvec.end());
1521
1522 // Ab = [0^T;
1523 // 1^T;
1524 // -M]
1525 Eigen::SparseMatrix<zono_float> Ab (2+nV, n_polys);
1526 tripvec.clear();
1527 for (int i=0; i<n_polys; i++)
1528 {
1529 tripvec.emplace_back(1, i, one);
1530 }
1531 Eigen::SparseMatrix<zono_float> mM_sp = -M.sparseView();
1532 get_triplets_offset<zono_float>(mM_sp, tripvec, 2, 0);
1533 Ab.setFromTriplets(tripvec.begin(), tripvec.end());
1534
1535 // b = [1;
1536 // 1;
1537 // 0]
1538 Eigen::Vector<zono_float, -1> b (2+nV);
1539 b.setZero();
1540 b(0) = 1;
1541 b(1) = 1;
1542
1543 // return hybrid zonotope
1544 return std::make_unique<HybZono>(Gc, Gb, c, Ac, Ab, b, true, true);
1545}
1546
1547inline std::unique_ptr<ConZono> vrep_2_conzono(const Eigen::Matrix<zono_float, -1, -1> &Vpoly)
1548{
1549 // dimensions
1550 const int n_dims = static_cast<int>(Vpoly.cols());
1551 const int n_verts = static_cast<int>(Vpoly.rows());
1552
1553 // make generators
1554 const Eigen::SparseMatrix<zono_float> G = Vpoly.transpose().sparseView();
1555 Eigen::Vector<zono_float, -1> c (n_dims);
1556 c.setZero();
1557
1558 // make constraints
1559 std::vector<Eigen::Triplet<zono_float>> tripvec;
1560 Eigen::SparseMatrix<zono_float> A (1, n_verts);
1561 for (int i=0; i<n_verts; i++)
1562 {
1563 tripvec.emplace_back(0, i, one);
1564 }
1565 A.setFromTriplets(tripvec.begin(), tripvec.end());
1566
1567 Eigen::Vector<zono_float, -1> b (1);
1568 b(0) = 1;
1569
1570 // return conzono
1571 return std::make_unique<ConZono>(G, c, A, b, true);
1572}
1573
1574
1575inline std::unique_ptr<Zono> interval_2_zono(const Box& box)
1576{
1577 // generator matrix
1578 std::vector<Eigen::Triplet<zono_float>> triplets;
1579 Eigen::SparseMatrix<zono_float> G (static_cast<Eigen::Index>(box.size()), static_cast<Eigen::Index>(box.size()));
1580 for (int i=0; i<static_cast<int>(box.size()); i++)
1581 {
1582 triplets.emplace_back(i, i, box[i].width()/two);
1583 }
1584 G.setFromSortedTriplets(triplets.begin(), triplets.end());
1585
1586 // center
1587 Eigen::Vector<zono_float, -1> c = box.center();
1588
1589 // return zonotope
1590 return std::make_unique<Zono>(G, c, false);
1591}
1592
1593
1594inline std::unique_ptr<Zono> make_regular_zono_2D(const zono_float radius, const int n_sides, const bool outer_approx, const Eigen::Vector<zono_float, 2>& c)
1595{
1596 // check number of sides
1597 if (n_sides % 2 != 0 || n_sides < 4)
1598 {
1599 throw std::invalid_argument("make_regular_zono_2D: number of sides must be even and >= 4.");
1600 }
1601
1602 // check radius
1603 if (radius <= 0)
1604 {
1605 throw std::invalid_argument("make_regular_zono_2D: radius must be positive.");
1606 }
1607
1608 // problem parameters
1609 const int n_gens = n_sides/2;
1610 const zono_float dphi = pi / static_cast<zono_float>(n_gens);
1611 const zono_float R = outer_approx ? radius/std::cos(dphi/2) : radius;
1612
1613 // generator matrix
1614 const int n_gens_2 = n_gens/2;
1615 zono_float phi = (static_cast<zono_float>(n_gens_2))*dphi;
1616 const zono_float l_side = 2*R*std::sin(dphi/2);
1617 Eigen::Matrix<zono_float, -1, -1> G(2, n_gens);
1618 for (int i = 0; i < n_gens; i++)
1619 {
1620 G(0, i) = l_side*std::cos(phi);
1621 G(1, i) = l_side*std::sin(phi);
1622 phi -= dphi;
1623 }
1624
1625 // return zonotope
1626 return std::make_unique<Zono>(p5*G.sparseView(), c, false);
1627}
1628
1629// convex relaxation
1630inline std::unique_ptr<ConZono> HybZono::convex_relaxation() const { return std::make_unique<ConZono>(this->G, this->c, this->A, this->b, this->zero_one_form); }
1631
1632// bounding box
1633inline Box HybZono::do_bounding_box(const OptSettings &settings, OptSolution* solution)
1634{
1635 // if sharp, compute from convex relaxation
1636 if (this->sharp)
1637 {
1638 const auto Z_CR = this->convex_relaxation();
1639 return Z_CR->bounding_box(settings, solution);
1640 }
1641
1642 // make sure dimension is at least 1
1643 if (this->n == 0)
1644 {
1645 throw std::invalid_argument("Bounding box: empty set");
1646 }
1647
1648 // init search direction for bounding box
1649 Eigen::Vector<zono_float, -1> d (this->n);
1650 d.setZero();
1651
1652 // declarations
1653 Box box (this->n); // init
1654 zono_float s_neg, s_pos;
1655
1656 // build QP for ADMM
1657 const Eigen::SparseMatrix<zono_float> P (this->nG, this->nG);
1658 Eigen::Vector<zono_float, -1> q = -this->G.transpose()*d;
1659
1660 // mixed-integer solution
1661
1662 // get support in all box directions
1663 for (int i=0; i<this->n; i++)
1664 {
1665 // negative direction
1666
1667 // update QP
1668 d.setZero();
1669 d(i) = -1;
1670 q = -this->G.transpose()*d;
1671
1672 // solve
1673 OptSolution sol = this->mi_opt(P, q, 0, this->A, this->b, settings);
1674 if (sol.infeasible)
1675 throw std::invalid_argument("Bounding box: Z is empty");
1676 else
1677 s_neg = -d.dot(this->G*sol.z + this->c);
1678
1679 // positive direction
1680
1681 // update QP
1682 d.setZero();
1683 d(i) = 1;
1684 q = -this->G.transpose()*d;
1685
1686 // solve
1687 sol = this->mi_opt(P, q, 0, this->A, this->b, settings);
1688 if (sol.infeasible)
1689 throw std::invalid_argument("Bounding box: Z is empty");
1690 else
1691 s_pos = d.dot(this->G*sol.z + this->c);
1692
1693 // store bounds
1694 box[i] = Interval(s_neg, s_pos);
1695 }
1696
1697 return box;
1698}
1699
1700inline zono_float HybZono::do_support(const Eigen::Vector<zono_float, -1>& d, const OptSettings &settings,
1701 OptSolution* solution)
1702{
1703 // check dimensions
1704 if (this->n != d.size())
1705 {
1706 throw std::invalid_argument("Support: inconsistent dimensions.");
1707 }
1708
1709 // if sharp, can solve as convex optimization problem
1710 if (this->sharp)
1711 {
1712 const auto Zc = this->convex_relaxation();
1713 return Zc->support(d, settings, solution);
1714 }
1715
1716 // cost
1717 Eigen::SparseMatrix<zono_float> P (this->nG, this->nG);
1718 Eigen::Vector<zono_float, -1> q = -this->G.transpose()*d;
1719
1720 // solve MIQP
1721 const OptSolution sol = this->mi_opt(P, q, 0, this->A, this->b, settings, solution);
1722
1723 // check feasibility and return solution
1724 if (sol.infeasible) // Z is empty
1725 throw std::invalid_argument("Support: infeasible");
1726 else
1727 return d.dot(this->G*sol.z + this->c);
1728}
1729
1730inline std::vector<ConZono> HybZono::get_leaves(const bool remove_redundancy, const OptSettings &settings,
1731 OptSolution* solution, const int n_leaves, const int contractor_iter) const
1732{
1733 // allocate all threads to branch and bound
1735
1736 // get leaves as conzonos
1737 const std::vector<Eigen::Vector<zono_float, -1>> bin_leaves = this->get_bin_leaves(settings_get_leaves, solution, n_leaves);
1738 std::vector<ConZono> leaves;
1739 for (auto &xi_b : bin_leaves)
1740 {
1741 Eigen::Vector<zono_float, -1> cp = this->c + this->Gb*xi_b;
1742 Eigen::Vector<zono_float, -1> bp = this->b - this->Ab*xi_b;
1743 leaves.emplace_back(this->Gc, cp, this->Ac, bp, this->zero_one_form);
1744 }
1745 if (remove_redundancy)
1746 {
1747 for (auto &leaf : leaves)
1748 {
1749 leaf.remove_redundancy(contractor_iter);
1750 }
1751 }
1752
1753 return leaves;
1754}
1755
1756
1758{
1759 // make sure there are constraints to remove
1760 if (this->nC == 0) return;
1761
1762 // put set into [-1, 1] form
1763 if (this->zero_one_form) this->convert_form();
1764
1765 // execute algorithm 1 from paper
1766 Eigen::Vector<zono_float, -1> x_lb (this->nG);
1767 Eigen::Vector<zono_float, -1> x_ub (this->nG);
1768 x_lb.setConstant(-1);
1769 x_ub.setConstant(1);
1770 Box E (x_lb, x_ub);
1771 x_lb.setConstant(-std::numeric_limits<zono_float>::infinity());
1772 x_ub.setConstant(std::numeric_limits<zono_float>::infinity());
1773 Box R (x_lb, x_ub);
1774 Eigen::SparseMatrix<zono_float, Eigen::RowMajor> A_rm = this->A;
1775 for (int i=0; i<this->nC; ++i)
1776 {
1777 for (Eigen::SparseMatrix<zono_float, Eigen::RowMajor>::InnerIterator it_j(A_rm, i); it_j; ++it_j)
1778 {
1779 const zono_float a_ij = it_j.value();
1780 Interval y (this->b(i) / a_ij, this->b(i) / a_ij);
1781 for (Eigen::SparseMatrix<zono_float, Eigen::RowMajor>::InnerIterator it_k(A_rm, i); it_k; ++it_k)
1782 {
1783 if (it_j.col() == it_k.col()) continue;
1784 y = y - E[it_k.col()].to_interval()*(it_k.value()/a_ij);
1785 }
1786 R[it_j.col()].intersect_assign(R[it_j.col()], y.as_view());
1787 E[it_j.col()].intersect_assign(E[it_j.col()], R[it_j.col()]);
1788 }
1789 }
1790
1791 // make sure conzono isn't empty (interval check)
1792 for (int j=0; j<this->nG; ++j)
1793 {
1794 if (E[j].is_empty())
1795 throw std::runtime_error("ConZono constraint reduction: set is empty");
1796 }
1797
1798 // build Q matrix
1799 Eigen::SparseMatrix<zono_float> Q (this->nG + this->nC, this->nG + this->nC);
1800
1801 Eigen::SparseMatrix<zono_float> I_nG (this->nG, this->nG);
1802 I_nG.setIdentity();
1803 const Eigen::SparseMatrix<zono_float> Phi = this->G.transpose()*this->G + I_nG;
1804
1805 std::vector<Eigen::Triplet<zono_float>> triplets;
1807 get_triplets_offset<zono_float>(this->A, triplets, this->nG, 0);
1808 get_triplets_offset<zono_float>(this->A.transpose(), triplets, 0, this->nG);
1809 Q.setFromTriplets(triplets.begin(), triplets.end());
1810
1811 // factorize Q
1812 Eigen::SimplicialLDLT<Eigen::SparseMatrix<zono_float>> Q_ldlt (Q);
1813 if (Q_ldlt.info() != Eigen::Success)
1814 throw std::runtime_error("ConZono constraint reduction: Q matrix factorization failed, most likely A is not full row rank.");
1815
1816 // get estimated Hausdorff error for eliminating each generator
1817 std::vector<std::pair<int, zono_float>> haus_vec; // (index, error)
1818 haus_vec.reserve(this->nG);
1819 auto shift_permute = [size=this->nG + this->nC + 1](const int start_index, const int end_index) -> Eigen::PermutationMatrix<Eigen::Dynamic, Eigen::Dynamic>
1820 {
1821 assert(start_index >= 0 && end_index >= 0 && start_index < size && end_index < size && start_index < end_index);
1822 Eigen::PermutationMatrix<Eigen::Dynamic, Eigen::Dynamic> P(size);
1823 for (int i=0; i<start_index; ++i)
1824 {
1825 P.indices()[i] = i;
1826 }
1827 P.indices()[start_index] = end_index;
1828 for (int i=start_index+1; i<=end_index; ++i)
1829 {
1830 P.indices()[i] = i-1;
1831 }
1832 for (int i=end_index+1; i<size; ++i)
1833 {
1834 P.indices()[i] = i;
1835 }
1836 return P;
1837 };
1838 Eigen::Vector<zono_float, -1> e_j (this->nG + this->nC);
1839 for (int j=0; j<this->nG; ++j)
1840 {
1841 // get r_j
1842 const zono_float r_j = std::max<zono_float>(zero, std::max<zono_float>(std::abs(R[j].to_interval().lb), std::abs(R[j].to_interval().ub)) - one);
1843 if (r_j < zono_eps)
1844 {
1845 haus_vec.emplace_back(j, zero);
1846 continue;
1847 }
1848
1849 // linear system matrix
1850 e_j.setZero();
1851 e_j(j) = 1;
1852 const Eigen::Vector<zono_float, -1> Qinv_e_j = Q_ldlt.solve(e_j);
1853
1854 triplets.clear();
1855 for (int i=0; i<this->nG + this->nC; ++i)
1856 {
1857 triplets.emplace_back(i, i, one);
1858 if (i == j)
1859 {
1860 triplets.emplace_back(this->nG + this->nC, j, one); // extra row for e_j^T
1861 }
1862 }
1863 for (int i=0; i<this->nG + this->nC; ++i)
1864 {
1865 triplets.emplace_back(i, this->nG + this->nC, Qinv_e_j(i));
1866 }
1867 Eigen::SparseMatrix<zono_float> M (this->nG + this->nC + 1, this->nG + this->nC + 1);
1868 M.setFromSortedTriplets(triplets.begin(), triplets.end());
1869
1870 // RHS for linear system
1871 Eigen::Vector<zono_float, -1> rhs (this->nG + this->nC + 1);
1872 rhs.setZero();
1873 rhs(this->nG + this->nC) = r_j;
1874
1875 // permutation matrices to make system upper triangular
1876 const auto P_R = shift_permute(j, this->nG + this->nC);
1877 const auto P_L = shift_permute(j, this->nG + this->nC - 1);
1878
1879 // solve linear system in permuted space
1880 const Eigen::SparseMatrix<zono_float> M_perm = P_L*M*P_R.inverse();
1881 const Eigen::Vector<zono_float, -1> rhs_perm = P_L*rhs;
1882 const Eigen::Vector<zono_float, -1> y_perm = M_perm.triangularView<Eigen::Upper>().solve(rhs_perm);
1883 const Eigen::Vector<zono_float, -1> y = P_R*y_perm;
1884
1885 // get Hausdorff distance estimate
1886 const Eigen::Vector<zono_float, -1> d = y.segment(0, this->nG);
1887 const zono_float haus_j = (this->G*d).norm() + d.norm();
1888 haus_vec.emplace_back(j, haus_j);
1889 }
1890
1891 // sort by Hausdorff error
1892 std::sort(haus_vec.begin(), haus_vec.end(), [](const auto &a, const auto &b) { return a.second < b.second; });
1893
1894 Eigen::SparseMatrix<zono_float> Ea (this->nG, this->nC); // init
1895 int gen_remove = -1;
1896 int cons_remove = -1;
1897 for (const auto& [j, err] : haus_vec)
1898 {
1899 // loop through column k of A to find a constraint to remove
1900 for (Eigen::SparseMatrix<zono_float>::InnerIterator it(this->A, j); it; ++it)
1901 {
1902 if (std::abs(it.value()) > zono_eps)
1903 {
1904 triplets.emplace_back(j, static_cast<int>(it.row()), one/it.value());
1905 Ea.insert(j, it.row()) = 1/it.value();
1906 gen_remove = j;
1907 cons_remove = static_cast<int>(it.row());
1908 break;
1909 }
1910 }
1911
1912 // check if done
1913 if (gen_remove != -1 && cons_remove != -1)
1914 break;
1915 }
1916 if (gen_remove == -1 || cons_remove == -1)
1917 throw std::runtime_error("ConZono: constraint reduction cannot find valid constraint to remove");
1918
1919 // apply algorithm from Scott paper
1920 const Eigen::SparseMatrix<zono_float> Lambda_G = G*Ea;
1921 const Eigen::SparseMatrix<zono_float> Lambda_A = A*Ea;
1922 Eigen::SparseMatrix<zono_float> Gp = this->G - Lambda_G*this->A;
1923 Eigen::Vector<zono_float, -1> cp = this->c + Lambda_G*this->b;
1924 Eigen::SparseMatrix<zono_float> Ap = this->A - Lambda_A*this->A;
1925 Eigen::Vector<zono_float, -1> bp = this->b - Lambda_A*this->b;
1926
1927 // generator removal matrix
1928 triplets.clear();
1929 for (int j=0; j<gen_remove; ++j)
1930 {
1931 triplets.emplace_back(j, j, one);
1932 }
1933 for (int j=gen_remove+1; j<this->nG; ++j)
1934 {
1935 triplets.emplace_back(j, j-1, one);
1936 }
1937 Eigen::SparseMatrix<zono_float> dG (this->nG, this->nG - 1);
1938 dG.setFromSortedTriplets(triplets.begin(), triplets.end());
1939
1940 // constraint removal matrix
1941 triplets.clear();
1942 for (int i=0; i<cons_remove; ++i)
1943 {
1944 triplets.emplace_back(i, i, one);
1945 }
1946 for (int i=cons_remove+1; i<this->nC; ++i)
1947 {
1948 triplets.emplace_back(i-1, i, one);
1949 }
1950 Eigen::SparseMatrix<zono_float> dA (this->nC - 1, this->nC);
1951 dA.setFromSortedTriplets(triplets.begin(), triplets.end());
1952
1953 // update
1954 this->set(Gp*dG, cp, dA*Ap*dG, dA*bp, false);
1955}
1956
1957inline std::unique_ptr<Zono> ConZono::to_zono_approx() const
1958{
1959 // check for case that there are no constraints
1960 if (this->nG == 0)
1961 {
1962 return std::make_unique<Point>(this->c);
1963 }
1964 if (this->nC == 0)
1965 {
1966 return std::make_unique<Zono>(this->G, this->c, this->zero_one_form);
1967 }
1968
1969 // compute SVD of A
1970 const Eigen::BDCSVD<Eigen::Matrix<zono_float, -1, -1>, Eigen::ComputeFullV | Eigen::ComputeFullU> svd (this->A.toDense());
1971 const Eigen::Vector<zono_float, -1>& sin_vals = svd.singularValues();
1972
1973 const int n = this->nG;
1974 const int r = static_cast<int>(svd.rank());
1975 const int d = n-r;
1976 const Eigen::Matrix<zono_float, -1, -1> Vr = svd.matrixV().block(0, 0, n, r);
1977 const Eigen::Matrix<zono_float, -1, -1> Vd = svd.matrixV().block(0, r, n, d);
1978
1979 // get xi_tilde_r
1980 const Eigen::Vector<zono_float, -1> b_tilde = svd.matrixU().transpose()*this->b;
1981 const Eigen::Vector<zono_float, -1> xi_tilde_r = b_tilde.segment(0, r).array() / sin_vals.segment(0, r).array();
1982
1983 // build bounding zonotope
1984 const Eigen::SparseMatrix<zono_float> Vd_VdT_sp = (Vd*Vd.transpose()).sparseView();
1985 const Eigen::SparseMatrix<zono_float> G_zono = this->G*Vd_VdT_sp;
1986 const Eigen::Vector<zono_float, -1> c_zono = this->c + this->G*(Vr*xi_tilde_r);
1987 return std::make_unique<Zono>(G_zono, c_zono, this->zero_one_form);
1988}
1989
1990} // namespace ZonoOpt
1991
1992#endif
Constrained zonotope class for ZonoOpt library.
Empty Set class for ZonoOpt library.
Hybrid zonotope class for ZonoOpt library.
Point class for ZonoOpt library.
Utilities for sparse matrix operations in ZonoOpt library.
Zonotope class for ZonoOpt library.
Box (i.e., interval vector) class.
Definition Intervals.hpp:718
Eigen::Vector< zono_float, -1 > center() const
get center of box
Definition Intervals.hpp:891
size_t size() const
get size of Box object
Definition Intervals.hpp:830
Constrained zonotope class.
Definition ConZono.hpp:33
void set(const Eigen::SparseMatrix< zono_float > &G, const Eigen::Vector< zono_float, -1 > &c, const Eigen::SparseMatrix< zono_float > &A, const Eigen::Vector< zono_float, -1 > &b, bool zero_one_form=false)
Reset constrained zonotope object with the given parameters.
Definition ConZono.hpp:150
virtual void constraint_reduction()
Execute constraint reduction algorithm from Scott et. al. 2016.
Definition PolymorphicFunctions.hpp:1757
std::unique_ptr< HybZono > do_complement(zono_float delta_m, bool, const OptSettings &, OptSolution *, int, int) override
Definition PolymorphicFunctions.hpp:1021
void convert_form() override
Converts the set representation between -1-1 and 0-1 forms.
Definition ConZono.hpp:179
virtual std::unique_ptr< Zono > to_zono_approx() const
Compute outer approximation of constrained zonotope as zonotope using SVD.
Definition PolymorphicFunctions.hpp:1957
Empty Set class.
Definition EmptySet.hpp:26
Hybrid zonotope class.
Definition HybZono.hpp:44
virtual bool is_0_1_form() const
Returns true if factors are in range [0,1], false if they are in range [-1,1].
Definition HybZono.hpp:199
int nC
number of constraints
Definition HybZono.hpp:482
bool is_empty(const OptSettings &settings=OptSettings(), OptSolution *solution=nullptr) const
Returns true if the set is provably empty, false otherwise.
Definition HybZono.hpp:351
int n
set dimension
Definition HybZono.hpp:470
OptSolution mi_opt(const Eigen::SparseMatrix< zono_float > &P, const Eigen::Vector< zono_float, -1 > &q, zono_float c, const Eigen::SparseMatrix< zono_float > &A, const Eigen::Vector< zono_float, -1 > &b, const OptSettings &settings=OptSettings(), OptSolution *solution=nullptr) const
Definition HybZono.hpp:987
bool is_hybzono() const
Polymorphic type checking: true if set is a hybrid zonotope.
Definition PolymorphicFunctions.hpp:56
bool zero_one_form
flag to indicate whether the set is in 0-1 or -1-1 form
Definition HybZono.hpp:485
Eigen::SparseMatrix< zono_float > Gc
continuous generator matrix
Definition HybZono.hpp:449
virtual void convert_form()
Converts the set representation between -1-1 and 0-1 forms.
Definition HybZono.hpp:741
Eigen::SparseMatrix< zono_float > Gb
binary generator matrix
Definition HybZono.hpp:452
int nG
total number of factors. nG = nGc + nGb
Definition HybZono.hpp:473
bool is_conzono() const
Polymorphic type checking: true if set is a constrained zonotope.
Definition PolymorphicFunctions.hpp:47
virtual Box do_bounding_box(const OptSettings &settings, OptSolution *solution)
Definition PolymorphicFunctions.hpp:1633
virtual zono_float do_support(const Eigen::Vector< zono_float, -1 > &d, const OptSettings &settings, OptSolution *solution)
Definition PolymorphicFunctions.hpp:1700
bool is_point() const
Polymorphic type checking: true if set is a point.
Definition PolymorphicFunctions.hpp:34
virtual HybZono * clone() const
Clone method for polymorphic behavior.
Definition HybZono.hpp:96
void set(const Eigen::SparseMatrix< zono_float > &Gc, const Eigen::SparseMatrix< zono_float > &Gb, const Eigen::Vector< zono_float, -1 > &c, const Eigen::SparseMatrix< zono_float > &Ac, const Eigen::SparseMatrix< zono_float > &Ab, const Eigen::Vector< zono_float, -1 > &b, bool zero_one_form=false, bool sharp=false)
Reset hybrid zonotope object with the given parameters.
Definition HybZono.hpp:713
virtual std::unique_ptr< ConZono > convex_relaxation() const
Returns convex relaxation of the hybrid zonotope.
Definition PolymorphicFunctions.hpp:1630
bool sharp
flag to indicate whether the set is known to be sharp (i.e., convex relaxation = convex hull)
Definition HybZono.hpp:488
Eigen::Vector< zono_float, -1 > c
center vector
Definition HybZono.hpp:464
int nGb
number of binary factors
Definition HybZono.hpp:479
bool is_zono() const
Polymorphic type checking: true if set is a zonotope.
Definition PolymorphicFunctions.hpp:40
Eigen::SparseMatrix< zono_float > A
constraint matrix A = [Ac, Ab]
Definition HybZono.hpp:455
Eigen::Vector< zono_float, -1 > b
constraint vector
Definition HybZono.hpp:467
virtual std::unique_ptr< HybZono > do_complement(zono_float, bool remove_redundancy, const OptSettings &settings, OptSolution *solution, int n_leaves, int contractor_iter)
Definition PolymorphicFunctions.hpp:989
friend std::unique_ptr< HybZono > intersection(const HybZono &Z1, HybZono &Z2, const Eigen::SparseMatrix< zono_float > &R)
Computes the generalized intersection of sets Z1 and Z2 over the matrix R.
Definition PolymorphicFunctions.hpp:198
bool is_empty_set() const
Polymorphic type checking: true if set is empty set object.
Definition PolymorphicFunctions.hpp:64
std::vector< ConZono > get_leaves(bool remove_redundancy=true, const OptSettings &settings=OptSettings(), OptSolution *solution=nullptr, int n_leaves=std::numeric_limits< int >::max(), int contractor_iter=100) const
Computes individual constrained zonotopes whose union is the hybrid zonotope object.
Definition PolymorphicFunctions.hpp:1730
Eigen::SparseMatrix< zono_float > Ab
binary constraint matrix
Definition HybZono.hpp:461
Eigen::SparseMatrix< zono_float > Ac
continuous constraint matrix
Definition HybZono.hpp:458
Eigen::SparseMatrix< zono_float > G
generator matrix G = [Gc, Gb]
Definition HybZono.hpp:446
int nGc
number of continuous factors
Definition HybZono.hpp:476
virtual std::unique_ptr< HybZono > complement(const zono_float delta_m=100, const bool remove_redundancy=true, const OptSettings &settings=OptSettings(), OptSolution *solution=nullptr, const int n_leaves=std::numeric_limits< int >::max(), const int contractor_iter=100)
Computes the complement of the set Z.
Definition HybZono.hpp:258
Inequality class.
Definition Inequality.hpp:50
void set_ineq_type(const IneqType type)
Sets the direction of the inequality or sets it to be an equality.
Definition Inequality.hpp:99
void set_rhs(const zono_float rhs)
Set the right hand side of the inequality.
Definition Inequality.hpp:89
void add_term(const int idx, const zono_float coeff)
Adds a term to the inequality.
Definition Inequality.hpp:76
Point class.
Definition Point.hpp:28
Zonotope class.
Definition Zono.hpp:33
#define zono_float
Defines the floating-point type used in ZonoOpt.
Definition ZonoOpt.hpp:43
#define zono_eps
Defines the precision used for floating point comparisons in ZonoOpt.
Definition ZonoOpt.hpp:51
std::unique_ptr< HybZono > cartesian_product(const HybZono &Z1, HybZono &Z2)
Computes the Cartesian product of two sets Z1 and Z2.
Definition PolymorphicFunctions.hpp:808
std::unique_ptr< HybZono > set_diff(const HybZono &Z1, HybZono &Z2, zono_float delta_m=100, bool remove_redundancy=true, const OptSettings &settings=OptSettings(), OptSolution *solution=nullptr, int n_leaves=std::numeric_limits< int >::max(), int contractor_iter=100)
Set difference Z1 \ Z2.
Definition PolymorphicFunctions.hpp:1248
std::unique_ptr< HybZono > union_of_many(const std::vector< HybZono * > &Zs, bool preserve_sharpness=false, bool expose_indicators=false)
Computes union of several sets.
Definition PolymorphicFunctions.hpp:516
std::unique_ptr< HybZono > minkowski_sum(const HybZono &Z1, HybZono &Z2)
Computes Minkowski sum of two sets Z1 and Z2.
Definition PolymorphicFunctions.hpp:143
std::unique_ptr< HybZono > project_onto_dims(const HybZono &Z, const std::vector< int > &dims)
Projects set Z onto the dimensions specified in dims.
Definition PolymorphicFunctions.hpp:114
std::unique_ptr< HybZono > affine_map(const HybZono &Z, const Eigen::SparseMatrix< zono_float > &R, const Eigen::Vector< zono_float, -1 > &s=Eigen::Vector< zono_float, -1 >())
Returns affine map R*Z + s of set Z.
Definition PolymorphicFunctions.hpp:71
std::unique_ptr< HybZono > intersection(const HybZono &Z1, HybZono &Z2, const Eigen::SparseMatrix< zono_float > &R=Eigen::SparseMatrix< zono_float >())
Computes the generalized intersection of sets Z1 and Z2 over the matrix R.
Definition PolymorphicFunctions.hpp:198
std::unique_ptr< HybZono > halfspace_intersection(HybZono &Z, const Eigen::SparseMatrix< zono_float > &H, const Eigen::Vector< zono_float, -1 > &f, const Eigen::SparseMatrix< zono_float > &R=Eigen::SparseMatrix< zono_float >())
Computes the intersection generalized intersection of set Z with halfspace H*x <= f over matrix R.
Definition PolymorphicFunctions.hpp:304
std::unique_ptr< HybZono > intersection_over_dims(const HybZono &Z1, HybZono &Z2, const std::vector< int > &dims)
Computes the generalized intersection of sets Z1 and Z2 over the specified dimensions.
Definition PolymorphicFunctions.hpp:273
std::unique_ptr< HybZono > pontry_diff(HybZono &Z1, HybZono &Z2, bool exact=false)
Computes the Pontryagin difference Z1 - Z2.
Definition PolymorphicFunctions.hpp:334
std::unique_ptr< HybZono > constrain(HybZono &Z, const std::vector< Inequality > &ineqs, const Eigen::SparseMatrix< zono_float > &R=Eigen::SparseMatrix< zono_float >())
Applies inequalities to set.
Definition PolymorphicFunctions.hpp:867
std::unique_ptr< HybZono > vrep_2_hybzono(const std::vector< Eigen::Matrix< zono_float, -1, -1 > > &Vpolys, bool expose_indicators=false)
Computes a hybrid zonotope from a union of vertex representation polytopes.
Definition PolymorphicFunctions.hpp:1411
std::unique_ptr< HybZono > zono_union_2_hybzono(std::vector< Zono > &Zs, bool expose_indicators=false)
Computes a hybrid zonotope from a union of zonotopes.
Definition PolymorphicFunctions.hpp:1264
std::unique_ptr< Zono > interval_2_zono(const Box &box)
Builds a zonotope from a Box object.
Definition PolymorphicFunctions.hpp:1575
std::unique_ptr< Zono > make_regular_zono_2D(const zono_float radius, const int n_sides, const bool outer_approx, const Eigen::Vector< zono_float, 2 > &c)
Builds a 2D regular zonotope with a given radius and number of sides.
Definition PolymorphicFunctions.hpp:1594
std::unique_ptr< ConZono > vrep_2_conzono(const Eigen::Matrix< zono_float, -1, -1 > &Vpoly)
Builds a constrained zonotope from a vertex representation polytope.
Definition PolymorphicFunctions.hpp:1547
Definition ADMM.hpp:41
@ LESS_OR_EQUAL
Definition Inequality.hpp:39
@ GREATER_OR_EQUAL
Definition Inequality.hpp:40
@ GREATER
Definition Inequality.hpp:43
@ EQUAL
Definition Inequality.hpp:41
@ LESS
Definition Inequality.hpp:42
Interval class.
Definition Intervals.hpp:372
Settings for optimization routines in ZonoOpt library.
Definition SolverDataStructures.hpp:26
Solution data structure for optimization routines in ZonoOpt library.
Definition SolverDataStructures.hpp:153
Eigen::Vector< zono_float, -1 > z
solution vector
Definition SolverDataStructures.hpp:157