From 5b970f94146b26df4f54d00a3d6076b7b832bd6c Mon Sep 17 00:00:00 2001 From: SimonRohou Date: Tue, 24 May 2022 15:38:06 +0200 Subject: [PATCH 001/256] [codac2] towards new implementation of tubes --- src/core/2/domain/tube/codac2_SliceVector.cpp | 113 +++++++++++++ src/core/2/domain/tube/codac2_SliceVector.h | 77 +++++++++ src/core/2/domain/tube/codac2_TDomain.cpp | 63 +++++++ src/core/2/domain/tube/codac2_TDomain.h | 47 ++++++ src/core/2/domain/tube/codac2_TSlice.cpp | 46 +++++ src/core/2/domain/tube/codac2_TSlice.h | 46 +++++ .../domain/tube/codac2_TubeAbstract_const.cpp | 31 ++++ .../2/domain/tube/codac2_TubeAbstract_const.h | 77 +++++++++ src/core/2/domain/tube/codac2_TubeVector.cpp | 126 ++++++++++++++ src/core/2/domain/tube/codac2_TubeVector.h | 158 ++++++++++++++++++ .../tube/codac2_TubeVectorComponent.cpp | 43 +++++ .../domain/tube/codac2_TubeVectorComponent.h | 46 +++++ src/core/CMakeLists.txt | 14 ++ 13 files changed, 887 insertions(+) create mode 100644 src/core/2/domain/tube/codac2_SliceVector.cpp create mode 100644 src/core/2/domain/tube/codac2_SliceVector.h create mode 100644 src/core/2/domain/tube/codac2_TDomain.cpp create mode 100644 src/core/2/domain/tube/codac2_TDomain.h create mode 100644 src/core/2/domain/tube/codac2_TSlice.cpp create mode 100644 src/core/2/domain/tube/codac2_TSlice.h create mode 100644 src/core/2/domain/tube/codac2_TubeAbstract_const.cpp create mode 100644 src/core/2/domain/tube/codac2_TubeAbstract_const.h create mode 100644 src/core/2/domain/tube/codac2_TubeVector.cpp create mode 100644 src/core/2/domain/tube/codac2_TubeVector.h create mode 100644 src/core/2/domain/tube/codac2_TubeVectorComponent.cpp create mode 100644 src/core/2/domain/tube/codac2_TubeVectorComponent.h diff --git a/src/core/2/domain/tube/codac2_SliceVector.cpp b/src/core/2/domain/tube/codac2_SliceVector.cpp new file mode 100644 index 000000000..30912bbc2 --- /dev/null +++ b/src/core/2/domain/tube/codac2_SliceVector.cpp @@ -0,0 +1,113 @@ +/** + * \file + * + * ---------------------------------------------------------------------------- + * \date 2022 + * \author Simon Rohou + * \copyright Copyright 2022 Codac Team + * \license This program is distributed under the terms of + * the GNU Lesser General Public License (LGPL). + */ + +#include "codac2_TubeVector.h" +#include "codac2_SliceVector.h" + +using namespace std; + +namespace codac2 +{ + SliceVector::SliceVector(size_t n, const TubeVector& tube_vector, list>::iterator it_tslice) : + _tube_vector(tube_vector), _it_tslice(it_tslice), + _codomain(IntervalVector(n)) + { + + } + + SliceVector::~SliceVector() + { + + } + + const TubeVector& SliceVector::tube_vector() const + { + return _tube_vector; + } + + size_t SliceVector::size() const + { + return _codomain.size(); + } + + bool SliceVector::contains(const TrajectoryVector& value) const + { + return true; + } + + const Interval& SliceVector::tdomain() const + { + return _it_tslice->get()->tdomain(); + } + + shared_ptr SliceVector::prev_slice() const + { + if(_tube_vector.first_slice().get() == this) + return shared_ptr(nullptr); + return prev(_it_tslice)->get()->slices().at(&_tube_vector); + } + + shared_ptr SliceVector::prev_slice() + { + if(_tube_vector.first_slice().get() == this) + return shared_ptr(nullptr); + return prev(_it_tslice)->get()->slices().at(&_tube_vector); + } + + shared_ptr SliceVector::next_slice() const + { + if(_tube_vector.last_slice().get() == this) + return shared_ptr(nullptr); + return next(_it_tslice)->get()->slices().at(&_tube_vector); + } + + shared_ptr SliceVector::next_slice() + { + if(_tube_vector.last_slice().get() == this) + return shared_ptr(nullptr); + return next(_it_tslice)->get()->slices().at(&_tube_vector); + } + + const IntervalVector& SliceVector::codomain() const + { + return _codomain; + } + + const IntervalVector SliceVector::input_gate() const + { + IntervalVector gate = codomain(); + if(prev_slice()) + gate &= prev_slice().get()->codomain(); + return gate; + } + + const IntervalVector SliceVector::output_gate() const + { + IntervalVector gate = codomain(); + if(next_slice()) + gate &= next_slice().get()->codomain(); + return gate; + } + + void SliceVector::set(const IntervalVector& codomain) + { + assert((size_t)codomain.size() == size()); + _codomain = codomain; + } + + ostream& operator<<(ostream& os, const SliceVector& x) + { + os << x.tdomain() + << "↦" << x.codomain() + << flush; + return os; + } +} // namespace codac \ No newline at end of file diff --git a/src/core/2/domain/tube/codac2_SliceVector.h b/src/core/2/domain/tube/codac2_SliceVector.h new file mode 100644 index 000000000..842875661 --- /dev/null +++ b/src/core/2/domain/tube/codac2_SliceVector.h @@ -0,0 +1,77 @@ +/** + * \file + * + * ---------------------------------------------------------------------------- + * \date 2022 + * \author Simon Rohou + * \copyright Copyright 2022 Codac Team + * \license This program is distributed under the terms of + * the GNU Lesser General Public License (LGPL). + */ + +#ifndef __CODAC2_SLICEVECTOR_H__ +#define __CODAC2_SLICEVECTOR_H__ + +#include +#include "codac_Interval.h" +#include "codac_IntervalVector.h" +#include "codac_TrajectoryVector.h" +#include "codac2_TSlice.h" +#include "codac2_TDomain.h" + +namespace codac2 +{ + using codac::Interval; + using codac::IntervalVector; + using codac::TrajectoryVector; + + class TubeVector; + class TSlice; + + class SliceVector + { + public: + + explicit SliceVector(size_t n, const TubeVector& tube_vector, std::list>::iterator it_tslice); + ~SliceVector(); + + const TubeVector& tube_vector() const; + + // SliceVector objects cannot be copyable or movable, + // as they are supposed to be connected to other SliceVector objects + SliceVector& operator=(const SliceVector&) = delete; + SliceVector(SliceVector&&) = delete; + SliceVector& operator=(SliceVector&&) = delete; + + size_t size() const; + + bool contains(const TrajectoryVector& value) const; + + std::shared_ptr prev_slice() const; + std::shared_ptr prev_slice(); + std::shared_ptr next_slice() const; + std::shared_ptr next_slice(); + + const Interval& tdomain() const; + + const IntervalVector& codomain() const; + const IntervalVector input_gate() const; + const IntervalVector output_gate() const; + + void set(const IntervalVector& codomain); + + friend std::ostream& operator<<(std::ostream& os, const SliceVector& x); + + + protected: + + friend class TubeVector; + //friend class TubeVectorComponent; + + const TubeVector& _tube_vector; + std::list>::iterator _it_tslice; + IntervalVector _codomain; + }; +} // namespace codac + +#endif \ No newline at end of file diff --git a/src/core/2/domain/tube/codac2_TDomain.cpp b/src/core/2/domain/tube/codac2_TDomain.cpp new file mode 100644 index 000000000..25238245e --- /dev/null +++ b/src/core/2/domain/tube/codac2_TDomain.cpp @@ -0,0 +1,63 @@ +/** + * \file + * + * ---------------------------------------------------------------------------- + * \date 2022 + * \author Simon Rohou + * \copyright Copyright 2022 Codac Team + * \license This program is distributed under the terms of + * the GNU Lesser General Public License (LGPL). + */ + +#include "codac2_TDomain.h" +#include "codac_predef_values.h" + +using namespace std; +using namespace codac; + +namespace codac2 +{ + TDomain::TDomain(const Interval& t0_tf, double dt, bool with_gates) + { + _tslices.push_back(make_shared(Interval(-oo,t0_tf.lb()))); + + for(double t = t0_tf.lb() ; t < t0_tf.ub() ; t+=dt) + { + if(with_gates) + _tslices.push_back(make_shared(Interval(t))); + _tslices.push_back(make_shared(Interval(t,std::min(t0_tf.ub(),t+dt)))); + } + + if(with_gates) + _tslices.push_back(make_shared(Interval(t0_tf.ub()))); + _tslices.push_back(make_shared(Interval(t0_tf.ub(),oo))); + + // End of container: + _tslices.push_back(make_shared(Interval(oo,oo))); + } + + const Interval TDomain::t0_tf() const + { + return Interval(_tslices.front()->tdomain().ub(), + prev(prev(_tslices.end()))->get()->tdomain().lb()); + } + + size_t TDomain::nb_tslices() const + { + return _tslices.size() - 1; + } + + const list> TDomain::tslices() const + { + return _tslices; + } + + ostream& operator<<(ostream& os, const TDomain& x) + { + os << x.t0_tf() + << ", " << x.nb_tslices() + << " slice" << (x.nb_tslices() > 1 ? "s" : "") + << flush; + return os; + } +} // namespace codac \ No newline at end of file diff --git a/src/core/2/domain/tube/codac2_TDomain.h b/src/core/2/domain/tube/codac2_TDomain.h new file mode 100644 index 000000000..b5e34e0c7 --- /dev/null +++ b/src/core/2/domain/tube/codac2_TDomain.h @@ -0,0 +1,47 @@ +/** + * \file + * + * ---------------------------------------------------------------------------- + * \date 2022 + * \author Simon Rohou + * \copyright Copyright 2022 Codac Team + * \license This program is distributed under the terms of + * the GNU Lesser General Public License (LGPL). + */ + +#ifndef __CODAC2_TDOMAIN_H__ +#define __CODAC2_TDOMAIN_H__ + +#include +#include +#include +#include + +#include "codac_Interval.h" +#include "codac2_TSlice.h" + +namespace codac2 +{ + using codac::Interval; + + class TDomain + { + public: + + explicit TDomain(const Interval& t0_tf, double dt, bool with_gates = false); + const Interval t0_tf() const; + size_t nb_tslices() const; + friend std::ostream& operator<<(std::ostream& os, const TDomain& x); + + + protected: + + const std::list> tslices() const; + + friend class SliceVector; + friend class TubeVector; + std::list> _tslices; + }; +} // namespace codac + +#endif \ No newline at end of file diff --git a/src/core/2/domain/tube/codac2_TSlice.cpp b/src/core/2/domain/tube/codac2_TSlice.cpp new file mode 100644 index 000000000..d4f096d93 --- /dev/null +++ b/src/core/2/domain/tube/codac2_TSlice.cpp @@ -0,0 +1,46 @@ +/** + * \file + * + * ---------------------------------------------------------------------------- + * \date 2022 + * \author Simon Rohou + * \copyright Copyright 2022 Codac Team + * \license This program is distributed under the terms of + * the GNU Lesser General Public License (LGPL). + */ + +#include "codac2_TSlice.h" +#include "codac2_SliceVector.h" + +using namespace std; +using namespace codac; + +namespace codac2 +{ + TSlice::TSlice(const Interval& t0_tf) : _tdomain(t0_tf) + { + + } + + const Interval& TSlice::tdomain() const + { + return _tdomain; + } + + const map> TSlice::slices() const + { + return _slices; + } + + void TSlice::add_slice(const std::shared_ptr& slice) + { + _slices.insert( + pair>(&slice.get()->tube_vector(), slice)); + } + + ostream& operator<<(ostream& os, const TSlice& x) + { + os << x._tdomain; + return os; + } +} // namespace codac \ No newline at end of file diff --git a/src/core/2/domain/tube/codac2_TSlice.h b/src/core/2/domain/tube/codac2_TSlice.h new file mode 100644 index 000000000..d857b605b --- /dev/null +++ b/src/core/2/domain/tube/codac2_TSlice.h @@ -0,0 +1,46 @@ +/** + * \file + * + * ---------------------------------------------------------------------------- + * \date 2022 + * \author Simon Rohou + * \copyright Copyright 2022 Codac Team + * \license This program is distributed under the terms of + * the GNU Lesser General Public License (LGPL). + */ + +#ifndef __CODAC2_TSLICE_H__ +#define __CODAC2_TSLICE_H__ + +#include +#include +#include +#include + +#include "codac_Interval.h" + +namespace codac2 +{ + using codac::Interval; + + class SliceVector; + class TubeVector; + + class TSlice + { + public: + + explicit TSlice(const Interval& t0_tf); + const Interval& tdomain() const; + const std::map> slices() const; + void add_slice(const std::shared_ptr& slice); + friend std::ostream& operator<<(std::ostream& os, const TSlice& x); + + protected: + + const Interval _tdomain; + std::map> _slices; + }; +} // namespace codac + +#endif \ No newline at end of file diff --git a/src/core/2/domain/tube/codac2_TubeAbstract_const.cpp b/src/core/2/domain/tube/codac2_TubeAbstract_const.cpp new file mode 100644 index 000000000..ac4313983 --- /dev/null +++ b/src/core/2/domain/tube/codac2_TubeAbstract_const.cpp @@ -0,0 +1,31 @@ +/** + * \file + * + * ---------------------------------------------------------------------------- + * \date 2022 + * \author Simon Rohou + * \copyright Copyright 2022 Codac Team + * \license This program is distributed under the terms of + * the GNU Lesser General Public License (LGPL). + */ + +#include "codac2_TubeAbstract_const.h" + +using namespace std; + +namespace codac2 +{ + /*template + TubeAbstract_const::TubeAbstract_const() + { + + } + + //ostream& operator<<(ostream& os, const TubeAbstract_const& x) + void TubeAbstract_const::print(ostream& os) const + { + os << tdomain() + << "↦" << codomain() + << flush; + }*/ +} // namespace codac \ No newline at end of file diff --git a/src/core/2/domain/tube/codac2_TubeAbstract_const.h b/src/core/2/domain/tube/codac2_TubeAbstract_const.h new file mode 100644 index 000000000..b0b516e41 --- /dev/null +++ b/src/core/2/domain/tube/codac2_TubeAbstract_const.h @@ -0,0 +1,77 @@ +/** + * \file + * + * ---------------------------------------------------------------------------- + * \date 2022 + * \author Simon Rohou + * \copyright Copyright 2022 Codac Team + * \license This program is distributed under the terms of + * the GNU Lesser General Public License (LGPL). + */ + +#ifndef __CODAC2_TUBEVECTOR_CONST_H__ +#define __CODAC2_TUBEVECTOR_CONST_H__ + +#include +#include + +#include "codac_Interval.h" +#include "codac_IntervalVector.h" +#include "codac_TrajectoryVector.h" + +namespace codac2 +{ + using codac::Tube; + using codac::Trajectory; + using codac::TrajectoryVector; + using codac::Interval; + using codac::IntervalVector; + + template + class TubeAbstract_const + { + public: + + TubeAbstract_const() + { + + } + + explicit TubeAbstract_const(const T& x); + + virtual size_t size() const = 0; + virtual bool contains(const V& value) const = 0; + virtual Interval t0_tf() const = 0; + virtual I codomain() const = 0; + virtual I operator()(double t) const = 0; + virtual I operator()(const Interval& t) const = 0; + + //TubeVectorComponent operator[](size_t index); + //const TubeVectorComponent operator[](size_t index) const; + + //friend std::ostream& operator<<(std::ostream& os, const TubeVector_& x); + void print(std::ostream& os) const + { + os << t0_tf() + << "↦" << codomain() + << std::flush; + } + }; + + class Tube_const : public TubeAbstract_const + { + + }; + + class TubeVector_const : public TubeAbstract_const + { + + }; + + /*class TubeMatrix_const : public TubeAbstract_const + { + + };*/ +} // namespace codac + +#endif \ No newline at end of file diff --git a/src/core/2/domain/tube/codac2_TubeVector.cpp b/src/core/2/domain/tube/codac2_TubeVector.cpp new file mode 100644 index 000000000..d9e91db4d --- /dev/null +++ b/src/core/2/domain/tube/codac2_TubeVector.cpp @@ -0,0 +1,126 @@ +/** + * \file + * + * ---------------------------------------------------------------------------- + * \date 2022 + * \author Simon Rohou + * \copyright Copyright 2022 Codac Team + * \license This program is distributed under the terms of + * the GNU Lesser General Public License (LGPL). + */ + +#include +#include "codac2_TubeVector.h" +#include "codac2_SliceVector.h" +#include "codac2_TubeVectorComponent.h" + +using namespace std; + +namespace codac2 +{ + TubeVector::TubeVector(size_t n, TDomain& tdomain) : + _tdomain(tdomain) + { + for(list>::iterator it = _tdomain._tslices.begin(); + it != _tdomain._tslices.end(); ++it) + it->get()->add_slice(make_shared(n, *this, it)); + } + + size_t TubeVector::size() const + { + return first_slice()->size(); + } + + size_t TubeVector::nb_slices() const + { + return _tdomain.nb_tslices(); + } + + const std::shared_ptr& TubeVector::first_slice() const + { + return _tdomain.tslices().front()->slices().at(this); + } + + std::shared_ptr& TubeVector::first_slice() + { + return const_cast&>( + static_cast(*this).first_slice()); + } + + const std::shared_ptr& TubeVector::last_slice() const + { + return _tdomain.tslices().back()->slices().at(this); + } + + std::shared_ptr& TubeVector::last_slice() + { + return const_cast&>( + static_cast(*this).last_slice()); + } + + bool TubeVector::contains(const TrajectoryVector& value) const + { + return true; + } + + TDomain& TubeVector::tdomain() const + { + return _tdomain; + } + + Interval TubeVector::t0_tf() const + { + return _tdomain.t0_tf(); + } + + IntervalVector TubeVector::codomain() const + { + IntervalVector codomain(size()); + codomain.set_empty(); + for(const auto& s : *this) + codomain |= s.codomain(); + return codomain; + } + + IntervalVector TubeVector::operator()(double t) const + { + return codomain(); + } + + IntervalVector TubeVector::operator()(const Interval& t) const + { + return codomain(); + } + + void TubeVector::set(const IntervalVector& codomain) + { + assert((size_t)codomain.size() == size()); + for(auto& s : *this) + s.set(codomain); + } + + /*TubeVectorComponent TubeVector::operator[](size_t index) + { + assert(index >= 0 && index < size()); + return TubeVectorComponent(*this, index); + } + + const TubeVectorComponent TubeVector::operator[](size_t index) const + { +// assert(index >= 0 && index < size()); +//return const_cast(static_cast(*this).operator[](index)); + + assert(index >= 0 && index < size()); + return TubeVectorComponent(*this, index); + }*/ + + ostream& operator<<(ostream& os, const TubeVector& x) + { + x.TubeVector_const::print(os); + // Adding information related to sliced structure + os << ", " << x.nb_slices() + << " slice" << (x.nb_slices() > 1 ? "s" : "") + << flush; + return os; + } +} // namespace codac \ No newline at end of file diff --git a/src/core/2/domain/tube/codac2_TubeVector.h b/src/core/2/domain/tube/codac2_TubeVector.h new file mode 100644 index 000000000..92f1c02e6 --- /dev/null +++ b/src/core/2/domain/tube/codac2_TubeVector.h @@ -0,0 +1,158 @@ +/** + * \file + * + * ---------------------------------------------------------------------------- + * \date 2022 + * \author Simon Rohou + * \copyright Copyright 2022 Codac Team + * \license This program is distributed under the terms of + * the GNU Lesser General Public License (LGPL). + */ + +#ifndef __CODAC2_TUBEVECTOR_H__ +#define __CODAC2_TUBEVECTOR_H__ + +#include +#include +#include "codac2_TDomain.h" +#include "codac2_SliceVector.h" + +#include "codac2_TubeAbstract_const.h" + +namespace codac2 +{ + class TDomain; + //class SliceVector; + + class TubeVector : public TubeVector_const + { + public: + + explicit TubeVector(size_t n, TDomain& tdomain); + size_t size() const; + + size_t nb_slices() const; + const std::shared_ptr& first_slice() const; + std::shared_ptr& first_slice(); + const std::shared_ptr& last_slice() const; + std::shared_ptr& last_slice(); + + //TubeVectorComponent operator[](size_t index); + //const TubeVectorComponent operator[](size_t index) const; + + bool contains(const TrajectoryVector& value) const; + + TDomain& tdomain() const; + Interval t0_tf() const; + IntervalVector codomain() const; + IntervalVector operator()(double t) const; + IntervalVector operator()(const Interval& t) const; + void set(const IntervalVector& codomain); + + friend std::ostream& operator<<(std::ostream& os, const TubeVector& x); + + + protected: + + //friend class TubeVectorComponent; + TDomain& _tdomain; + + + public: + + struct Iterator + { + using iterator_category = std::bidirectional_iterator_tag; + using difference_type = std::ptrdiff_t; + using value_type = SliceVector; + using pointer = std::shared_ptr; + using reference = SliceVector&; + + public: + + Iterator(pointer ptr) : m_ptr(ptr) { } + + reference operator*() const + { + return *m_ptr; + } + + pointer operator->() + { + return m_ptr; + } + + Iterator& operator++() + { + if(m_ptr != nullptr) + m_ptr = m_ptr->next_slice(); + return *this; + } + + friend bool operator==(const Iterator& a, const Iterator& b) + { + return a.m_ptr == b.m_ptr; + }; + + friend bool operator!=(const Iterator& a, const Iterator& b) + { + return a.m_ptr != b.m_ptr; + }; + + private: + + pointer m_ptr; + }; + + Iterator begin() { return Iterator(first_slice()); } + Iterator end() { return Iterator(last_slice()); } + + struct ConstIterator + { + using iterator_category = std::bidirectional_iterator_tag; + using difference_type = std::ptrdiff_t; + using value_type = SliceVector; + using pointer = const std::shared_ptr; + using reference = const SliceVector&; + + public: + + ConstIterator(pointer ptr) : m_ptr(ptr) { } + + reference operator*() const + { + return *m_ptr; + } + + pointer operator->() + { + return m_ptr; + } + + const ConstIterator& operator++() + { + m_ptr = m_ptr->next_slice(); + return *this; + } + + friend bool operator==(const ConstIterator& a, const ConstIterator& b) + { + return a.m_ptr == b.m_ptr; + }; + + friend bool operator!=(const ConstIterator& a, const ConstIterator& b) + { + return a.m_ptr != b.m_ptr; + }; + + private: + + std::shared_ptr m_ptr; + }; + + ConstIterator begin() const { return ConstIterator(first_slice()); } + ConstIterator end() const { return ConstIterator(last_slice()); } + }; +} // namespace codac + +#endif \ No newline at end of file diff --git a/src/core/2/domain/tube/codac2_TubeVectorComponent.cpp b/src/core/2/domain/tube/codac2_TubeVectorComponent.cpp new file mode 100644 index 000000000..1f7074099 --- /dev/null +++ b/src/core/2/domain/tube/codac2_TubeVectorComponent.cpp @@ -0,0 +1,43 @@ +/** + * \file + * + * ---------------------------------------------------------------------------- + * \date 2022 + * \author Simon Rohou + * \copyright Copyright 2022 Codac Team + * \license This program is distributed under the terms of + * the GNU Lesser General Public License (LGPL). + */ + +#include "codac2_TubeVectorComponent.h" +#include "codac2_TubeVector.h" +#include "codac2_SliceVector.h" + +using namespace std; + +namespace codac2 +{ + /*const Interval_& TubeVectorComponent::tdomain() const + { + return m_tubevector.tdomain(); + } + + bool TubeVectorComponent::contains(const Trajectory_& value) const + { + return true; + } + + TubeVectorComponent::TubeVectorComponent(T_TubeVector& tubevector, size_t i) : + m_i(i), m_tubevector(tubevector) + { + assert(i >= 0 && i < tubevector.size()); + + } + + void TubeVectorComponent::set_codomain(const Interval_& codomain) + { + for(auto& s : m_tubevector.m_slices) + s->m_codomain[m_i] = codomain; + }*/ + +} // namespace codac \ No newline at end of file diff --git a/src/core/2/domain/tube/codac2_TubeVectorComponent.h b/src/core/2/domain/tube/codac2_TubeVectorComponent.h new file mode 100644 index 000000000..1e9fe9ded --- /dev/null +++ b/src/core/2/domain/tube/codac2_TubeVectorComponent.h @@ -0,0 +1,46 @@ +/** + * \file + * + * ---------------------------------------------------------------------------- + * \date 2022 + * \author Simon Rohou + * \copyright Copyright 2022 Codac Team + * \license This program is distributed under the terms of + * the GNU Lesser General Public License (LGPL). + */ + +#ifndef __CODAC2_TUBEVECTORCOMPONENT_H__ +#define __CODAC2_TUBEVECTORCOMPONENT_H__ + +#include +#include "codac_Interval.h" +#include "codac2_TubeVector.h" + +namespace codac2 +{ + class TubeVector_; + + /*template + class TubeVectorComponent// : public DomainItem_, public TemporalItem_ + { + public: + + const Interval_& tdomain() const; + + bool contains(const Trajectory_& value) const; + + void set_codomain(const Interval_& codomain); + + + protected: + + friend class TubeVector_; + explicit TubeVectorComponent(T_TubeVector& tubevector, size_t i); + + size_t m_i; + T_TubeVector& m_tubevector; + + };*/ +} // namespace codac + +#endif \ No newline at end of file diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 948371585..99e094a93 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -153,6 +153,20 @@ ${CMAKE_CURRENT_SOURCE_DIR}/tools/codac_Eigen.h ${CMAKE_CURRENT_SOURCE_DIR}/sivia/codac_sivia.cpp ${CMAKE_CURRENT_SOURCE_DIR}/sivia/codac_sivia.h + + # Files related to codac2 + ${CMAKE_CURRENT_SOURCE_DIR}/2/domain/tube/codac2_SliceVector.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/2/domain/tube/codac2_SliceVector.h + ${CMAKE_CURRENT_SOURCE_DIR}/2/domain/tube/codac2_TSlice.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/2/domain/tube/codac2_TSlice.h + ${CMAKE_CURRENT_SOURCE_DIR}/2/domain/tube/codac2_TDomain.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/2/domain/tube/codac2_TDomain.h + ${CMAKE_CURRENT_SOURCE_DIR}/2/domain/tube/codac2_TubeAbstract_const.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/2/domain/tube/codac2_TubeAbstract_const.h + ${CMAKE_CURRENT_SOURCE_DIR}/2/domain/tube/codac2_TubeVector.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/2/domain/tube/codac2_TubeVector.h + ${CMAKE_CURRENT_SOURCE_DIR}/2/domain/tube/codac2_TubeVectorComponent.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/2/domain/tube/codac2_TubeVectorComponent.h ) From d2ed825cecfeadeef8ba182c38bb1196d076bade Mon Sep 17 00:00:00 2001 From: SimonRohou Date: Tue, 24 May 2022 15:48:48 +0200 Subject: [PATCH 002/256] [tube] const_pointer_cast for prev/next slices --- src/core/2/domain/tube/codac2_SliceVector.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/core/2/domain/tube/codac2_SliceVector.cpp b/src/core/2/domain/tube/codac2_SliceVector.cpp index 30912bbc2..88dd8a092 100644 --- a/src/core/2/domain/tube/codac2_SliceVector.cpp +++ b/src/core/2/domain/tube/codac2_SliceVector.cpp @@ -57,9 +57,8 @@ namespace codac2 shared_ptr SliceVector::prev_slice() { - if(_tube_vector.first_slice().get() == this) - return shared_ptr(nullptr); - return prev(_it_tslice)->get()->slices().at(&_tube_vector); + return const_pointer_cast( + static_cast(*this).prev_slice()); } shared_ptr SliceVector::next_slice() const @@ -71,9 +70,8 @@ namespace codac2 shared_ptr SliceVector::next_slice() { - if(_tube_vector.last_slice().get() == this) - return shared_ptr(nullptr); - return next(_it_tslice)->get()->slices().at(&_tube_vector); + return const_pointer_cast( + static_cast(*this).next_slice()); } const IntervalVector& SliceVector::codomain() const From 3cfc1fc2f884c4df054de735f8ebc54540151db2 Mon Sep 17 00:00:00 2001 From: SimonRohou Date: Tue, 24 May 2022 16:57:09 +0200 Subject: [PATCH 003/256] [tube] using iterator from list --- src/core/2/domain/tube/codac2_TDomain.cpp | 7 +- src/core/2/domain/tube/codac2_TSlice.h | 1 + src/core/2/domain/tube/codac2_TubeVector.h | 92 +++++++--------------- 3 files changed, 31 insertions(+), 69 deletions(-) diff --git a/src/core/2/domain/tube/codac2_TDomain.cpp b/src/core/2/domain/tube/codac2_TDomain.cpp index 25238245e..66ebbfc33 100644 --- a/src/core/2/domain/tube/codac2_TDomain.cpp +++ b/src/core/2/domain/tube/codac2_TDomain.cpp @@ -31,20 +31,17 @@ namespace codac2 if(with_gates) _tslices.push_back(make_shared(Interval(t0_tf.ub()))); _tslices.push_back(make_shared(Interval(t0_tf.ub(),oo))); - - // End of container: - _tslices.push_back(make_shared(Interval(oo,oo))); } const Interval TDomain::t0_tf() const { return Interval(_tslices.front()->tdomain().ub(), - prev(prev(_tslices.end()))->get()->tdomain().lb()); + prev(_tslices.end())->get()->tdomain().lb()); } size_t TDomain::nb_tslices() const { - return _tslices.size() - 1; + return _tslices.size(); } const list> TDomain::tslices() const diff --git a/src/core/2/domain/tube/codac2_TSlice.h b/src/core/2/domain/tube/codac2_TSlice.h index d857b605b..7f49ffc19 100644 --- a/src/core/2/domain/tube/codac2_TSlice.h +++ b/src/core/2/domain/tube/codac2_TSlice.h @@ -38,6 +38,7 @@ namespace codac2 protected: + friend class TubeVector; const Interval _tdomain; std::map> _slices; }; diff --git a/src/core/2/domain/tube/codac2_TubeVector.h b/src/core/2/domain/tube/codac2_TubeVector.h index 92f1c02e6..02c591361 100644 --- a/src/core/2/domain/tube/codac2_TubeVector.h +++ b/src/core/2/domain/tube/codac2_TubeVector.h @@ -60,98 +60,62 @@ namespace codac2 public: - struct Iterator + using base_container = std::list>; + + struct iterator : public base_container::iterator { - using iterator_category = std::bidirectional_iterator_tag; - using difference_type = std::ptrdiff_t; + using iterator_category = typename base_container::iterator::iterator_category; + using difference_type = typename base_container::iterator::difference_type; + using value_type = SliceVector; using pointer = std::shared_ptr; using reference = SliceVector&; public: + + iterator(const TubeVector& tube_vector, base_container::iterator it) : + base_container::iterator(it), _tube_vector(tube_vector) { } - Iterator(pointer ptr) : m_ptr(ptr) { } - - reference operator*() const - { - return *m_ptr; - } - - pointer operator->() + reference operator*() { - return m_ptr; + return *((*this)->get()->_slices.at(&_tube_vector)); } - Iterator& operator++() - { - if(m_ptr != nullptr) - m_ptr = m_ptr->next_slice(); - return *this; - } - - friend bool operator==(const Iterator& a, const Iterator& b) - { - return a.m_ptr == b.m_ptr; - }; - - friend bool operator!=(const Iterator& a, const Iterator& b) - { - return a.m_ptr != b.m_ptr; - }; - - private: + protected: - pointer m_ptr; + const TubeVector& _tube_vector; }; - Iterator begin() { return Iterator(first_slice()); } - Iterator end() { return Iterator(last_slice()); } + iterator begin() { return iterator(*this, _tdomain._tslices.begin()); } + iterator end() { return iterator(*this, _tdomain._tslices.end()); } + - struct ConstIterator + struct const_iterator : public base_container::const_iterator { - using iterator_category = std::bidirectional_iterator_tag; - using difference_type = std::ptrdiff_t; + using iterator_category = typename base_container::const_iterator::iterator_category; + using difference_type = typename base_container::const_iterator::difference_type; + using value_type = SliceVector; using pointer = const std::shared_ptr; using reference = const SliceVector&; public: - - ConstIterator(pointer ptr) : m_ptr(ptr) { } + + const_iterator(const TubeVector& tube_vector, base_container::const_iterator it) : + base_container::const_iterator(it), _tube_vector(tube_vector) { } reference operator*() const { - return *m_ptr; - } - - pointer operator->() - { - return m_ptr; + return *((*this)->get()->_slices.at(&_tube_vector)); } - const ConstIterator& operator++() - { - m_ptr = m_ptr->next_slice(); - return *this; - } - - friend bool operator==(const ConstIterator& a, const ConstIterator& b) - { - return a.m_ptr == b.m_ptr; - }; - - friend bool operator!=(const ConstIterator& a, const ConstIterator& b) - { - return a.m_ptr != b.m_ptr; - }; - - private: + protected: - std::shared_ptr m_ptr; + const TubeVector& _tube_vector; }; - ConstIterator begin() const { return ConstIterator(first_slice()); } - ConstIterator end() const { return ConstIterator(last_slice()); } + const_iterator begin() const { return const_iterator(*this, _tdomain._tslices.cbegin()); } + const_iterator end() const { return const_iterator(*this, _tdomain._tslices.cend()); } }; } // namespace codac From f78e8c283caf6f28b3e55517a85d17d3d6b8594f Mon Sep 17 00:00:00 2001 From: SimonRohou Date: Wed, 25 May 2022 17:10:37 +0200 Subject: [PATCH 004/256] [tube] sampling of TDomain --- src/core/2/domain/tube/codac2_SliceVector.cpp | 6 +++ src/core/2/domain/tube/codac2_SliceVector.h | 1 + src/core/2/domain/tube/codac2_TDomain.cpp | 51 +++++++++++++++---- src/core/2/domain/tube/codac2_TDomain.h | 3 ++ src/core/2/domain/tube/codac2_TSlice.cpp | 15 +++++- src/core/2/domain/tube/codac2_TSlice.h | 8 ++- 6 files changed, 71 insertions(+), 13 deletions(-) diff --git a/src/core/2/domain/tube/codac2_SliceVector.cpp b/src/core/2/domain/tube/codac2_SliceVector.cpp index 88dd8a092..1d1cef0f3 100644 --- a/src/core/2/domain/tube/codac2_SliceVector.cpp +++ b/src/core/2/domain/tube/codac2_SliceVector.cpp @@ -23,6 +23,12 @@ namespace codac2 } + SliceVector::SliceVector(const SliceVector& s) : + _tube_vector(s._tube_vector), _it_tslice(s._it_tslice), _codomain(s.size()) + { + + } + SliceVector::~SliceVector() { diff --git a/src/core/2/domain/tube/codac2_SliceVector.h b/src/core/2/domain/tube/codac2_SliceVector.h index 842875661..2a37ac16a 100644 --- a/src/core/2/domain/tube/codac2_SliceVector.h +++ b/src/core/2/domain/tube/codac2_SliceVector.h @@ -33,6 +33,7 @@ namespace codac2 public: explicit SliceVector(size_t n, const TubeVector& tube_vector, std::list>::iterator it_tslice); + SliceVector(const SliceVector& s); ~SliceVector(); const TubeVector& tube_vector() const; diff --git a/src/core/2/domain/tube/codac2_TDomain.cpp b/src/core/2/domain/tube/codac2_TDomain.cpp index 66ebbfc33..88271b0b6 100644 --- a/src/core/2/domain/tube/codac2_TDomain.cpp +++ b/src/core/2/domain/tube/codac2_TDomain.cpp @@ -9,7 +9,9 @@ * the GNU Lesser General Public License (LGPL). */ +#include #include "codac2_TDomain.h" +#include "codac2_TubeVector.h" #include "codac_predef_values.h" using namespace std; @@ -19,18 +21,15 @@ namespace codac2 { TDomain::TDomain(const Interval& t0_tf, double dt, bool with_gates) { - _tslices.push_back(make_shared(Interval(-oo,t0_tf.lb()))); - - for(double t = t0_tf.lb() ; t < t0_tf.ub() ; t+=dt) + _tslices.push_back(make_shared(Interval(-oo,oo))); + for(double t = t0_tf.lb() ; t < t0_tf.ub()+dt ; t+=dt) { + double t_ = min(t, t0_tf.ub()); + + sample(t_); if(with_gates) - _tslices.push_back(make_shared(Interval(t))); - _tslices.push_back(make_shared(Interval(t,std::min(t0_tf.ub(),t+dt)))); + sample(t_); // second sampling } - - if(with_gates) - _tslices.push_back(make_shared(Interval(t0_tf.ub()))); - _tslices.push_back(make_shared(Interval(t0_tf.ub(),oo))); } const Interval TDomain::t0_tf() const @@ -44,7 +43,37 @@ namespace codac2 return _tslices.size(); } - const list> TDomain::tslices() const + size_t TDomain::nb_tubes() const + { + return _tslices.front().get()->_slices.size(); + } + + list>::iterator TDomain::iterator_tslice(double t) + { + list>::iterator it; + for(it = _tslices.begin(); it != _tslices.end(); ++it) + if(it->get()->tdomain().contains(t)) return it; + return _tslices.end(); + } + + void TDomain::sample(double t) + { + std::list>::iterator it = iterator_tslice(t); + assert(it != _tslices.end()); + const Interval tdomain = it->get()->tdomain(); + assert(tdomain.contains(t)); + + it->get()->set_tdomain(Interval(tdomain.lb(), t)); + ++it; + + if(t == tdomain.ub()) + assert(!it->get()->tdomain().is_degenerated() && + "degenerated tslice (gate) already existing at this time"); + + _tslices.insert(it, make_shared(Interval(t, tdomain.ub()))); + } + + const list> TDomain::tslices() const { return _tslices; } @@ -54,6 +83,8 @@ namespace codac2 os << x.t0_tf() << ", " << x.nb_tslices() << " slice" << (x.nb_tslices() > 1 ? "s" : "") + << ", " << x.nb_tubes() + << " tube" << (x.nb_tubes() > 1 ? "s" : "") << flush; return os; } diff --git a/src/core/2/domain/tube/codac2_TDomain.h b/src/core/2/domain/tube/codac2_TDomain.h index b5e34e0c7..9401fa175 100644 --- a/src/core/2/domain/tube/codac2_TDomain.h +++ b/src/core/2/domain/tube/codac2_TDomain.h @@ -30,7 +30,10 @@ namespace codac2 explicit TDomain(const Interval& t0_tf, double dt, bool with_gates = false); const Interval t0_tf() const; + std::list>::iterator iterator_tslice(double t); size_t nb_tslices() const; + size_t nb_tubes() const; + void sample(double t); friend std::ostream& operator<<(std::ostream& os, const TDomain& x); diff --git a/src/core/2/domain/tube/codac2_TSlice.cpp b/src/core/2/domain/tube/codac2_TSlice.cpp index d4f096d93..bd5121402 100644 --- a/src/core/2/domain/tube/codac2_TSlice.cpp +++ b/src/core/2/domain/tube/codac2_TSlice.cpp @@ -17,15 +17,28 @@ using namespace codac; namespace codac2 { - TSlice::TSlice(const Interval& t0_tf) : _tdomain(t0_tf) + TSlice::TSlice(const Interval& tdomain) { + set_tdomain(tdomain); + } + TSlice::TSlice(const TSlice& tslice, const Interval& tdomain) + { + set_tdomain(tdomain); + for(const auto& [tv,s] : tslice.slices()) + add_slice(make_shared(*s.get())); } const Interval& TSlice::tdomain() const { return _tdomain; } + + void TSlice::set_tdomain(const Interval& tdomain) + { + assert(!tdomain.is_empty()); + _tdomain = tdomain; + } const map> TSlice::slices() const { diff --git a/src/core/2/domain/tube/codac2_TSlice.h b/src/core/2/domain/tube/codac2_TSlice.h index 7f49ffc19..69ef52c9e 100644 --- a/src/core/2/domain/tube/codac2_TSlice.h +++ b/src/core/2/domain/tube/codac2_TSlice.h @@ -30,7 +30,8 @@ namespace codac2 { public: - explicit TSlice(const Interval& t0_tf); + explicit TSlice(const Interval& tdomain); + TSlice(const TSlice& tslice, const Interval& tdomain); // performs a deep copy on slices const Interval& tdomain() const; const std::map> slices() const; void add_slice(const std::shared_ptr& slice); @@ -38,8 +39,11 @@ namespace codac2 protected: + void set_tdomain(const Interval& tdomain); + friend class TubeVector; - const Interval _tdomain; + friend class TDomain; + Interval _tdomain; std::map> _slices; }; } // namespace codac From 9d2a8bf8ce66330938a32436d819462a1ddc235c Mon Sep 17 00:00:00 2001 From: SimonRohou Date: Thu, 26 May 2022 16:37:16 +0200 Subject: [PATCH 005/256] [tube] simplifying pointers --- src/core/2/domain/tube/codac2_SliceVector.cpp | 32 +++++++++---------- src/core/2/domain/tube/codac2_SliceVector.h | 12 +++---- src/core/2/domain/tube/codac2_TDomain.cpp | 26 +++++++-------- src/core/2/domain/tube/codac2_TDomain.h | 6 ++-- src/core/2/domain/tube/codac2_TSlice.cpp | 8 ++--- src/core/2/domain/tube/codac2_TSlice.h | 6 ++-- src/core/2/domain/tube/codac2_TubeVector.cpp | 22 ++++++------- src/core/2/domain/tube/codac2_TubeVector.h | 18 +++++------ 8 files changed, 65 insertions(+), 65 deletions(-) diff --git a/src/core/2/domain/tube/codac2_SliceVector.cpp b/src/core/2/domain/tube/codac2_SliceVector.cpp index 1d1cef0f3..8a6929009 100644 --- a/src/core/2/domain/tube/codac2_SliceVector.cpp +++ b/src/core/2/domain/tube/codac2_SliceVector.cpp @@ -16,7 +16,7 @@ using namespace std; namespace codac2 { - SliceVector::SliceVector(size_t n, const TubeVector& tube_vector, list>::iterator it_tslice) : + SliceVector::SliceVector(size_t n, const TubeVector& tube_vector, list::iterator it_tslice) : _tube_vector(tube_vector), _it_tslice(it_tslice), _codomain(IntervalVector(n)) { @@ -51,32 +51,32 @@ namespace codac2 const Interval& SliceVector::tdomain() const { - return _it_tslice->get()->tdomain(); + return _it_tslice->tdomain(); } - shared_ptr SliceVector::prev_slice() const + const SliceVector* SliceVector::prev_slice() const { - if(_tube_vector.first_slice().get() == this) - return shared_ptr(nullptr); - return prev(_it_tslice)->get()->slices().at(&_tube_vector); + if(&_tube_vector.first_slice() == this) + return nullptr; + return &prev(_it_tslice)->slices().at(&_tube_vector); } - shared_ptr SliceVector::prev_slice() + SliceVector* SliceVector::prev_slice() { - return const_pointer_cast( + return const_cast( static_cast(*this).prev_slice()); } - shared_ptr SliceVector::next_slice() const + const SliceVector* SliceVector::next_slice() const { - if(_tube_vector.last_slice().get() == this) - return shared_ptr(nullptr); - return next(_it_tslice)->get()->slices().at(&_tube_vector); + if(&_tube_vector.last_slice() == this) + return nullptr; + return &next(_it_tslice)->slices().at(&_tube_vector); } - shared_ptr SliceVector::next_slice() + SliceVector* SliceVector::next_slice() { - return const_pointer_cast( + return const_cast( static_cast(*this).next_slice()); } @@ -89,7 +89,7 @@ namespace codac2 { IntervalVector gate = codomain(); if(prev_slice()) - gate &= prev_slice().get()->codomain(); + gate &= prev_slice()->codomain(); return gate; } @@ -97,7 +97,7 @@ namespace codac2 { IntervalVector gate = codomain(); if(next_slice()) - gate &= next_slice().get()->codomain(); + gate &= next_slice()->codomain(); return gate; } diff --git a/src/core/2/domain/tube/codac2_SliceVector.h b/src/core/2/domain/tube/codac2_SliceVector.h index 2a37ac16a..a9dcdc1ae 100644 --- a/src/core/2/domain/tube/codac2_SliceVector.h +++ b/src/core/2/domain/tube/codac2_SliceVector.h @@ -32,7 +32,7 @@ namespace codac2 { public: - explicit SliceVector(size_t n, const TubeVector& tube_vector, std::list>::iterator it_tslice); + explicit SliceVector(size_t n, const TubeVector& tube_vector, std::list::iterator it_tslice); SliceVector(const SliceVector& s); ~SliceVector(); @@ -48,10 +48,10 @@ namespace codac2 bool contains(const TrajectoryVector& value) const; - std::shared_ptr prev_slice() const; - std::shared_ptr prev_slice(); - std::shared_ptr next_slice() const; - std::shared_ptr next_slice(); + const SliceVector* prev_slice() const; + SliceVector* prev_slice(); + const SliceVector* next_slice() const; + SliceVector* next_slice(); const Interval& tdomain() const; @@ -70,7 +70,7 @@ namespace codac2 //friend class TubeVectorComponent; const TubeVector& _tube_vector; - std::list>::iterator _it_tslice; + std::list::iterator _it_tslice; IntervalVector _codomain; }; } // namespace codac diff --git a/src/core/2/domain/tube/codac2_TDomain.cpp b/src/core/2/domain/tube/codac2_TDomain.cpp index 88271b0b6..6d4993be2 100644 --- a/src/core/2/domain/tube/codac2_TDomain.cpp +++ b/src/core/2/domain/tube/codac2_TDomain.cpp @@ -21,7 +21,7 @@ namespace codac2 { TDomain::TDomain(const Interval& t0_tf, double dt, bool with_gates) { - _tslices.push_back(make_shared(Interval(-oo,oo))); + _tslices.push_back(TSlice(Interval(-oo,oo))); for(double t = t0_tf.lb() ; t < t0_tf.ub()+dt ; t+=dt) { double t_ = min(t, t0_tf.ub()); @@ -34,8 +34,8 @@ namespace codac2 const Interval TDomain::t0_tf() const { - return Interval(_tslices.front()->tdomain().ub(), - prev(_tslices.end())->get()->tdomain().lb()); + return Interval(_tslices.front().tdomain().ub(), + prev(_tslices.end())->tdomain().lb()); } size_t TDomain::nb_tslices() const @@ -45,35 +45,35 @@ namespace codac2 size_t TDomain::nb_tubes() const { - return _tslices.front().get()->_slices.size(); + return _tslices.front()._slices.size(); } - list>::iterator TDomain::iterator_tslice(double t) + list::iterator TDomain::iterator_tslice(double t) { - list>::iterator it; + list::iterator it; for(it = _tslices.begin(); it != _tslices.end(); ++it) - if(it->get()->tdomain().contains(t)) return it; + if(it->tdomain().contains(t)) return it; return _tslices.end(); } void TDomain::sample(double t) { - std::list>::iterator it = iterator_tslice(t); + std::list::iterator it = iterator_tslice(t); assert(it != _tslices.end()); - const Interval tdomain = it->get()->tdomain(); + const Interval tdomain = it->tdomain(); assert(tdomain.contains(t)); - it->get()->set_tdomain(Interval(tdomain.lb(), t)); + it->set_tdomain(Interval(tdomain.lb(), t)); ++it; if(t == tdomain.ub()) - assert(!it->get()->tdomain().is_degenerated() && + assert(!it->tdomain().is_degenerated() && "degenerated tslice (gate) already existing at this time"); - _tslices.insert(it, make_shared(Interval(t, tdomain.ub()))); + _tslices.insert(it, TSlice(Interval(t, tdomain.ub()))); } - const list> TDomain::tslices() const + const list& TDomain::tslices() const { return _tslices; } diff --git a/src/core/2/domain/tube/codac2_TDomain.h b/src/core/2/domain/tube/codac2_TDomain.h index 9401fa175..f6eacdfec 100644 --- a/src/core/2/domain/tube/codac2_TDomain.h +++ b/src/core/2/domain/tube/codac2_TDomain.h @@ -30,7 +30,7 @@ namespace codac2 explicit TDomain(const Interval& t0_tf, double dt, bool with_gates = false); const Interval t0_tf() const; - std::list>::iterator iterator_tslice(double t); + std::list::iterator iterator_tslice(double t); size_t nb_tslices() const; size_t nb_tubes() const; void sample(double t); @@ -39,11 +39,11 @@ namespace codac2 protected: - const std::list> tslices() const; + const std::list& tslices() const; friend class SliceVector; friend class TubeVector; - std::list> _tslices; + std::list _tslices; }; } // namespace codac diff --git a/src/core/2/domain/tube/codac2_TSlice.cpp b/src/core/2/domain/tube/codac2_TSlice.cpp index bd5121402..c3872f9cd 100644 --- a/src/core/2/domain/tube/codac2_TSlice.cpp +++ b/src/core/2/domain/tube/codac2_TSlice.cpp @@ -26,7 +26,7 @@ namespace codac2 { set_tdomain(tdomain); for(const auto& [tv,s] : tslice.slices()) - add_slice(make_shared(*s.get())); + add_slice(s); } const Interval& TSlice::tdomain() const @@ -40,15 +40,15 @@ namespace codac2 _tdomain = tdomain; } - const map> TSlice::slices() const + const map& TSlice::slices() const { return _slices; } - void TSlice::add_slice(const std::shared_ptr& slice) + void TSlice::add_slice(const SliceVector& slice) { _slices.insert( - pair>(&slice.get()->tube_vector(), slice)); + pair(&slice.tube_vector(), slice)); } ostream& operator<<(ostream& os, const TSlice& x) diff --git a/src/core/2/domain/tube/codac2_TSlice.h b/src/core/2/domain/tube/codac2_TSlice.h index 69ef52c9e..8311c7b2e 100644 --- a/src/core/2/domain/tube/codac2_TSlice.h +++ b/src/core/2/domain/tube/codac2_TSlice.h @@ -33,8 +33,8 @@ namespace codac2 explicit TSlice(const Interval& tdomain); TSlice(const TSlice& tslice, const Interval& tdomain); // performs a deep copy on slices const Interval& tdomain() const; - const std::map> slices() const; - void add_slice(const std::shared_ptr& slice); + const std::map& slices() const; + void add_slice(const SliceVector& slice); friend std::ostream& operator<<(std::ostream& os, const TSlice& x); protected: @@ -44,7 +44,7 @@ namespace codac2 friend class TubeVector; friend class TDomain; Interval _tdomain; - std::map> _slices; + std::map _slices; }; } // namespace codac diff --git a/src/core/2/domain/tube/codac2_TubeVector.cpp b/src/core/2/domain/tube/codac2_TubeVector.cpp index d9e91db4d..90de4cb89 100644 --- a/src/core/2/domain/tube/codac2_TubeVector.cpp +++ b/src/core/2/domain/tube/codac2_TubeVector.cpp @@ -21,14 +21,14 @@ namespace codac2 TubeVector::TubeVector(size_t n, TDomain& tdomain) : _tdomain(tdomain) { - for(list>::iterator it = _tdomain._tslices.begin(); + for(list::iterator it = _tdomain._tslices.begin(); it != _tdomain._tslices.end(); ++it) - it->get()->add_slice(make_shared(n, *this, it)); + it->add_slice(SliceVector(n, *this, it)); } size_t TubeVector::size() const { - return first_slice()->size(); + return first_slice().size(); } size_t TubeVector::nb_slices() const @@ -36,25 +36,25 @@ namespace codac2 return _tdomain.nb_tslices(); } - const std::shared_ptr& TubeVector::first_slice() const + const SliceVector& TubeVector::first_slice() const { - return _tdomain.tslices().front()->slices().at(this); + return _tdomain.tslices().front().slices().at(this); } - std::shared_ptr& TubeVector::first_slice() + SliceVector& TubeVector::first_slice() { - return const_cast&>( + return const_cast( static_cast(*this).first_slice()); } - const std::shared_ptr& TubeVector::last_slice() const + const SliceVector& TubeVector::last_slice() const { - return _tdomain.tslices().back()->slices().at(this); + return _tdomain.tslices().back().slices().at(this); } - std::shared_ptr& TubeVector::last_slice() + SliceVector& TubeVector::last_slice() { - return const_cast&>( + return const_cast( static_cast(*this).last_slice()); } diff --git a/src/core/2/domain/tube/codac2_TubeVector.h b/src/core/2/domain/tube/codac2_TubeVector.h index 02c591361..b06ef15c4 100644 --- a/src/core/2/domain/tube/codac2_TubeVector.h +++ b/src/core/2/domain/tube/codac2_TubeVector.h @@ -32,10 +32,10 @@ namespace codac2 size_t size() const; size_t nb_slices() const; - const std::shared_ptr& first_slice() const; - std::shared_ptr& first_slice(); - const std::shared_ptr& last_slice() const; - std::shared_ptr& last_slice(); + const SliceVector& first_slice() const; + SliceVector& first_slice(); + const SliceVector& last_slice() const; + SliceVector& last_slice(); //TubeVectorComponent operator[](size_t index); //const TubeVectorComponent operator[](size_t index) const; @@ -60,7 +60,7 @@ namespace codac2 public: - using base_container = std::list>; + using base_container = std::list; struct iterator : public base_container::iterator { @@ -68,7 +68,7 @@ namespace codac2 using difference_type = typename base_container::iterator::difference_type; using value_type = SliceVector; - using pointer = std::shared_ptr; + using pointer = SliceVector*; using reference = SliceVector&; public: @@ -78,7 +78,7 @@ namespace codac2 reference operator*() { - return *((*this)->get()->_slices.at(&_tube_vector)); + return ((*this)->_slices.at(&_tube_vector)); } protected: @@ -96,7 +96,7 @@ namespace codac2 using difference_type = typename base_container::const_iterator::difference_type; using value_type = SliceVector; - using pointer = const std::shared_ptr; + using pointer = const SliceVector*; using reference = const SliceVector&; public: @@ -106,7 +106,7 @@ namespace codac2 reference operator*() const { - return *((*this)->get()->_slices.at(&_tube_vector)); + return ((*this)->_slices.at(&_tube_vector)); } protected: From ea7cef3042e20279744c01875536bfe6fbd198cd Mon Sep 17 00:00:00 2001 From: SimonRohou Date: Mon, 30 May 2022 13:22:48 +0200 Subject: [PATCH 006/256] [tube] new implementation --- src/core/2/domain/tube/codac2_SliceVector.cpp | 26 +++---- src/core/2/domain/tube/codac2_SliceVector.h | 15 ++-- src/core/2/domain/tube/codac2_TDomain.cpp | 38 +++++++--- src/core/2/domain/tube/codac2_TDomain.h | 4 +- src/core/2/domain/tube/codac2_TSlice.cpp | 27 +++---- src/core/2/domain/tube/codac2_TSlice.h | 7 +- .../2/domain/tube/codac2_TubeAbstract_const.h | 4 +- src/core/2/domain/tube/codac2_TubeVector.cpp | 66 +++++++++++----- src/core/2/domain/tube/codac2_TubeVector.h | 21 ++++-- .../tube/codac2_TubeVectorComponent.cpp | 75 ++++++++++++++++--- .../domain/tube/codac2_TubeVectorComponent.h | 39 +++++++--- .../tube/codac2_TubeVectorEvaluation.cpp | 60 +++++++++++++++ .../domain/tube/codac2_TubeVectorEvaluation.h | 49 ++++++++++++ src/core/CMakeLists.txt | 2 + 14 files changed, 335 insertions(+), 98 deletions(-) create mode 100644 src/core/2/domain/tube/codac2_TubeVectorEvaluation.cpp create mode 100644 src/core/2/domain/tube/codac2_TubeVectorEvaluation.h diff --git a/src/core/2/domain/tube/codac2_SliceVector.cpp b/src/core/2/domain/tube/codac2_SliceVector.cpp index 8a6929009..e5bac1440 100644 --- a/src/core/2/domain/tube/codac2_SliceVector.cpp +++ b/src/core/2/domain/tube/codac2_SliceVector.cpp @@ -17,16 +17,16 @@ using namespace std; namespace codac2 { SliceVector::SliceVector(size_t n, const TubeVector& tube_vector, list::iterator it_tslice) : - _tube_vector(tube_vector), _it_tslice(it_tslice), + _tubevector(tube_vector), _it_tslice(it_tslice), _codomain(IntervalVector(n)) { } SliceVector::SliceVector(const SliceVector& s) : - _tube_vector(s._tube_vector), _it_tslice(s._it_tslice), _codomain(s.size()) + _tubevector(s._tubevector), _it_tslice(s._it_tslice), _codomain(s._codomain) { - + } SliceVector::~SliceVector() @@ -36,7 +36,7 @@ namespace codac2 const TubeVector& SliceVector::tube_vector() const { - return _tube_vector; + return _tubevector; } size_t SliceVector::size() const @@ -49,16 +49,16 @@ namespace codac2 return true; } - const Interval& SliceVector::tdomain() const + const Interval& SliceVector::t0_tf() const { - return _it_tslice->tdomain(); + return _it_tslice->t0_tf(); } const SliceVector* SliceVector::prev_slice() const { - if(&_tube_vector.first_slice() == this) + if(&_tubevector.first_slice() == this) return nullptr; - return &prev(_it_tslice)->slices().at(&_tube_vector); + return &prev(_it_tslice)->slices().at(&_tubevector); } SliceVector* SliceVector::prev_slice() @@ -69,9 +69,9 @@ namespace codac2 const SliceVector* SliceVector::next_slice() const { - if(&_tube_vector.last_slice() == this) + if(&_tubevector.last_slice() == this) return nullptr; - return &next(_it_tslice)->slices().at(&_tube_vector); + return &next(_it_tslice)->slices().at(&_tubevector); } SliceVector* SliceVector::next_slice() @@ -85,7 +85,7 @@ namespace codac2 return _codomain; } - const IntervalVector SliceVector::input_gate() const + IntervalVector SliceVector::input_gate() const { IntervalVector gate = codomain(); if(prev_slice()) @@ -93,7 +93,7 @@ namespace codac2 return gate; } - const IntervalVector SliceVector::output_gate() const + IntervalVector SliceVector::output_gate() const { IntervalVector gate = codomain(); if(next_slice()) @@ -109,7 +109,7 @@ namespace codac2 ostream& operator<<(ostream& os, const SliceVector& x) { - os << x.tdomain() + os << x.t0_tf() << "↦" << x.codomain() << flush; return os; diff --git a/src/core/2/domain/tube/codac2_SliceVector.h b/src/core/2/domain/tube/codac2_SliceVector.h index a9dcdc1ae..dee7e121c 100644 --- a/src/core/2/domain/tube/codac2_SliceVector.h +++ b/src/core/2/domain/tube/codac2_SliceVector.h @@ -33,8 +33,9 @@ namespace codac2 public: explicit SliceVector(size_t n, const TubeVector& tube_vector, std::list::iterator it_tslice); - SliceVector(const SliceVector& s); ~SliceVector(); + + SliceVector(const SliceVector& s); const TubeVector& tube_vector() const; @@ -53,11 +54,11 @@ namespace codac2 const SliceVector* next_slice() const; SliceVector* next_slice(); - const Interval& tdomain() const; + const Interval& t0_tf() const; const IntervalVector& codomain() const; - const IntervalVector input_gate() const; - const IntervalVector output_gate() const; + IntervalVector input_gate() const; + IntervalVector output_gate() const; void set(const IntervalVector& codomain); @@ -67,9 +68,11 @@ namespace codac2 protected: friend class TubeVector; - //friend class TubeVectorComponent; + friend class TubeVectorComponent; + friend class TDomain; + friend class TSlice; - const TubeVector& _tube_vector; + const TubeVector& _tubevector; std::list::iterator _it_tslice; IntervalVector _codomain; }; diff --git a/src/core/2/domain/tube/codac2_TDomain.cpp b/src/core/2/domain/tube/codac2_TDomain.cpp index 6d4993be2..0a910be93 100644 --- a/src/core/2/domain/tube/codac2_TDomain.cpp +++ b/src/core/2/domain/tube/codac2_TDomain.cpp @@ -21,6 +21,9 @@ namespace codac2 { TDomain::TDomain(const Interval& t0_tf, double dt, bool with_gates) { + assert(!t0_tf.is_empty()); + assert(dt > 0.); + _tslices.push_back(TSlice(Interval(-oo,oo))); for(double t = t0_tf.lb() ; t < t0_tf.ub()+dt ; t+=dt) { @@ -34,8 +37,8 @@ namespace codac2 const Interval TDomain::t0_tf() const { - return Interval(_tslices.front().tdomain().ub(), - prev(_tslices.end())->tdomain().lb()); + return Interval(_tslices.front().t0_tf().ub(), + prev(_tslices.end())->t0_tf().lb()); } size_t TDomain::nb_tslices() const @@ -50,27 +53,40 @@ namespace codac2 list::iterator TDomain::iterator_tslice(double t) { + if(t == -oo) return _tslices.begin(); + if(t == oo) return prev(_tslices.end()); + list::iterator it; for(it = _tslices.begin(); it != _tslices.end(); ++it) - if(it->tdomain().contains(t)) return it; + { + const Interval& tdom = it->t0_tf(); + if((tdom.is_degenerated() && tdom.lb() == t) // gate + || (tdom.lb() <= t && tdom.ub() > t)) // slice + return it; + } + assert(false && "time must belong to one slice because tdomain is unbounded"); return _tslices.end(); } - void TDomain::sample(double t) + list::iterator TDomain::sample(double t, bool allow_gate) { - std::list::iterator it = iterator_tslice(t); + list::iterator it = iterator_tslice(t); assert(it != _tslices.end()); - const Interval tdomain = it->tdomain(); + const Interval tdomain = it->t0_tf(); assert(tdomain.contains(t)); + if((tdomain.lb() == t && !allow_gate) || tdomain.is_degenerated()) + return it; + it->set_tdomain(Interval(tdomain.lb(), t)); + TSlice ts(*it, Interval(t, tdomain.ub())); + ++it; + it = _tslices.insert(it, ts); + for(auto& [k,s] : it->_slices) // adding the new iterator pointer to the new slices + s._it_tslice = it; - if(t == tdomain.ub()) - assert(!it->tdomain().is_degenerated() && - "degenerated tslice (gate) already existing at this time"); - - _tslices.insert(it, TSlice(Interval(t, tdomain.ub()))); + return it; } const list& TDomain::tslices() const diff --git a/src/core/2/domain/tube/codac2_TDomain.h b/src/core/2/domain/tube/codac2_TDomain.h index f6eacdfec..e3bd45dc3 100644 --- a/src/core/2/domain/tube/codac2_TDomain.h +++ b/src/core/2/domain/tube/codac2_TDomain.h @@ -29,11 +29,11 @@ namespace codac2 public: explicit TDomain(const Interval& t0_tf, double dt, bool with_gates = false); - const Interval t0_tf() const; + const Interval t0_tf() const; // todo: keep this method? std::list::iterator iterator_tslice(double t); size_t nb_tslices() const; size_t nb_tubes() const; - void sample(double t); + std::list::iterator sample(double t, bool allow_gate = true); friend std::ostream& operator<<(std::ostream& os, const TDomain& x); diff --git a/src/core/2/domain/tube/codac2_TSlice.cpp b/src/core/2/domain/tube/codac2_TSlice.cpp index c3872f9cd..12532624f 100644 --- a/src/core/2/domain/tube/codac2_TSlice.cpp +++ b/src/core/2/domain/tube/codac2_TSlice.cpp @@ -11,49 +11,46 @@ #include "codac2_TSlice.h" #include "codac2_SliceVector.h" +#include "codac2_TubeVector.h" using namespace std; using namespace codac; namespace codac2 { - TSlice::TSlice(const Interval& tdomain) + TSlice::TSlice(const Interval& tdomain) : + _slices(map()) { set_tdomain(tdomain); } - TSlice::TSlice(const TSlice& tslice, const Interval& tdomain) + TSlice::TSlice(const TSlice& tslice, const Interval& tdomain) : + TSlice(tdomain) { - set_tdomain(tdomain); - for(const auto& [tv,s] : tslice.slices()) - add_slice(s); + for(const auto[k,s] : tslice._slices) + _slices.insert(pair( + k, SliceVector(s))); } - const Interval& TSlice::tdomain() const + const Interval& TSlice::t0_tf() const { - return _tdomain; + return _t0_tf; } void TSlice::set_tdomain(const Interval& tdomain) { assert(!tdomain.is_empty()); - _tdomain = tdomain; + _t0_tf = tdomain; } const map& TSlice::slices() const { return _slices; } - - void TSlice::add_slice(const SliceVector& slice) - { - _slices.insert( - pair(&slice.tube_vector(), slice)); - } ostream& operator<<(ostream& os, const TSlice& x) { - os << x._tdomain; + os << x._t0_tf; return os; } } // namespace codac \ No newline at end of file diff --git a/src/core/2/domain/tube/codac2_TSlice.h b/src/core/2/domain/tube/codac2_TSlice.h index 8311c7b2e..5d92496a0 100644 --- a/src/core/2/domain/tube/codac2_TSlice.h +++ b/src/core/2/domain/tube/codac2_TSlice.h @@ -32,9 +32,8 @@ namespace codac2 explicit TSlice(const Interval& tdomain); TSlice(const TSlice& tslice, const Interval& tdomain); // performs a deep copy on slices - const Interval& tdomain() const; + const Interval& t0_tf() const; const std::map& slices() const; - void add_slice(const SliceVector& slice); friend std::ostream& operator<<(std::ostream& os, const TSlice& x); protected: @@ -42,8 +41,10 @@ namespace codac2 void set_tdomain(const Interval& tdomain); friend class TubeVector; + friend class TubeVectorComponent; + friend class TubeVectorEvaluation; friend class TDomain; - Interval _tdomain; + Interval _t0_tf; std::map _slices; }; } // namespace codac diff --git a/src/core/2/domain/tube/codac2_TubeAbstract_const.h b/src/core/2/domain/tube/codac2_TubeAbstract_const.h index b0b516e41..8b8647bad 100644 --- a/src/core/2/domain/tube/codac2_TubeAbstract_const.h +++ b/src/core/2/domain/tube/codac2_TubeAbstract_const.h @@ -43,8 +43,8 @@ namespace codac2 virtual bool contains(const V& value) const = 0; virtual Interval t0_tf() const = 0; virtual I codomain() const = 0; - virtual I operator()(double t) const = 0; - virtual I operator()(const Interval& t) const = 0; + // virtual I operator()(double t) const = 0; + //virtual I operator()(const Interval& t) const = 0; //TubeVectorComponent operator[](size_t index); //const TubeVectorComponent operator[](size_t index) const; diff --git a/src/core/2/domain/tube/codac2_TubeVector.cpp b/src/core/2/domain/tube/codac2_TubeVector.cpp index 90de4cb89..bb44aaa42 100644 --- a/src/core/2/domain/tube/codac2_TubeVector.cpp +++ b/src/core/2/domain/tube/codac2_TubeVector.cpp @@ -23,7 +23,29 @@ namespace codac2 { for(list::iterator it = _tdomain._tslices.begin(); it != _tdomain._tslices.end(); ++it) - it->add_slice(SliceVector(n, *this, it)); + { + it->_slices.insert( + pair(this, + SliceVector(n, *this, it))); + } + } + + TubeVector::TubeVector(const TubeVector& x) : + _tdomain(x.tdomain()) + { + for(list::iterator it = _tdomain._tslices.begin(); + it != _tdomain._tslices.end(); ++it) + { + it->_slices.insert( + pair(this, + SliceVector(it->_slices.at(&x).size(), *this, it))); + } + } + + TubeVector::~TubeVector() + { + for(auto& s : _tdomain._tslices) + s._slices.erase(this); } size_t TubeVector::size() const @@ -82,14 +104,33 @@ namespace codac2 return codomain; } - IntervalVector TubeVector::operator()(double t) const + TubeVectorEvaluation TubeVector::operator()(double t) { - return codomain(); + return TubeVectorEvaluation(*this, t); } - IntervalVector TubeVector::operator()(const Interval& t) const + TubeVectorEvaluation TubeVector::operator()(const Interval& t) { - return codomain(); + return TubeVectorEvaluation(*this, t); + } + + IntervalVector TubeVector::eval(double t) const + { + return _tdomain.iterator_tslice(t)->_slices.at(this).codomain(); + } + + IntervalVector TubeVector::eval(const Interval& t) const + { + list::iterator it = _tdomain.iterator_tslice(t.lb()); + IntervalVector codomain = it->_slices.at(this).codomain(); + + while(it != _tdomain.iterator_tslice(t.ub())) + { + codomain |= it->_slices.at(this).codomain(); + it++; + } + + return codomain; } void TubeVector::set(const IntervalVector& codomain) @@ -99,21 +140,12 @@ namespace codac2 s.set(codomain); } - /*TubeVectorComponent TubeVector::operator[](size_t index) + TubeVectorComponent TubeVector::operator[](size_t i) { - assert(index >= 0 && index < size()); - return TubeVectorComponent(*this, index); + assert(i >= 0 && i < size()); + return TubeVectorComponent(*this, i); } - const TubeVectorComponent TubeVector::operator[](size_t index) const - { -// assert(index >= 0 && index < size()); -//return const_cast(static_cast(*this).operator[](index)); - - assert(index >= 0 && index < size()); - return TubeVectorComponent(*this, index); - }*/ - ostream& operator<<(ostream& os, const TubeVector& x) { x.TubeVector_const::print(os); diff --git a/src/core/2/domain/tube/codac2_TubeVector.h b/src/core/2/domain/tube/codac2_TubeVector.h index b06ef15c4..f162219b7 100644 --- a/src/core/2/domain/tube/codac2_TubeVector.h +++ b/src/core/2/domain/tube/codac2_TubeVector.h @@ -16,19 +16,23 @@ #include #include "codac2_TDomain.h" #include "codac2_SliceVector.h" - +#include "codac2_TubeVectorComponent.h" +#include "codac2_TubeVectorEvaluation.h" #include "codac2_TubeAbstract_const.h" namespace codac2 { class TDomain; - //class SliceVector; + class TubeVectorComponent; + class TubeVectorEvaluation; class TubeVector : public TubeVector_const { public: explicit TubeVector(size_t n, TDomain& tdomain); + explicit TubeVector(const TubeVector& x); + ~TubeVector(); size_t size() const; size_t nb_slices() const; @@ -37,16 +41,18 @@ namespace codac2 const SliceVector& last_slice() const; SliceVector& last_slice(); - //TubeVectorComponent operator[](size_t index); - //const TubeVectorComponent operator[](size_t index) const; + TubeVectorComponent operator[](size_t i); + //const TubeVectorComponent operator[](size_t i) const; bool contains(const TrajectoryVector& value) const; TDomain& tdomain() const; Interval t0_tf() const; IntervalVector codomain() const; - IntervalVector operator()(double t) const; - IntervalVector operator()(const Interval& t) const; + TubeVectorEvaluation operator()(double t); + TubeVectorEvaluation operator()(const Interval& t); + IntervalVector eval(double t) const; + IntervalVector eval(const Interval& t) const; void set(const IntervalVector& codomain); friend std::ostream& operator<<(std::ostream& os, const TubeVector& x); @@ -54,7 +60,8 @@ namespace codac2 protected: - //friend class TubeVectorComponent; + friend class TubeVectorComponent; + friend class TubeVectorEvaluation; TDomain& _tdomain; diff --git a/src/core/2/domain/tube/codac2_TubeVectorComponent.cpp b/src/core/2/domain/tube/codac2_TubeVectorComponent.cpp index 1f7074099..07c04b093 100644 --- a/src/core/2/domain/tube/codac2_TubeVectorComponent.cpp +++ b/src/core/2/domain/tube/codac2_TubeVectorComponent.cpp @@ -9,35 +9,88 @@ * the GNU Lesser General Public License (LGPL). */ +#include #include "codac2_TubeVectorComponent.h" #include "codac2_TubeVector.h" #include "codac2_SliceVector.h" +#include "ibex_Interval.h" using namespace std; namespace codac2 { - /*const Interval_& TubeVectorComponent::tdomain() const + TubeVectorComponent::TubeVectorComponent(TubeVector& tubevector, size_t i) : + _i(i), _tubevector(tubevector) { - return m_tubevector.tdomain(); + assert(i >= 0 && i < tubevector.size()); } - bool TubeVectorComponent::contains(const Trajectory_& value) const + TubeVectorComponent::TubeVectorComponent(const TubeVectorComponent& tubevector_i) : + _i(tubevector_i._i), _tubevector(tubevector_i._tubevector) { - return true; + + } + + size_t TubeVectorComponent::size() const + { + return 1; } - TubeVectorComponent::TubeVectorComponent(T_TubeVector& tubevector, size_t i) : - m_i(i), m_tubevector(tubevector) + const TDomain& TubeVectorComponent::tdomain() const { - assert(i >= 0 && i < tubevector.size()); + return _tubevector.tdomain(); + } + + Interval TubeVectorComponent::t0_tf() const + { + return _tubevector.t0_tf(); + } + + Interval TubeVectorComponent::codomain() const + { + Interval codomain(Interval::EMPTY_SET); + for(const auto& s : _tubevector) + codomain |= s.codomain()[_i]; + return codomain; + } + bool TubeVectorComponent::contains(const Trajectory& value) const + { + assert(false); + return true; } - void TubeVectorComponent::set_codomain(const Interval_& codomain) + void TubeVectorComponent::set(const Interval& codomain) + { + for(auto& s : _tubevector) + s._codomain[_i] = codomain; + } + + const TubeVectorComponent& TubeVectorComponent::operator=(const TubeVectorComponent& x) + { + assert(&x.tdomain() == &tdomain()); + for(auto& s : _tubevector) + s._codomain[_i] = s._it_tslice->_slices.at(&x._tubevector)._codomain[x._i]; + return *this; + } + + const TubeVectorComponent& TubeVectorComponent::operator=(pair,const TubeVectorComponent> rel) { - for(auto& s : m_tubevector.m_slices) - s->m_codomain[m_i] = codomain; - }*/ + assert(&rel.second.tdomain() == &tdomain()); + for(auto& s : _tubevector) + s._codomain[_i] = rel.first(s._it_tslice->_slices.at(&rel.second._tubevector)._codomain[_i]); + return *this; + } + + ostream& operator<<(ostream& os, const TubeVectorComponent& x) + { + os << "Component " << x._i << " of: " << x._tubevector << flush; + return os; + } + + pair,const TubeVectorComponent> cos(const TubeVectorComponent& x) + { + return make_pair(static_cast(ibex::cos), x); + } } // namespace codac \ No newline at end of file diff --git a/src/core/2/domain/tube/codac2_TubeVectorComponent.h b/src/core/2/domain/tube/codac2_TubeVectorComponent.h index 1e9fe9ded..99ee6b868 100644 --- a/src/core/2/domain/tube/codac2_TubeVectorComponent.h +++ b/src/core/2/domain/tube/codac2_TubeVectorComponent.h @@ -14,33 +14,50 @@ #include #include "codac_Interval.h" +#include "codac_Trajectory.h" #include "codac2_TubeVector.h" +#include "codac2_TubeAbstract_const.h" +#include "ibex_Interval.h" namespace codac2 { - class TubeVector_; + using codac::Interval; + using codac::Trajectory; - /*template - class TubeVectorComponent// : public DomainItem_, public TemporalItem_ + class TubeVector; + + class TubeVectorComponent : public Tube_const { public: - const Interval_& tdomain() const; + TubeVectorComponent(const TubeVectorComponent& tubevector_i); + + size_t size() const; + const TDomain& tdomain() const; + Interval t0_tf() const; + Interval codomain() const; + + bool contains(const Trajectory& value) const; - bool contains(const Trajectory_& value) const; + void set(const Interval& codomain); + const TubeVectorComponent& operator=(const TubeVectorComponent& x); + const TubeVectorComponent& operator=(std::pair,const TubeVectorComponent> x); - void set_codomain(const Interval_& codomain); + friend std::ostream& operator<<(std::ostream& os, const TubeVectorComponent& x); protected: - friend class TubeVector_; - explicit TubeVectorComponent(T_TubeVector& tubevector, size_t i); + friend class TubeVector; + explicit TubeVectorComponent(TubeVector& tubevector, size_t i); + + size_t _i; + TubeVector& _tubevector; + }; + + std::pair,const TubeVectorComponent> cos(const TubeVectorComponent& x); - size_t m_i; - T_TubeVector& m_tubevector; - };*/ } // namespace codac #endif \ No newline at end of file diff --git a/src/core/2/domain/tube/codac2_TubeVectorEvaluation.cpp b/src/core/2/domain/tube/codac2_TubeVectorEvaluation.cpp new file mode 100644 index 000000000..fa69ca2bc --- /dev/null +++ b/src/core/2/domain/tube/codac2_TubeVectorEvaluation.cpp @@ -0,0 +1,60 @@ +/** + * \file + * + * ---------------------------------------------------------------------------- + * \date 2022 + * \author Simon Rohou + * \copyright Copyright 2022 Codac Team + * \license This program is distributed under the terms of + * the GNU Lesser General Public License (LGPL). + */ + +#include "codac2_TubeVectorEvaluation.h" +#include "codac2_TubeVector.h" +#include "codac2_SliceVector.h" +#include "ibex_Interval.h" + +using namespace std; + +namespace codac2 +{ + TubeVectorEvaluation::TubeVectorEvaluation(TubeVector& tubevector, double t) : + _t(Interval(t)), _tubevector(tubevector) + { + + } + + TubeVectorEvaluation::TubeVectorEvaluation(TubeVector& tubevector, const Interval& t) : + _t(t), _tubevector(tubevector) + { + + } + + TubeVectorEvaluation::operator IntervalVector() const + { + return _tubevector.eval(_t); + } + + const TubeVectorEvaluation& TubeVectorEvaluation::operator=(const IntervalVector& x) + { + // Sampling the tube only if affectation is performed + // (i.e. this is not done in the constructor) + list::iterator it_lb = _tubevector._tdomain.sample(_t.lb(), false); + list::iterator it_ub = _tubevector._tdomain.sample(_t.ub(), _t.is_degenerated()); + + do + { + it_lb->_slices.at(&_tubevector).set(x); + it_lb++; + } while(it_lb != it_ub); + + return *this; + } + + ostream& operator<<(ostream& os, const TubeVectorEvaluation& x) + { + os << x._tubevector.eval(x._t) << flush; + return os; + } + +} // namespace codac \ No newline at end of file diff --git a/src/core/2/domain/tube/codac2_TubeVectorEvaluation.h b/src/core/2/domain/tube/codac2_TubeVectorEvaluation.h new file mode 100644 index 000000000..3cb63030e --- /dev/null +++ b/src/core/2/domain/tube/codac2_TubeVectorEvaluation.h @@ -0,0 +1,49 @@ +/** + * \file + * + * ---------------------------------------------------------------------------- + * \date 2022 + * \author Simon Rohou + * \copyright Copyright 2022 Codac Team + * \license This program is distributed under the terms of + * the GNU Lesser General Public License (LGPL). + */ + +#ifndef __CODAC2_TUBEVECTOREVALUATION_H__ +#define __CODAC2_TUBEVECTOREVALUATION_H__ + +#include +#include "codac_Interval.h" +#include "codac_Trajectory.h" +#include "codac2_TubeVector.h" +#include "ibex_Interval.h" + +namespace codac2 +{ + using codac::Interval; + + class TubeVector; + + class TubeVectorEvaluation + { + public: + + const TubeVectorEvaluation& operator=(const IntervalVector& x); + operator IntervalVector() const; + + friend std::ostream& operator<<(std::ostream& os, const TubeVectorEvaluation& x); + + + protected: + + friend class TubeVector; + explicit TubeVectorEvaluation(TubeVector& tubevector, double t); + explicit TubeVectorEvaluation(TubeVector& tubevector, const Interval& t); + + const Interval _t; + TubeVector& _tubevector; + }; + +} // namespace codac + +#endif \ No newline at end of file diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 99e094a93..49fb3ae54 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -167,6 +167,8 @@ ${CMAKE_CURRENT_SOURCE_DIR}/2/domain/tube/codac2_TubeVector.h ${CMAKE_CURRENT_SOURCE_DIR}/2/domain/tube/codac2_TubeVectorComponent.cpp ${CMAKE_CURRENT_SOURCE_DIR}/2/domain/tube/codac2_TubeVectorComponent.h + ${CMAKE_CURRENT_SOURCE_DIR}/2/domain/tube/codac2_TubeVectorEvaluation.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/2/domain/tube/codac2_TubeVectorEvaluation.h ) From db72e0a9dc03715ee282dd6fc977b776d4b91a4c Mon Sep 17 00:00:00 2001 From: SimonRohou Date: Mon, 30 May 2022 15:13:23 +0200 Subject: [PATCH 007/256] [tube] to_codac1() conversion methods --- src/core/2/domain/tube/codac2_TubeVector.cpp | 12 ++++++++++++ src/core/2/domain/tube/codac2_TubeVector.h | 3 +++ .../2/domain/tube/codac2_TubeVectorComponent.cpp | 12 ++++++++++++ src/core/2/domain/tube/codac2_TubeVectorComponent.h | 3 +++ 4 files changed, 30 insertions(+) diff --git a/src/core/2/domain/tube/codac2_TubeVector.cpp b/src/core/2/domain/tube/codac2_TubeVector.cpp index bb44aaa42..2bac74edf 100644 --- a/src/core/2/domain/tube/codac2_TubeVector.cpp +++ b/src/core/2/domain/tube/codac2_TubeVector.cpp @@ -155,4 +155,16 @@ namespace codac2 << flush; return os; } + + codac::TubeVector TubeVector::to_codac1() const + { + codac::TubeVector x(t0_tf(), size()); + for(const auto& s : *this) + if(!s.t0_tf().is_unbounded()) + x.set(s.codomain(), s.t0_tf()); + for(const auto& s : *this) // setting gate (were overwritten) + if(s.t0_tf().is_degenerated()) + x.set(s.codomain(), s.t0_tf()); + return x; + } } // namespace codac \ No newline at end of file diff --git a/src/core/2/domain/tube/codac2_TubeVector.h b/src/core/2/domain/tube/codac2_TubeVector.h index f162219b7..cbaf81221 100644 --- a/src/core/2/domain/tube/codac2_TubeVector.h +++ b/src/core/2/domain/tube/codac2_TubeVector.h @@ -19,6 +19,7 @@ #include "codac2_TubeVectorComponent.h" #include "codac2_TubeVectorEvaluation.h" #include "codac2_TubeAbstract_const.h" +#include "codac_TubeVector.h" // to be removed namespace codac2 { @@ -57,6 +58,8 @@ namespace codac2 friend std::ostream& operator<<(std::ostream& os, const TubeVector& x); + codac::TubeVector to_codac1() const; // to be removed + protected: diff --git a/src/core/2/domain/tube/codac2_TubeVectorComponent.cpp b/src/core/2/domain/tube/codac2_TubeVectorComponent.cpp index 07c04b093..17cb2fffb 100644 --- a/src/core/2/domain/tube/codac2_TubeVectorComponent.cpp +++ b/src/core/2/domain/tube/codac2_TubeVectorComponent.cpp @@ -92,5 +92,17 @@ namespace codac2 { return make_pair(static_cast(ibex::cos), x); } + + codac::Tube TubeVectorComponent::to_codac1() const + { + codac::Tube x(t0_tf()); + for(const auto& s : _tubevector) + if(!s.t0_tf().is_unbounded()) + x.set(s.codomain()[_i], s.t0_tf()); + for(const auto& s : _tubevector) // setting gate (were overwritten) + if(s.t0_tf().is_degenerated()) + x.set(s.codomain()[_i], s.t0_tf()); + return x; + } } // namespace codac \ No newline at end of file diff --git a/src/core/2/domain/tube/codac2_TubeVectorComponent.h b/src/core/2/domain/tube/codac2_TubeVectorComponent.h index 99ee6b868..9029bf566 100644 --- a/src/core/2/domain/tube/codac2_TubeVectorComponent.h +++ b/src/core/2/domain/tube/codac2_TubeVectorComponent.h @@ -18,6 +18,7 @@ #include "codac2_TubeVector.h" #include "codac2_TubeAbstract_const.h" #include "ibex_Interval.h" +#include "codac_Tube.h" // to be removed namespace codac2 { @@ -44,6 +45,8 @@ namespace codac2 const TubeVectorComponent& operator=(std::pair,const TubeVectorComponent> x); friend std::ostream& operator<<(std::ostream& os, const TubeVectorComponent& x); + + codac::Tube to_codac1() const; // to be removed protected: From 0482cd7f3952d6bbdc4cb896388ef51b2277360f Mon Sep 17 00:00:00 2001 From: SimonRohou Date: Mon, 30 May 2022 16:37:39 +0200 Subject: [PATCH 008/256] [ctc] preparing for CtcDiffInclusion --- .../2/contractors/codac2_CtcDiffInclusion.cpp | 49 +++++++++++++++++++ .../2/contractors/codac2_CtcDiffInclusion.h | 42 ++++++++++++++++ .../tube/codac2_SliceVector.cpp | 22 +++++++++ .../tube/codac2_SliceVector.h | 7 +++ .../tube/codac2_TDomain.cpp | 0 .../{domain => domains}/tube/codac2_TDomain.h | 0 .../tube/codac2_TSlice.cpp | 0 .../{domain => domains}/tube/codac2_TSlice.h | 0 .../tube/codac2_TubeAbstract_const.cpp | 0 .../tube/codac2_TubeAbstract_const.h | 0 .../tube/codac2_TubeVector.cpp | 16 ++++++ .../tube/codac2_TubeVector.h | 2 + .../tube/codac2_TubeVectorComponent.cpp | 0 .../tube/codac2_TubeVectorComponent.h | 0 .../tube/codac2_TubeVectorEvaluation.cpp | 0 .../tube/codac2_TubeVectorEvaluation.h | 0 src/core/CMakeLists.txt | 34 +++++++------ 17 files changed, 157 insertions(+), 15 deletions(-) create mode 100644 src/core/2/contractors/codac2_CtcDiffInclusion.cpp create mode 100644 src/core/2/contractors/codac2_CtcDiffInclusion.h rename src/core/2/{domain => domains}/tube/codac2_SliceVector.cpp (85%) rename src/core/2/{domain => domains}/tube/codac2_SliceVector.h (90%) rename src/core/2/{domain => domains}/tube/codac2_TDomain.cpp (100%) rename src/core/2/{domain => domains}/tube/codac2_TDomain.h (100%) rename src/core/2/{domain => domains}/tube/codac2_TSlice.cpp (100%) rename src/core/2/{domain => domains}/tube/codac2_TSlice.h (100%) rename src/core/2/{domain => domains}/tube/codac2_TubeAbstract_const.cpp (100%) rename src/core/2/{domain => domains}/tube/codac2_TubeAbstract_const.h (100%) rename src/core/2/{domain => domains}/tube/codac2_TubeVector.cpp (93%) rename src/core/2/{domain => domains}/tube/codac2_TubeVector.h (98%) rename src/core/2/{domain => domains}/tube/codac2_TubeVectorComponent.cpp (100%) rename src/core/2/{domain => domains}/tube/codac2_TubeVectorComponent.h (100%) rename src/core/2/{domain => domains}/tube/codac2_TubeVectorEvaluation.cpp (100%) rename src/core/2/{domain => domains}/tube/codac2_TubeVectorEvaluation.h (100%) diff --git a/src/core/2/contractors/codac2_CtcDiffInclusion.cpp b/src/core/2/contractors/codac2_CtcDiffInclusion.cpp new file mode 100644 index 000000000..69b516fad --- /dev/null +++ b/src/core/2/contractors/codac2_CtcDiffInclusion.cpp @@ -0,0 +1,49 @@ +/** + * CtcDiffInclusion class + * ---------------------------------------------------------------------------- + * \date 2022 + * \author + * \copyright Copyright 2022 Codac Team + * \license This program is distributed under the terms of + * the GNU Lesser General Public License (LGPL). + */ + +#include "codac2_CtcDiffInclusion.h" + +using namespace std; +using namespace ibex; + +namespace codac2 +{ + CtcDiffInclusion::CtcDiffInclusion(const TFunction& f) + : _f(f) + { + + } + + const TFunction& CtcDiffInclusion::f() const + { + return _f; + } + + void CtcDiffInclusion::contract(TubeVector& x, TubeVector& u) + { + // Verifying that x and u share exactly the same tdomain and slicing: + assert(&x.tdomain() == &u.tdomain()); + assert(_f.nb_var() == x.size() + u.size()); + assert(_f.image_dim() == x.size()); + + for(auto& sx : x) // sx is a SliceVector of the TubeVector x + { + // su is a SliceVector of the TubeVector u: + const SliceVector& su = sx.tslice().slices().at(&u); + + + sx.set(su.codomain()); + cout << sx << " " << su << endl; + + // ... + } + + } +} \ No newline at end of file diff --git a/src/core/2/contractors/codac2_CtcDiffInclusion.h b/src/core/2/contractors/codac2_CtcDiffInclusion.h new file mode 100644 index 000000000..50b362afb --- /dev/null +++ b/src/core/2/contractors/codac2_CtcDiffInclusion.h @@ -0,0 +1,42 @@ +/** + * \file + * CtcDiffInclusion class + * ---------------------------------------------------------------------------- + * \date 2022 + * \author + * \copyright Copyright 2022 Codac Team + * \license This program is distributed under the terms of + * the GNU Lesser General Public License (LGPL). + */ + +#ifndef __CODAC2_CTCDIFFINCLUSION_H__ +#define __CODAC2_CTCDIFFINCLUSION_H__ + +#include "codac_TFunction.h" +#include "codac2_TubeVector.h" + +namespace codac2 +{ + using codac::TFunction; + + /** + * \class CtcDiffInclusion + * \brief ... + */ + class CtcDiffInclusion + { + public: + + CtcDiffInclusion(const TFunction& t); + void contract(TubeVector& x, const TubeVector& u); + const TFunction& f() const; + + void contract(TubeVector& x, TubeVector& u); + + protected: + + const TFunction _f; + }; +} + +#endif \ No newline at end of file diff --git a/src/core/2/domain/tube/codac2_SliceVector.cpp b/src/core/2/domains/tube/codac2_SliceVector.cpp similarity index 85% rename from src/core/2/domain/tube/codac2_SliceVector.cpp rename to src/core/2/domains/tube/codac2_SliceVector.cpp index e5bac1440..4939b65a9 100644 --- a/src/core/2/domain/tube/codac2_SliceVector.cpp +++ b/src/core/2/domains/tube/codac2_SliceVector.cpp @@ -44,6 +44,21 @@ namespace codac2 return _codomain.size(); } + bool SliceVector::is_gate() const + { + return t0_tf().is_degenerated(); + } + + bool SliceVector::is_empty() const + { + return input_gate().is_empty() || output_gate().is_empty(); + } + + bool SliceVector::is_unbounded() const + { + return _codomain.is_unbounded(); + } + bool SliceVector::contains(const TrajectoryVector& value) const { return true; @@ -54,6 +69,11 @@ namespace codac2 return _it_tslice->t0_tf(); } + const TSlice& SliceVector::tslice() const + { + return *_it_tslice; + } + const SliceVector* SliceVector::prev_slice() const { if(&_tubevector.first_slice() == this) @@ -105,6 +125,8 @@ namespace codac2 { assert((size_t)codomain.size() == size()); _codomain = codomain; + if(is_gate()) + _codomain &= prev_slice()->codomain() & next_slice()->codomain(); } ostream& operator<<(ostream& os, const SliceVector& x) diff --git a/src/core/2/domain/tube/codac2_SliceVector.h b/src/core/2/domains/tube/codac2_SliceVector.h similarity index 90% rename from src/core/2/domain/tube/codac2_SliceVector.h rename to src/core/2/domains/tube/codac2_SliceVector.h index dee7e121c..47c65e11c 100644 --- a/src/core/2/domain/tube/codac2_SliceVector.h +++ b/src/core/2/domains/tube/codac2_SliceVector.h @@ -47,6 +47,9 @@ namespace codac2 size_t size() const; + bool is_gate() const; + bool is_empty() const; + bool is_unbounded() const; bool contains(const TrajectoryVector& value) const; const SliceVector* prev_slice() const; @@ -55,6 +58,7 @@ namespace codac2 SliceVector* next_slice(); const Interval& t0_tf() const; + const TSlice& tslice() const; const IntervalVector& codomain() const; IntervalVector input_gate() const; @@ -75,6 +79,9 @@ namespace codac2 const TubeVector& _tubevector; std::list::iterator _it_tslice; IntervalVector _codomain; + + // Wrappers related to guaranteed integration can be dynamically allocated here + // ... }; } // namespace codac diff --git a/src/core/2/domain/tube/codac2_TDomain.cpp b/src/core/2/domains/tube/codac2_TDomain.cpp similarity index 100% rename from src/core/2/domain/tube/codac2_TDomain.cpp rename to src/core/2/domains/tube/codac2_TDomain.cpp diff --git a/src/core/2/domain/tube/codac2_TDomain.h b/src/core/2/domains/tube/codac2_TDomain.h similarity index 100% rename from src/core/2/domain/tube/codac2_TDomain.h rename to src/core/2/domains/tube/codac2_TDomain.h diff --git a/src/core/2/domain/tube/codac2_TSlice.cpp b/src/core/2/domains/tube/codac2_TSlice.cpp similarity index 100% rename from src/core/2/domain/tube/codac2_TSlice.cpp rename to src/core/2/domains/tube/codac2_TSlice.cpp diff --git a/src/core/2/domain/tube/codac2_TSlice.h b/src/core/2/domains/tube/codac2_TSlice.h similarity index 100% rename from src/core/2/domain/tube/codac2_TSlice.h rename to src/core/2/domains/tube/codac2_TSlice.h diff --git a/src/core/2/domain/tube/codac2_TubeAbstract_const.cpp b/src/core/2/domains/tube/codac2_TubeAbstract_const.cpp similarity index 100% rename from src/core/2/domain/tube/codac2_TubeAbstract_const.cpp rename to src/core/2/domains/tube/codac2_TubeAbstract_const.cpp diff --git a/src/core/2/domain/tube/codac2_TubeAbstract_const.h b/src/core/2/domains/tube/codac2_TubeAbstract_const.h similarity index 100% rename from src/core/2/domain/tube/codac2_TubeAbstract_const.h rename to src/core/2/domains/tube/codac2_TubeAbstract_const.h diff --git a/src/core/2/domain/tube/codac2_TubeVector.cpp b/src/core/2/domains/tube/codac2_TubeVector.cpp similarity index 93% rename from src/core/2/domain/tube/codac2_TubeVector.cpp rename to src/core/2/domains/tube/codac2_TubeVector.cpp index 2bac74edf..f7b0f4182 100644 --- a/src/core/2/domain/tube/codac2_TubeVector.cpp +++ b/src/core/2/domains/tube/codac2_TubeVector.cpp @@ -80,6 +80,22 @@ namespace codac2 static_cast(*this).last_slice()); } + bool TubeVector::is_empty() const + { + for(const auto& s : *this) + if(s.is_empty()) + return true; + return false; + } + + bool TubeVector::is_unbounded() const + { + for(const auto& s : *this) + if(s.is_unbounded()) + return true; + return false; + } + bool TubeVector::contains(const TrajectoryVector& value) const { return true; diff --git a/src/core/2/domain/tube/codac2_TubeVector.h b/src/core/2/domains/tube/codac2_TubeVector.h similarity index 98% rename from src/core/2/domain/tube/codac2_TubeVector.h rename to src/core/2/domains/tube/codac2_TubeVector.h index cbaf81221..2cad7aca1 100644 --- a/src/core/2/domain/tube/codac2_TubeVector.h +++ b/src/core/2/domains/tube/codac2_TubeVector.h @@ -45,6 +45,8 @@ namespace codac2 TubeVectorComponent operator[](size_t i); //const TubeVectorComponent operator[](size_t i) const; + bool is_empty() const; + bool is_unbounded() const; bool contains(const TrajectoryVector& value) const; TDomain& tdomain() const; diff --git a/src/core/2/domain/tube/codac2_TubeVectorComponent.cpp b/src/core/2/domains/tube/codac2_TubeVectorComponent.cpp similarity index 100% rename from src/core/2/domain/tube/codac2_TubeVectorComponent.cpp rename to src/core/2/domains/tube/codac2_TubeVectorComponent.cpp diff --git a/src/core/2/domain/tube/codac2_TubeVectorComponent.h b/src/core/2/domains/tube/codac2_TubeVectorComponent.h similarity index 100% rename from src/core/2/domain/tube/codac2_TubeVectorComponent.h rename to src/core/2/domains/tube/codac2_TubeVectorComponent.h diff --git a/src/core/2/domain/tube/codac2_TubeVectorEvaluation.cpp b/src/core/2/domains/tube/codac2_TubeVectorEvaluation.cpp similarity index 100% rename from src/core/2/domain/tube/codac2_TubeVectorEvaluation.cpp rename to src/core/2/domains/tube/codac2_TubeVectorEvaluation.cpp diff --git a/src/core/2/domain/tube/codac2_TubeVectorEvaluation.h b/src/core/2/domains/tube/codac2_TubeVectorEvaluation.h similarity index 100% rename from src/core/2/domain/tube/codac2_TubeVectorEvaluation.h rename to src/core/2/domains/tube/codac2_TubeVectorEvaluation.h diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 49fb3ae54..9929d4d21 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -155,20 +155,22 @@ ${CMAKE_CURRENT_SOURCE_DIR}/sivia/codac_sivia.h # Files related to codac2 - ${CMAKE_CURRENT_SOURCE_DIR}/2/domain/tube/codac2_SliceVector.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/2/domain/tube/codac2_SliceVector.h - ${CMAKE_CURRENT_SOURCE_DIR}/2/domain/tube/codac2_TSlice.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/2/domain/tube/codac2_TSlice.h - ${CMAKE_CURRENT_SOURCE_DIR}/2/domain/tube/codac2_TDomain.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/2/domain/tube/codac2_TDomain.h - ${CMAKE_CURRENT_SOURCE_DIR}/2/domain/tube/codac2_TubeAbstract_const.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/2/domain/tube/codac2_TubeAbstract_const.h - ${CMAKE_CURRENT_SOURCE_DIR}/2/domain/tube/codac2_TubeVector.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/2/domain/tube/codac2_TubeVector.h - ${CMAKE_CURRENT_SOURCE_DIR}/2/domain/tube/codac2_TubeVectorComponent.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/2/domain/tube/codac2_TubeVectorComponent.h - ${CMAKE_CURRENT_SOURCE_DIR}/2/domain/tube/codac2_TubeVectorEvaluation.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/2/domain/tube/codac2_TubeVectorEvaluation.h + ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/tube/codac2_SliceVector.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/tube/codac2_SliceVector.h + ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/tube/codac2_TSlice.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/tube/codac2_TSlice.h + ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/tube/codac2_TDomain.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/tube/codac2_TDomain.h + ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/tube/codac2_TubeAbstract_const.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/tube/codac2_TubeAbstract_const.h + ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/tube/codac2_TubeVector.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/tube/codac2_TubeVector.h + ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/tube/codac2_TubeVectorComponent.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/tube/codac2_TubeVectorComponent.h + ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/tube/codac2_TubeVectorEvaluation.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/tube/codac2_TubeVectorEvaluation.h + ${CMAKE_CURRENT_SOURCE_DIR}/2/contractors/codac2_CtcDiffInclusion.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/2/contractors/codac2_CtcDiffInclusion.h ) @@ -196,7 +198,9 @@ ${CMAKE_CURRENT_SOURCE_DIR}/contractors/dyn ${CMAKE_CURRENT_SOURCE_DIR}/cn ${CMAKE_CURRENT_SOURCE_DIR}/tools - ${CMAKE_CURRENT_SOURCE_DIR}/sivia) + ${CMAKE_CURRENT_SOURCE_DIR}/sivia + ${CMAKE_CURRENT_SOURCE_DIR}/2/contractors + ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/tube) target_link_libraries(codac PUBLIC Ibex::ibex) From ea4719ac6f79c7e55a02932710b0f1423f7014d4 Mon Sep 17 00:00:00 2001 From: SimonRohou Date: Tue, 31 May 2022 10:50:59 +0200 Subject: [PATCH 009/256] [tube] faster constructor + build from TFnc --- .../2/contractors/codac2_CtcDiffInclusion.cpp | 27 +++++++++++++++---- .../2/contractors/codac2_CtcDiffInclusion.h | 4 +-- src/core/2/domains/tube/codac2_TDomain.cpp | 10 ++++--- src/core/2/domains/tube/codac2_TubeVector.cpp | 10 +++++++ src/core/2/domains/tube/codac2_TubeVector.h | 3 +++ 5 files changed, 44 insertions(+), 10 deletions(-) diff --git a/src/core/2/contractors/codac2_CtcDiffInclusion.cpp b/src/core/2/contractors/codac2_CtcDiffInclusion.cpp index 69b516fad..8544f6fc8 100644 --- a/src/core/2/contractors/codac2_CtcDiffInclusion.cpp +++ b/src/core/2/contractors/codac2_CtcDiffInclusion.cpp @@ -26,24 +26,41 @@ namespace codac2 return _f; } - void CtcDiffInclusion::contract(TubeVector& x, TubeVector& u) + void CtcDiffInclusion::contract(TubeVector& x, const TubeVector& u) { // Verifying that x and u share exactly the same tdomain and slicing: assert(&x.tdomain() == &u.tdomain()); - assert(_f.nb_var() == x.size() + u.size()); - assert(_f.image_dim() == x.size()); + // Verifying that the provided tubes are consistent with the function + assert((size_t)_f.nb_var() == 2); + assert((size_t)_f.image_dim() == x.size()); for(auto& sx : x) // sx is a SliceVector of the TubeVector x { + if(sx.is_gate()) // the slace may be on a degenerated temporal domain, i.e. a gate + continue; + // su is a SliceVector of the TubeVector u: const SliceVector& su = sx.tslice().slices().at(&u); + const double dt = sx.t0_tf().diam(); - sx.set(su.codomain()); - cout << sx << " " << su << endl; + // sx.set(su.codomain()); + // cout << sx << " " << su << endl; // ... } + } + + void CtcDiffInclusion::contract(SliceVector& x, const SliceVector& u) + { + // Verifying that x and u share exactly the same tdomain + assert(&x.tslice() == &u.tslice()); + // Verifying that the provided slices are consistent with the function + assert((size_t)_f.nb_var() == 2); + assert((size_t)_f.image_dim() == x.size()); + + const double dt = x.t0_tf().diam(); + // ... } } \ No newline at end of file diff --git a/src/core/2/contractors/codac2_CtcDiffInclusion.h b/src/core/2/contractors/codac2_CtcDiffInclusion.h index 50b362afb..02956b9cd 100644 --- a/src/core/2/contractors/codac2_CtcDiffInclusion.h +++ b/src/core/2/contractors/codac2_CtcDiffInclusion.h @@ -14,6 +14,7 @@ #include "codac_TFunction.h" #include "codac2_TubeVector.h" +#include "codac2_SliceVector.h" namespace codac2 { @@ -29,10 +30,9 @@ namespace codac2 CtcDiffInclusion(const TFunction& t); void contract(TubeVector& x, const TubeVector& u); + void contract(SliceVector& x, const SliceVector& u); const TFunction& f() const; - void contract(TubeVector& x, TubeVector& u); - protected: const TFunction _f; diff --git a/src/core/2/domains/tube/codac2_TDomain.cpp b/src/core/2/domains/tube/codac2_TDomain.cpp index 0a910be93..e2a34f514 100644 --- a/src/core/2/domains/tube/codac2_TDomain.cpp +++ b/src/core/2/domains/tube/codac2_TDomain.cpp @@ -24,15 +24,19 @@ namespace codac2 assert(!t0_tf.is_empty()); assert(dt > 0.); - _tslices.push_back(TSlice(Interval(-oo,oo))); + double prev_t = -oo; for(double t = t0_tf.lb() ; t < t0_tf.ub()+dt ; t+=dt) { double t_ = min(t, t0_tf.ub()); - sample(t_); + _tslices.push_back(TSlice(Interval(prev_t,t_))); if(with_gates) - sample(t_); // second sampling + _tslices.push_back(TSlice(Interval(t_,t_))); + + prev_t = t_; } + + _tslices.push_back(TSlice(Interval(t0_tf.ub(),oo))); } const Interval TDomain::t0_tf() const diff --git a/src/core/2/domains/tube/codac2_TubeVector.cpp b/src/core/2/domains/tube/codac2_TubeVector.cpp index f7b0f4182..47a4d95bd 100644 --- a/src/core/2/domains/tube/codac2_TubeVector.cpp +++ b/src/core/2/domains/tube/codac2_TubeVector.cpp @@ -30,6 +30,16 @@ namespace codac2 } } + TubeVector::TubeVector(size_t n, TDomain& tdomain, const TFnc& f) : + TubeVector(n, tdomain) + { + assert(f.nb_var() == 0 && "function's inputs must be limited to system variable"); + assert((size_t)f.image_dim() == n); + + for(auto& s : *this) + s.set(f.eval_vector(s.t0_tf())); + } + TubeVector::TubeVector(const TubeVector& x) : _tdomain(x.tdomain()) { diff --git a/src/core/2/domains/tube/codac2_TubeVector.h b/src/core/2/domains/tube/codac2_TubeVector.h index 2cad7aca1..1b39810c8 100644 --- a/src/core/2/domains/tube/codac2_TubeVector.h +++ b/src/core/2/domains/tube/codac2_TubeVector.h @@ -14,6 +14,7 @@ #include #include +#include "codac_TFnc.h" #include "codac2_TDomain.h" #include "codac2_SliceVector.h" #include "codac2_TubeVectorComponent.h" @@ -23,6 +24,7 @@ namespace codac2 { + using codac::TFnc; class TDomain; class TubeVectorComponent; class TubeVectorEvaluation; @@ -32,6 +34,7 @@ namespace codac2 public: explicit TubeVector(size_t n, TDomain& tdomain); + explicit TubeVector(size_t n, TDomain& tdomain, const TFnc& f); explicit TubeVector(const TubeVector& x); ~TubeVector(); size_t size() const; From 0d6ecda130e34f4197647137fbdd69aea556facc Mon Sep 17 00:00:00 2001 From: SimonRohou Date: Tue, 31 May 2022 10:51:37 +0200 Subject: [PATCH 010/256] [tests] added tests for codac2::TubeVector --- tests/core/CMakeLists.txt | 49 ++++--- tests/core/tests_codac2_tubes.cpp | 227 ++++++++++++++++++++++++++++++ 2 files changed, 253 insertions(+), 23 deletions(-) create mode 100644 tests/core/tests_codac2_tubes.cpp diff --git a/tests/core/CMakeLists.txt b/tests/core/CMakeLists.txt index a42b39c37..7762925b6 100644 --- a/tests/core/CMakeLists.txt +++ b/tests/core/CMakeLists.txt @@ -5,29 +5,32 @@ set(TESTS_NAME codac-tests-core) list(APPEND SRC_TESTS ${CMAKE_CURRENT_SOURCE_DIR}/main.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_predefined_tubes.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_predefined_tubes.h - ${CMAKE_CURRENT_SOURCE_DIR}/tests_arithmetic.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_cn.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_ctc_box.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_ctc_cart_prod.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_ctc_delay.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_ctc_deriv.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_ctc_chain.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_ctc_eval.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_ctc_picard.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_ctc_lohner.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_ctc_static.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_definition.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_functions.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_integration.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_operators.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_geometry.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_polygons.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_serialization.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_slices_structure.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_trajectory.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_values.cpp + + ${CMAKE_CURRENT_SOURCE_DIR}/tests_codac2_tubes.cpp + + #${CMAKE_CURRENT_SOURCE_DIR}/tests_predefined_tubes.cpp + #${CMAKE_CURRENT_SOURCE_DIR}/tests_predefined_tubes.h + #${CMAKE_CURRENT_SOURCE_DIR}/tests_arithmetic.cpp + #${CMAKE_CURRENT_SOURCE_DIR}/tests_cn.cpp + #${CMAKE_CURRENT_SOURCE_DIR}/tests_ctc_box.cpp + #${CMAKE_CURRENT_SOURCE_DIR}/tests_ctc_cart_prod.cpp + #${CMAKE_CURRENT_SOURCE_DIR}/tests_ctc_delay.cpp + #${CMAKE_CURRENT_SOURCE_DIR}/tests_ctc_deriv.cpp + #${CMAKE_CURRENT_SOURCE_DIR}/tests_ctc_chain.cpp + #${CMAKE_CURRENT_SOURCE_DIR}/tests_ctc_eval.cpp + #${CMAKE_CURRENT_SOURCE_DIR}/tests_ctc_picard.cpp + #${CMAKE_CURRENT_SOURCE_DIR}/tests_ctc_lohner.cpp + #${CMAKE_CURRENT_SOURCE_DIR}/tests_ctc_static.cpp + #${CMAKE_CURRENT_SOURCE_DIR}/tests_definition.cpp + #${CMAKE_CURRENT_SOURCE_DIR}/tests_functions.cpp + #${CMAKE_CURRENT_SOURCE_DIR}/tests_integration.cpp + #${CMAKE_CURRENT_SOURCE_DIR}/tests_operators.cpp + #${CMAKE_CURRENT_SOURCE_DIR}/tests_geometry.cpp + #${CMAKE_CURRENT_SOURCE_DIR}/tests_polygons.cpp + #${CMAKE_CURRENT_SOURCE_DIR}/tests_serialization.cpp + #${CMAKE_CURRENT_SOURCE_DIR}/tests_slices_structure.cpp + #${CMAKE_CURRENT_SOURCE_DIR}/tests_trajectory.cpp + #${CMAKE_CURRENT_SOURCE_DIR}/tests_values.cpp ) add_executable(${TESTS_NAME} ${SRC_TESTS}) diff --git a/tests/core/tests_codac2_tubes.cpp b/tests/core/tests_codac2_tubes.cpp new file mode 100644 index 000000000..c6f5605ff --- /dev/null +++ b/tests/core/tests_codac2_tubes.cpp @@ -0,0 +1,227 @@ +#include "catch_interval.hpp" +#include "vibes.h" + +// Using #define so that we can access protected methods +// of the class for tests purposes +#define protected public +#include "codac2_TDomain.h" + +#include "codac_predef_values.h" +#include "codac2_TubeVector.h" + +using namespace Catch; +using namespace Detail; +using namespace std; +using namespace ibex; +using namespace codac2; +using codac::oo; + +TEST_CASE("Test codac2::tubes") +{ + SECTION("Test TDomain") + { + TDomain tdomain(Interval(0,1), 0.5); + CHECK(tdomain.nb_tslices() == 4); + CHECK(tdomain.t0_tf() == Interval(0,1)); + + const list& tslices = tdomain.tslices(); + vector vector_tslices{ + make_move_iterator(tslices.begin()), + make_move_iterator(tslices.end()) }; + + CHECK(vector_tslices.size() == 4); + CHECK(vector_tslices[0].t0_tf() == Interval(-oo,0)); + CHECK(vector_tslices[1].t0_tf() == Interval(0,0.5)); + CHECK(vector_tslices[2].t0_tf() == Interval(0.5,1)); + CHECK(vector_tslices[3].t0_tf() == Interval(1,oo)); + + CHECK(tdomain.iterator_tslice(-10.)->t0_tf() == Interval(-oo,0)); + CHECK(tdomain.iterator_tslice(-120.)->t0_tf() == Interval(-oo,0)); + CHECK(tdomain.iterator_tslice(0.2)->t0_tf() == Interval(0,0.5)); + CHECK(tdomain.iterator_tslice(5540.2)->t0_tf() == Interval(1,oo)); + + CHECK(tdomain.nb_tubes() == 0); + TubeVector x(2, tdomain); // adding a tubevector to the tdomain + CHECK(tdomain.nb_tubes() == 1); + + { // new scope + TubeVector v(3, tdomain); + CHECK(tdomain.nb_tubes() == 2); + } // end of scope, auto removing the tube + + CHECK(tdomain.nb_tubes() == 1); + } + + SECTION("Test degenerated TDomain") + { + TDomain tdomain(Interval(1), 0.5); + CHECK(tdomain.nb_tslices() == 2); + CHECK(tdomain.t0_tf() == Interval(1)); + CHECK(tdomain.nb_tubes() == 0); + + const list& tslices = tdomain.tslices(); + vector vector_tslices{ + make_move_iterator(tslices.begin()), + make_move_iterator(tslices.end()) }; + + CHECK(vector_tslices.size() == 2); + CHECK(vector_tslices[0].t0_tf() == Interval(-oo,1)); + CHECK(vector_tslices[1].t0_tf() == Interval(1,oo)); + } + + SECTION("Test TDomain with gates") + { + TDomain tdomain(Interval(0,1), 0.5, true); + CHECK(tdomain.nb_tslices() == 7); + CHECK(tdomain.t0_tf() == Interval(0,1)); + CHECK(tdomain.nb_tubes() == 0); + + const list& tslices = tdomain.tslices(); + vector vector_tslices{ + make_move_iterator(tslices.begin()), + make_move_iterator(tslices.end()) }; + + CHECK(vector_tslices.size() == 7); + CHECK(vector_tslices[0].t0_tf() == Interval(-oo,0)); + CHECK(vector_tslices[1].t0_tf() == Interval(0)); + CHECK(vector_tslices[2].t0_tf() == Interval(0,0.5)); + CHECK(vector_tslices[3].t0_tf() == Interval(0.5)); + CHECK(vector_tslices[4].t0_tf() == Interval(0.5,1)); + CHECK(vector_tslices[5].t0_tf() == Interval(1,1)); + CHECK(vector_tslices[6].t0_tf() == Interval(1,oo)); + + CHECK(tdomain.iterator_tslice(-oo)->t0_tf() == Interval(-oo,0)); + CHECK(tdomain.iterator_tslice(-10.)->t0_tf() == Interval(-oo,0)); + CHECK(tdomain.iterator_tslice(0.)->t0_tf() == Interval(0)); + CHECK(tdomain.iterator_tslice(0.6)->t0_tf() == Interval(0.5,1)); + CHECK(tdomain.iterator_tslice(5540.2)->t0_tf() == Interval(1,oo)); + CHECK(tdomain.iterator_tslice(oo)->t0_tf() == Interval(1,oo)); + } + + SECTION("Test TDomain with sampling") + { + TDomain tdomain(Interval(1), 0.5); + CHECK(tdomain.nb_tslices() == 2); + tdomain.sample(10.); + CHECK(tdomain.nb_tslices() == 3); + tdomain.sample(10.); // second sampling which creates a gate + CHECK(tdomain.nb_tslices() == 4); + tdomain.sample(10.); // no more action + CHECK(tdomain.nb_tslices() == 4); + + const list& tslices = tdomain.tslices(); + vector vector_tslices{ + make_move_iterator(tslices.begin()), + make_move_iterator(tslices.end()) }; + + CHECK(vector_tslices.size() == 4); + CHECK(vector_tslices[0].t0_tf() == Interval(-oo,1)); + CHECK(vector_tslices[1].t0_tf() == Interval(1,10)); + CHECK(vector_tslices[2].t0_tf() == Interval(10)); + CHECK(vector_tslices[3].t0_tf() == Interval(10,oo)); + } + + SECTION("Test basic TubeVector") + { + TDomain tdomain(Interval(0,1), 0.1, false); + TubeVector x(3, tdomain); + + CHECK(x.size() == 3); + CHECK(&x.tdomain() == &tdomain); + CHECK(x.t0_tf() == Interval(0,1)); + CHECK(x.nb_slices() == tdomain.nb_tslices()); + CHECK(x.nb_slices() == 12); + CHECK(x.first_slice().t0_tf() == Interval(-oo,0)); + CHECK(x.last_slice().t0_tf() == Interval(1,oo)); + CHECK(x.codomain() == IntervalVector(3)); + x.set(IntervalVector(3, Interval(-10,10))); + CHECK(x.codomain() == IntervalVector(3, Interval(-10,10))); + + // TubeVectorComponent + CHECK(x[0].codomain() == Interval(-10,10)); + CHECK(x[0].t0_tf() == Interval(0,1)); + CHECK(&x[0].tdomain() == &tdomain); + x[0].set(Interval(-20,20)); + CHECK(x[0].codomain() == Interval(-20,20)); + CHECK(x.codomain() == IntervalVector({Interval(-20,20),Interval(-10,10),Interval(-10,10)})); + x[1] = x[0]; + CHECK(x[1].codomain() == Interval(-20,20)); + CHECK(x.codomain() == IntervalVector({Interval(-20,20),Interval(-20,20),Interval(-10,10)})); + + // Eval + CHECK(tdomain.nb_tubes() == 1); + CHECK(static_cast(x(Interval(-oo,oo))) == x.codomain()); + CHECK(static_cast(x(Interval(-1,1))) == x.codomain()); + CHECK(static_cast(x(-42.)) == x.codomain()); + + // Eval: affectation at scalar t + CHECK(tdomain.nb_tslices() == 12); + x(-42.) = IntervalVector(3,Interval(2.,3.)); + CHECK(tdomain.nb_tslices() == 14); + CHECK(static_cast(x(-42.)) == IntervalVector(3,Interval(2.,3.))); + CHECK(static_cast(x(ibex::previous_float(-42.))) == IntervalVector({Interval(-20,20),Interval(-20,20),Interval(-10,10)})); + CHECK(static_cast(x(ibex::next_float(-42.))) == IntervalVector({Interval(-20,20),Interval(-20,20),Interval(-10,10)})); + + // Eval: affectation at interval t + CHECK(tdomain.nb_tslices() == 14); + x(Interval(44,55)) = IntervalVector(3,Interval(9.,10.)); + CHECK(tdomain.nb_tslices() == 16); + CHECK(static_cast(x(Interval(44,55))) == IntervalVector(3,Interval(9.,10.))); + CHECK(static_cast(x(ibex::previous_float(44.))) == IntervalVector({Interval(-20,20),Interval(-20,20),Interval(-10,10)})); + CHECK(static_cast(x(ibex::next_float(55.))) == IntervalVector({Interval(-20,20),Interval(-20,20),Interval(-10,10)})); + + // Iterators tests + { + SliceVector* s_ = &x.first_slice(); + for(auto& s : x) + { + CHECK(&s == s_); + s_ = s_->next_slice(); + } + } + + // Iterators tests (const) + { + const TubeVector y(x); // copy constructor + const SliceVector* s_ = &x.first_slice(); + for(const auto& s : x) + { + CHECK(&s == s_); + s_ = s_->next_slice(); + } + } + } + + SECTION("Test SliceVector") + { + TDomain tdomain(Interval(0,1), 0.1); + TubeVector x(2, tdomain); + CHECK(x.nb_slices() == 12); + CHECK(&x.first_slice() == &tdomain.iterator_tslice(-oo)->_slices.at(&x)); + CHECK(&x.last_slice() == &tdomain.iterator_tslice(oo)->_slices.at(&x)); + + for(auto& s : x) + s.set(IntervalVector(2,s.t0_tf())); + + vector v; + for(const auto& s : x) + v.push_back(&s); + + CHECK(v[0]->t0_tf() == Interval(-oo,0)); + CHECK(v[0]->codomain() == IntervalVector(2,Interval(-oo,0))); + CHECK(v[0]->input_gate() == IntervalVector(2,Interval(-oo,0))); + CHECK(v[0]->output_gate() == IntervalVector(2,Interval(0))); + + CHECK(v[1]->t0_tf() == Interval(0,0.1)); + CHECK(v[1]->input_gate() == v[0]->output_gate()); + CHECK(v[1]->codomain() == IntervalVector(2,Interval(0,0.1))); + CHECK(v[1]->input_gate() == IntervalVector(2,Interval(0))); + CHECK(v[1]->output_gate() == IntervalVector(2,Interval(0.1))); + + CHECK(v[11]->t0_tf() == Interval(1,oo)); + CHECK(v[11]->input_gate() == v[10]->output_gate()); + CHECK(v[11]->codomain() == IntervalVector(2,Interval(1,oo))); + CHECK(v[11]->input_gate() == IntervalVector(2,Interval(1))); + CHECK(v[11]->output_gate() == IntervalVector(2,Interval(1,oo))); + } +} \ No newline at end of file From 729039b70609904dd4b107d9c586260ddb5fb8f5 Mon Sep 17 00:00:00 2001 From: SimonRohou Date: Tue, 31 May 2022 10:58:40 +0200 Subject: [PATCH 011/256] [ex] added first example of Codac2 --- examples/codac2/01/CMakeLists.txt | 41 +++++++++++++++++++++++++++++++ examples/codac2/01/main.cpp | 31 +++++++++++++++++++++++ 2 files changed, 72 insertions(+) create mode 100644 examples/codac2/01/CMakeLists.txt create mode 100644 examples/codac2/01/main.cpp diff --git a/examples/codac2/01/CMakeLists.txt b/examples/codac2/01/CMakeLists.txt new file mode 100644 index 000000000..40d8f265c --- /dev/null +++ b/examples/codac2/01/CMakeLists.txt @@ -0,0 +1,41 @@ +# ================================================================== +# codac / basics example - cmake configuration file +# ================================================================== + + cmake_minimum_required(VERSION 3.0.2) + project(codac_basics_01 LANGUAGES CXX) + +# Adding IBEX + + # In case you installed IBEX in a local directory, you need + # to specify its path with the CMAKE_PREFIX_PATH option. + # set(CMAKE_PREFIX_PATH "~/ibex-lib/build_install") + + find_package(IBEX REQUIRED) + ibex_init_common() # IBEX should have installed this function + message(STATUS "Found IBEX version ${IBEX_VERSION}") + +# Adding Eigen3 + + # In case you installed Eigen3 in a local directory, you need + # to specify its path with the CMAKE_PREFIX_PATH option, e.g. + # set(CMAKE_PREFIX_PATH "~/eigen/build_install") + + find_package(Eigen3 REQUIRED NO_MODULE) + message(STATUS "Found Eigen3 version ${EIGEN3_VERSION}") + +# Adding Codac + + # In case you installed Codac in a local directory, you need + # to specify its path with the CMAKE_PREFIX_PATH option. + # set(CMAKE_PREFIX_PATH "~/codac/build_install") + + find_package(CODAC REQUIRED) + message(STATUS "Found Codac version ${CODAC_VERSION}") + +# Compilation + + add_executable(${PROJECT_NAME} main.cpp) + target_compile_options(${PROJECT_NAME} PUBLIC ${CODAC_CXX_FLAGS}) + target_include_directories(${PROJECT_NAME} SYSTEM PUBLIC ${CODAC_INCLUDE_DIRS} ${EIGEN3_INCLUDE_DIRS}) + target_link_libraries(${PROJECT_NAME} PUBLIC ${CODAC_LIBRARIES} Ibex::ibex ${CODAC_LIBRARIES} Ibex::ibex) \ No newline at end of file diff --git a/examples/codac2/01/main.cpp b/examples/codac2/01/main.cpp new file mode 100644 index 000000000..970deb7ff --- /dev/null +++ b/examples/codac2/01/main.cpp @@ -0,0 +1,31 @@ +#include + +using namespace std; +using namespace codac; + +int main() +{ + codac2::TDomain tdomain(Interval(0,10), 0.01, true); // last argument creates "gates" (degenerated slices at scalar timesteps) + codac2::TubeVector x(2, tdomain, + TFunction("(sin(sqrt(t)+((t-5)^2)*[-0.01,0.01]) ; cos(t)+sin(t/0.2)*[-0.1,0.1])")); + codac2::TubeVector u(2, tdomain); + + TFunction tf("x[2]", "u[2]", "(sin(x[1]) ; -sin(x[0]))"); + codac2::CtcDiffInclusion ctc_diffincl(tf); + ctc_diffincl.contract(x,u); + + + vibes::beginDrawing(); + + codac::TubeVector x_codac1 = x.to_codac1(); // may take time + codac::Tube xi_codac1 = x[1].to_codac1(); // may take time + + VIBesFigTube fig("Tube"); + fig.set_properties(100, 100, 600, 300); + fig.add_tube(&xi_codac1, "x"); + fig.show(true); + + vibes::endDrawing(); + + return EXIT_SUCCESS; +} \ No newline at end of file From 050b0fb95e828f1d83044fdafb92779cb7df9451 Mon Sep 17 00:00:00 2001 From: SimonRohou Date: Thu, 9 Jun 2022 16:06:37 +0200 Subject: [PATCH 012/256] [tube] using std::variant for codomain of SliceVector --- .../2/contractors/codac2_CtcDiffInclusion.h | 1 + .../2/domains/tube/codac2_SliceVector.cpp | 52 +++++++++++++++---- src/core/2/domains/tube/codac2_SliceVector.h | 14 +++-- .../tube/codac2_TubeVectorComponent.cpp | 6 +-- .../2/integration/codac2_AbstractDomain.cpp | 27 ++++++++++ .../2/integration/codac2_AbstractDomain.h | 37 +++++++++++++ src/core/CMakeLists.txt | 5 +- 7 files changed, 122 insertions(+), 20 deletions(-) create mode 100644 src/core/2/integration/codac2_AbstractDomain.cpp create mode 100644 src/core/2/integration/codac2_AbstractDomain.h diff --git a/src/core/2/contractors/codac2_CtcDiffInclusion.h b/src/core/2/contractors/codac2_CtcDiffInclusion.h index 02956b9cd..99067811e 100644 --- a/src/core/2/contractors/codac2_CtcDiffInclusion.h +++ b/src/core/2/contractors/codac2_CtcDiffInclusion.h @@ -35,6 +35,7 @@ namespace codac2 protected: + friend class SliceVector; // to be removed const TFunction _f; }; } diff --git a/src/core/2/domains/tube/codac2_SliceVector.cpp b/src/core/2/domains/tube/codac2_SliceVector.cpp index 4939b65a9..7cb6b3d03 100644 --- a/src/core/2/domains/tube/codac2_SliceVector.cpp +++ b/src/core/2/domains/tube/codac2_SliceVector.cpp @@ -11,18 +11,30 @@ #include "codac2_TubeVector.h" #include "codac2_SliceVector.h" +#include "codac_Exception.h" using namespace std; namespace codac2 { - SliceVector::SliceVector(size_t n, const TubeVector& tube_vector, list::iterator it_tslice) : - _tubevector(tube_vector), _it_tslice(it_tslice), - _codomain(IntervalVector(n)) + SliceVector::SliceVector(size_t n, const TubeVector& tube_vector, const list::iterator& it_tslice) : + SliceVector(IntervalVector(n), tube_vector, it_tslice) { } + SliceVector::SliceVector(const IntervalVector& box, const TubeVector& tube_vector, const list::iterator& it_tslice) : + _tubevector(tube_vector), _it_tslice(it_tslice), _codomain(box) + { + assert(holds_alternative(_codomain)); + } + + SliceVector::SliceVector(const AbstractDomain& ad, const TubeVector& tube_vector, const list::iterator& it_tslice) : + _tubevector(tube_vector), _it_tslice(it_tslice), _codomain(ad) + { + assert(holds_alternative(_codomain)); + } + SliceVector::SliceVector(const SliceVector& s) : _tubevector(s._tubevector), _it_tslice(s._it_tslice), _codomain(s._codomain) { @@ -41,7 +53,7 @@ namespace codac2 size_t SliceVector::size() const { - return _codomain.size(); + return codomain().size(); } bool SliceVector::is_gate() const @@ -56,7 +68,7 @@ namespace codac2 bool SliceVector::is_unbounded() const { - return _codomain.is_unbounded(); + return codomain().is_unbounded(); } bool SliceVector::contains(const TrajectoryVector& value) const @@ -100,9 +112,16 @@ namespace codac2 static_cast(*this).next_slice()); } - const IntervalVector& SliceVector::codomain() const + const IntervalVector SliceVector::codomain() const { - return _codomain; + if(holds_alternative(_codomain)) + return get(_codomain); + + else if(holds_alternative(_codomain)) + return get(_codomain).box(); + + assert(false && "unhandled case"); + return IntervalVector(0); // should not happen } IntervalVector SliceVector::input_gate() const @@ -123,10 +142,21 @@ namespace codac2 void SliceVector::set(const IntervalVector& codomain) { - assert((size_t)codomain.size() == size()); - _codomain = codomain; - if(is_gate()) - _codomain &= prev_slice()->codomain() & next_slice()->codomain(); + if(holds_alternative(_codomain)) + { + assert((size_t)codomain.size() == size()); + get(_codomain) = codomain; + if(is_gate()) + get(_codomain) &= prev_slice()->codomain() & next_slice()->codomain(); + } + + else + throw codac::Exception(__func__, "unable to set values for this AbstractDomain"); + } + + void SliceVector::set_component(size_t i, const Interval& x) + { + } ostream& operator<<(ostream& os, const SliceVector& x) diff --git a/src/core/2/domains/tube/codac2_SliceVector.h b/src/core/2/domains/tube/codac2_SliceVector.h index 47c65e11c..ba90b632b 100644 --- a/src/core/2/domains/tube/codac2_SliceVector.h +++ b/src/core/2/domains/tube/codac2_SliceVector.h @@ -13,11 +13,13 @@ #define __CODAC2_SLICEVECTOR_H__ #include +#include #include "codac_Interval.h" #include "codac_IntervalVector.h" #include "codac_TrajectoryVector.h" #include "codac2_TSlice.h" #include "codac2_TDomain.h" +#include "codac2_AbstractDomain.h" namespace codac2 { @@ -32,7 +34,9 @@ namespace codac2 { public: - explicit SliceVector(size_t n, const TubeVector& tube_vector, std::list::iterator it_tslice); + explicit SliceVector(size_t n, const TubeVector& tube_vector, const std::list::iterator& it_tslice); + explicit SliceVector(const IntervalVector& box, const TubeVector& tube_vector, const std::list::iterator& it_tslice); + explicit SliceVector(const AbstractDomain& ad, const TubeVector& tube_vector, const std::list::iterator& it_tslice); ~SliceVector(); SliceVector(const SliceVector& s); @@ -60,11 +64,12 @@ namespace codac2 const Interval& t0_tf() const; const TSlice& tslice() const; - const IntervalVector& codomain() const; + const IntervalVector codomain() const; IntervalVector input_gate() const; IntervalVector output_gate() const; void set(const IntervalVector& codomain); + void set_component(size_t i, const Interval& x); friend std::ostream& operator<<(std::ostream& os, const SliceVector& x); @@ -78,10 +83,9 @@ namespace codac2 const TubeVector& _tubevector; std::list::iterator _it_tslice; - IntervalVector _codomain; - // Wrappers related to guaranteed integration can be dynamically allocated here - // ... + // Several abstract domains related to guaranteed integration can be considered here + std::variant _codomain; }; } // namespace codac diff --git a/src/core/2/domains/tube/codac2_TubeVectorComponent.cpp b/src/core/2/domains/tube/codac2_TubeVectorComponent.cpp index 17cb2fffb..6a527c0e1 100644 --- a/src/core/2/domains/tube/codac2_TubeVectorComponent.cpp +++ b/src/core/2/domains/tube/codac2_TubeVectorComponent.cpp @@ -63,14 +63,14 @@ namespace codac2 void TubeVectorComponent::set(const Interval& codomain) { for(auto& s : _tubevector) - s._codomain[_i] = codomain; + s.set_component(_i, codomain); } const TubeVectorComponent& TubeVectorComponent::operator=(const TubeVectorComponent& x) { assert(&x.tdomain() == &tdomain()); for(auto& s : _tubevector) - s._codomain[_i] = s._it_tslice->_slices.at(&x._tubevector)._codomain[x._i]; + s.set_component(_i, s._it_tslice->_slices.at(&x._tubevector).codomain()[x._i]); return *this; } @@ -78,7 +78,7 @@ namespace codac2 { assert(&rel.second.tdomain() == &tdomain()); for(auto& s : _tubevector) - s._codomain[_i] = rel.first(s._it_tslice->_slices.at(&rel.second._tubevector)._codomain[_i]); + s.set_component(_i, rel.first(s._it_tslice->_slices.at(&rel.second._tubevector).codomain()[rel.second._i])); return *this; } diff --git a/src/core/2/integration/codac2_AbstractDomain.cpp b/src/core/2/integration/codac2_AbstractDomain.cpp new file mode 100644 index 000000000..cd6c92143 --- /dev/null +++ b/src/core/2/integration/codac2_AbstractDomain.cpp @@ -0,0 +1,27 @@ +/** + * AbstractDomain class + * ---------------------------------------------------------------------------- + * \date 2022 + * \author Damien Massé + * \copyright Copyright 2022 Codac Team + * \license This program is distributed under the terms of + * the GNU Lesser General Public License (LGPL). + */ + +#include "codac2_AbstractDomain.h" + +using namespace std; +using namespace codac; + +namespace codac2 +{ + AbstractDomain::AbstractDomain() + { + + } + + IntervalVector AbstractDomain::box() const + { + return IntervalVector(0); // todo + } +} \ No newline at end of file diff --git a/src/core/2/integration/codac2_AbstractDomain.h b/src/core/2/integration/codac2_AbstractDomain.h new file mode 100644 index 000000000..60d3dca54 --- /dev/null +++ b/src/core/2/integration/codac2_AbstractDomain.h @@ -0,0 +1,37 @@ +/** + * \file + * AbstractDomain class + * ---------------------------------------------------------------------------- + * \date 2022 + * \author Damien Massé + * \copyright Copyright 2022 Codac Team + * \license This program is distributed under the terms of + * the GNU Lesser General Public License (LGPL). + */ + +#ifndef __CODAC2_ABSTRACTDOMAIN_H__ +#define __CODAC2_ABSTRACTDOMAIN_H__ + +#include "codac_IntervalVector.h" + +namespace codac2 +{ + using codac::IntervalVector; + + /** + * \class AbstractDomain + * \brief ... + */ + class AbstractDomain + { + public: + + AbstractDomain(); + IntervalVector box() const; + + protected: + + }; +} + +#endif \ No newline at end of file diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 9929d4d21..e2376f488 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -171,6 +171,8 @@ ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/tube/codac2_TubeVectorEvaluation.h ${CMAKE_CURRENT_SOURCE_DIR}/2/contractors/codac2_CtcDiffInclusion.cpp ${CMAKE_CURRENT_SOURCE_DIR}/2/contractors/codac2_CtcDiffInclusion.h + ${CMAKE_CURRENT_SOURCE_DIR}/2/integration/codac2_AbstractDomain.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/2/integration/codac2_AbstractDomain.h ) @@ -200,7 +202,8 @@ ${CMAKE_CURRENT_SOURCE_DIR}/tools ${CMAKE_CURRENT_SOURCE_DIR}/sivia ${CMAKE_CURRENT_SOURCE_DIR}/2/contractors - ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/tube) + ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/tube + ${CMAKE_CURRENT_SOURCE_DIR}/2/integration/) target_link_libraries(codac PUBLIC Ibex::ibex) From 3327f75c62496028cc023574eb9041c757eb9bde Mon Sep 17 00:00:00 2001 From: SimonRohou Date: Thu, 9 Jun 2022 16:52:46 +0200 Subject: [PATCH 013/256] [py] attempt to solve a bug --- python/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt index 12d3baafb..9db3ce068 100644 --- a/python/CMakeLists.txt +++ b/python/CMakeLists.txt @@ -180,7 +180,7 @@ ################################################################################ if(BUILD_TESTS) - include(CTest) + file(GLOB TESTS_FILES "${PYTHON_PACKAGE_DIR}/${PYTHON_PACKAGE_NAME}/tests/test_*.py" "${PYTHON_PACKAGE_DIR}/${PYTHON_PACKAGE_NAME}/tests/tests_from_pyibex/test_*.py" From e8a9130ee8b4a95c52d0000a133dfda2f03004ef Mon Sep 17 00:00:00 2001 From: SimonRohou Date: Thu, 9 Jun 2022 17:17:09 +0200 Subject: [PATCH 014/256] [actions] pip install numpy --- scripts/docker/build_pybinding.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/docker/build_pybinding.sh b/scripts/docker/build_pybinding.sh index 9119972f6..be0daf235 100644 --- a/scripts/docker/build_pybinding.sh +++ b/scripts/docker/build_pybinding.sh @@ -22,6 +22,7 @@ for PYBIN in /opt/python/cp3*/bin; do auditwheel repair "$whl" -w /io/wheelhouse/ done + "${PYBIN}/python" -m pip install numpy "${PYBIN}/python" -m pip install codac --no-deps --no-index -f /io/wheelhouse (cd "$HOME"; "${PYBIN}/python" -m unittest discover codac.tests) cd /io From 48104e2b766c8995fec0a6d12603aa0313e7a115 Mon Sep 17 00:00:00 2001 From: SimonRohou Date: Mon, 13 Jun 2022 17:30:21 +0200 Subject: [PATCH 015/256] [doc] updated info_dev (docker) --- doc/doc/dev/info_dev.rst | 11 ++++++++++- scripts/docker/build_pybinding.sh | 0 2 files changed, 10 insertions(+), 1 deletion(-) mode change 100644 => 100755 scripts/docker/build_pybinding.sh diff --git a/doc/doc/dev/info_dev.rst b/doc/doc/dev/info_dev.rst index 4b5797e34..e26a9463c 100644 --- a/doc/doc/dev/info_dev.rst +++ b/doc/doc/dev/info_dev.rst @@ -213,4 +213,13 @@ Upload the *wheels* on PyPi: .. code-block:: bash - python3 -m twine upload --repository pypi * \ No newline at end of file + python3 -m twine upload --repository pypi * + +.. rubric:: Testing with Docker + +In the :file:`codac` directory, test the Ubuntu configuration locally using Docker: + +.. code-block:: bash + + chmod a+x scripts/docker/build_pybinding.sh + docker run --rm -v `pwd`:/io lebarsfa/manylinux2010_x86_64-ibex /io/scripts/docker/build_pybinding.sh \ No newline at end of file diff --git a/scripts/docker/build_pybinding.sh b/scripts/docker/build_pybinding.sh old mode 100644 new mode 100755 From 340db48b39023bf2382a43cf7e102143529eaf85 Mon Sep 17 00:00:00 2001 From: SimonRohou Date: Mon, 13 Jun 2022 18:21:42 +0200 Subject: [PATCH 016/256] [ctc] removing variadic def of cart_prod for CtcCartProd (was shadowing other cart_prod functions) --- .../core/contractors/static/codac_py_CtcCartProd.cpp | 6 +++++- src/core/contractors/static/codac_CtcCartProd.cpp | 5 +++++ src/core/contractors/static/codac_CtcCartProd.h | 12 ++++-------- 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/python/src/core/contractors/static/codac_py_CtcCartProd.cpp b/python/src/core/contractors/static/codac_py_CtcCartProd.cpp index 14578b6a2..612963566 100644 --- a/python/src/core/contractors/static/codac_py_CtcCartProd.cpp +++ b/python/src/core/contractors/static/codac_py_CtcCartProd.cpp @@ -42,7 +42,11 @@ void export_CtcCartProd(py::module& m, py::class_& ctc) "x"_a.noconvert()) ; - m.def("cart_prod", [](ibex::Array a) + m.def("cart_prod", [](Ctc& c1, Ctc& c2) + { return cart_prod(c1, c2); }, + "c1"_a.noconvert(), "c2"_a.noconvert()); + + m.def("cart_prod", [](const ibex::Array& a) { return cart_prod(a); }, "array"_a.noconvert()); } \ No newline at end of file diff --git a/src/core/contractors/static/codac_CtcCartProd.cpp b/src/core/contractors/static/codac_CtcCartProd.cpp index a821791d5..14b48bfd1 100644 --- a/src/core/contractors/static/codac_CtcCartProd.cpp +++ b/src/core/contractors/static/codac_CtcCartProd.cpp @@ -27,6 +27,11 @@ namespace codac index += m_v[i].nb_var; } } + + CtcCartProd cart_prod(Ctc& c1, Ctc& c2) + { + return CtcCartProd(c1, c2); + } CtcCartProd cart_prod(const ibex::Array& array) { diff --git a/src/core/contractors/static/codac_CtcCartProd.h b/src/core/contractors/static/codac_CtcCartProd.h index 85e0d40ec..15081e20f 100644 --- a/src/core/contractors/static/codac_CtcCartProd.h +++ b/src/core/contractors/static/codac_CtcCartProd.h @@ -54,17 +54,13 @@ namespace codac }; /** - * \fn template CtcCartProd cart_prod(Args &...args) - * \brief Cartesian product of contractors + * \brief Cartesian product of contractors from two Ctc objects * - * \param args list of contractors + * \param c1 first Ctc contractor + * \param c2 second Ctc contractor * \return the Cartesian product of the contractors \f$\mathcal{C}_1\times\dots\times\mathcal{C}_n\f$ */ - template - CtcCartProd cart_prod(Args&...args) - { - return CtcCartProd(args...); - } + CtcCartProd cart_prod(Ctc& c1, Ctc& c2); /** * \brief Cartesian product of contractors from an ibex::Array From 99a39698cfd9b649fec58dab38ad95c1ee3af6d2 Mon Sep 17 00:00:00 2001 From: SimonRohou Date: Mon, 13 Jun 2022 18:22:43 +0200 Subject: [PATCH 017/256] [fnc] unlimited arguments for TFunction::eval_vector --- src/core/2/domains/tube/codac2_SliceVector.cpp | 9 +++++++++ src/core/functions/codac_TFunction.h | 10 ++++++++++ 2 files changed, 19 insertions(+) diff --git a/src/core/2/domains/tube/codac2_SliceVector.cpp b/src/core/2/domains/tube/codac2_SliceVector.cpp index 7cb6b3d03..a7be7d192 100644 --- a/src/core/2/domains/tube/codac2_SliceVector.cpp +++ b/src/core/2/domains/tube/codac2_SliceVector.cpp @@ -156,7 +156,16 @@ namespace codac2 void SliceVector::set_component(size_t i, const Interval& x) { + if(holds_alternative(_codomain)) + { + assert((size_t)codomain.size() == size()); + get(_codomain)[i] = x; + if(is_gate()) + get(_codomain)[i] &= prev_slice()->codomain()[i] & next_slice()->codomain()[i]; + } + else + throw codac::Exception(__func__, "unable to set values for this AbstractDomain"); } ostream& operator<<(ostream& os, const SliceVector& x) diff --git a/src/core/functions/codac_TFunction.h b/src/core/functions/codac_TFunction.h index 84d581e37..8e6455433 100644 --- a/src/core/functions/codac_TFunction.h +++ b/src/core/functions/codac_TFunction.h @@ -69,6 +69,16 @@ namespace codac const IntervalVector eval_vector(int slice_id, const TubeVector& x) const; const IntervalVector eval_vector(const Interval& t, const TubeVector& x) const; + template + const IntervalVector eval_vector(const IntervalVector& x, FirstArg& xi, Args&... xs) // recursive variadic function + { + IntervalVector x_ = cart_prod(x,xi); + if constexpr(sizeof...(xs) > 0) + return eval_vector(x_, xs...); + else + return eval_vector(x_); + } + const TFunction diff() const; protected: From db729b4492ccb4d77acd506ce39e51ebade581d3 Mon Sep 17 00:00:00 2001 From: SimonRohou Date: Mon, 13 Jun 2022 18:26:00 +0200 Subject: [PATCH 018/256] [ctc] TimePropag for CtcDiffInclusion --- .../2/contractors/codac2_CtcDiffInclusion.cpp | 28 +++++++++++++++++-- .../2/contractors/codac2_CtcDiffInclusion.h | 6 ++-- 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/src/core/2/contractors/codac2_CtcDiffInclusion.cpp b/src/core/2/contractors/codac2_CtcDiffInclusion.cpp index 8544f6fc8..96e4641b4 100644 --- a/src/core/2/contractors/codac2_CtcDiffInclusion.cpp +++ b/src/core/2/contractors/codac2_CtcDiffInclusion.cpp @@ -26,7 +26,7 @@ namespace codac2 return _f; } - void CtcDiffInclusion::contract(TubeVector& x, const TubeVector& u) + void CtcDiffInclusion::contract(TubeVector& x, const TubeVector& u, TimePropag t_propa) { // Verifying that x and u share exactly the same tdomain and slicing: assert(&x.tdomain() == &u.tdomain()); @@ -48,10 +48,22 @@ namespace codac2 // cout << sx << " " << su << endl; // ... + + if(t_propa & TimePropag::FORWARD) + { + // Computations related to forward propagation + // ... + } + + if(t_propa & TimePropag::BACKWARD) + { + // Computations related to backward propagation + // ... + } } } - void CtcDiffInclusion::contract(SliceVector& x, const SliceVector& u) + void CtcDiffInclusion::contract(SliceVector& x, const SliceVector& u, TimePropag t_propa) { // Verifying that x and u share exactly the same tdomain assert(&x.tslice() == &u.tslice()); @@ -62,5 +74,17 @@ namespace codac2 const double dt = x.t0_tf().diam(); // ... + + if(t_propa & TimePropag::FORWARD) + { + // Computations related to forward propagation + // ... + } + + if(t_propa & TimePropag::BACKWARD) + { + // Computations related to backward propagation + // ... + } } } \ No newline at end of file diff --git a/src/core/2/contractors/codac2_CtcDiffInclusion.h b/src/core/2/contractors/codac2_CtcDiffInclusion.h index 99067811e..558d28138 100644 --- a/src/core/2/contractors/codac2_CtcDiffInclusion.h +++ b/src/core/2/contractors/codac2_CtcDiffInclusion.h @@ -13,12 +13,14 @@ #define __CODAC2_CTCDIFFINCLUSION_H__ #include "codac_TFunction.h" +#include "codac_DynCtc.h" #include "codac2_TubeVector.h" #include "codac2_SliceVector.h" namespace codac2 { using codac::TFunction; + using codac::TimePropag; /** * \class CtcDiffInclusion @@ -29,8 +31,8 @@ namespace codac2 public: CtcDiffInclusion(const TFunction& t); - void contract(TubeVector& x, const TubeVector& u); - void contract(SliceVector& x, const SliceVector& u); + void contract(TubeVector& x, const TubeVector& u, TimePropag t_propa = TimePropag::FORWARD | TimePropag::BACKWARD); + void contract(SliceVector& x, const SliceVector& u, TimePropag t_propa = TimePropag::FORWARD | TimePropag::BACKWARD); const TFunction& f() const; protected: From 26e716ad4b7202bf50280782ef2deb27a5f83bc6 Mon Sep 17 00:00:00 2001 From: SimonRohou Date: Mon, 13 Jun 2022 18:38:06 +0200 Subject: [PATCH 019/256] [tube] assert error --- src/core/2/domains/tube/codac2_SliceVector.cpp | 12 ++++++------ src/core/2/domains/tube/codac2_SliceVector.h | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/core/2/domains/tube/codac2_SliceVector.cpp b/src/core/2/domains/tube/codac2_SliceVector.cpp index a7be7d192..963a4438f 100644 --- a/src/core/2/domains/tube/codac2_SliceVector.cpp +++ b/src/core/2/domains/tube/codac2_SliceVector.cpp @@ -140,12 +140,12 @@ namespace codac2 return gate; } - void SliceVector::set(const IntervalVector& codomain) + void SliceVector::set(const IntervalVector& x) { if(holds_alternative(_codomain)) { - assert((size_t)codomain.size() == size()); - get(_codomain) = codomain; + assert((size_t)codomain().size() == size()); + get(_codomain) = x; if(is_gate()) get(_codomain) &= prev_slice()->codomain() & next_slice()->codomain(); } @@ -154,12 +154,12 @@ namespace codac2 throw codac::Exception(__func__, "unable to set values for this AbstractDomain"); } - void SliceVector::set_component(size_t i, const Interval& x) + void SliceVector::set_component(size_t i, const Interval& xi) { if(holds_alternative(_codomain)) { - assert((size_t)codomain.size() == size()); - get(_codomain)[i] = x; + assert((size_t)codomain().size() == size()); + get(_codomain)[i] = xi; if(is_gate()) get(_codomain)[i] &= prev_slice()->codomain()[i] & next_slice()->codomain()[i]; } diff --git a/src/core/2/domains/tube/codac2_SliceVector.h b/src/core/2/domains/tube/codac2_SliceVector.h index ba90b632b..6c60f2f15 100644 --- a/src/core/2/domains/tube/codac2_SliceVector.h +++ b/src/core/2/domains/tube/codac2_SliceVector.h @@ -68,8 +68,8 @@ namespace codac2 IntervalVector input_gate() const; IntervalVector output_gate() const; - void set(const IntervalVector& codomain); - void set_component(size_t i, const Interval& x); + void set(const IntervalVector& x); + void set_component(size_t i, const Interval& xi); friend std::ostream& operator<<(std::ostream& os, const SliceVector& x); From 914cddc64b452e370a27a0bed0e87b1ce016153e Mon Sep 17 00:00:00 2001 From: SimonRohou Date: Mon, 13 Jun 2022 19:17:55 +0200 Subject: [PATCH 020/256] [c++] examples now running in C++17 --- examples/basics/01_arithmetic/CMakeLists.txt | 3 +++ examples/basics/02_simple_ctc/CMakeLists.txt | 3 +++ examples/basics/03_ivp_msinx/CMakeLists.txt | 3 +++ examples/basics/04_simple_eval/CMakeLists.txt | 3 +++ examples/basics/05_graphics/CMakeLists.txt | 3 +++ examples/basics/06_ctc_comparisons/CMakeLists.txt | 3 +++ examples/basics/07_temporal_ctc/CMakeLists.txt | 3 +++ examples/basics/08_tube_paving/CMakeLists.txt | 3 +++ examples/basics/09_cn_paving/CMakeLists.txt | 3 +++ examples/brunovsky/CMakeLists.txt | 3 +++ examples/codac2/01/CMakeLists.txt | 5 ++++- examples/lie_group/05_loc/CMakeLists.txt | 3 +++ examples/linobs/01_paper/CMakeLists.txt | 3 +++ examples/robotics/01_causal_chain/CMakeLists.txt | 3 +++ examples/robotics/03_drifting_clock/CMakeLists.txt | 3 +++ examples/robotics/04_redermor_traj/CMakeLists.txt | 3 +++ examples/robotics/05_loops_detec/CMakeLists.txt | 3 +++ examples/robotics/06_loops_proofs/CMakeLists.txt | 3 +++ examples/robotics/07_dynloc/CMakeLists.txt | 3 +++ examples/robotics/08_tubepaving/CMakeLists.txt | 3 +++ examples/robotics/09_lissajous/CMakeLists.txt | 3 +++ examples/robotics/10_datasso/CMakeLists.txt | 3 +++ examples/robotics/11_explored_area/CMakeLists.txt | 3 +++ examples/tuto/01_getting_started/CMakeLists.txt | 3 +++ examples/tuto/02_static_rangeonly/CMakeLists.txt | 3 +++ examples/tuto/03_static_rangebearing/CMakeLists.txt | 3 +++ examples/tuto/04_dyn_rangeonly/CMakeLists.txt | 3 +++ examples/tuto/05_dyn_rangebearing/CMakeLists.txt | 3 +++ 28 files changed, 85 insertions(+), 1 deletion(-) diff --git a/examples/basics/01_arithmetic/CMakeLists.txt b/examples/basics/01_arithmetic/CMakeLists.txt index 1891afbb6..95a4f02e5 100644 --- a/examples/basics/01_arithmetic/CMakeLists.txt +++ b/examples/basics/01_arithmetic/CMakeLists.txt @@ -5,6 +5,9 @@ cmake_minimum_required(VERSION 3.0.2) project(codac_basics_01 LANGUAGES CXX) + set(CMAKE_CXX_STANDARD 17) + set(CMAKE_CXX_STANDARD_REQUIRED ON) + # Adding IBEX # In case you installed IBEX in a local directory, you need diff --git a/examples/basics/02_simple_ctc/CMakeLists.txt b/examples/basics/02_simple_ctc/CMakeLists.txt index 6c9f32d38..44bd9fba5 100644 --- a/examples/basics/02_simple_ctc/CMakeLists.txt +++ b/examples/basics/02_simple_ctc/CMakeLists.txt @@ -5,6 +5,9 @@ cmake_minimum_required(VERSION 3.0.2) project(codac_basics_02 LANGUAGES CXX) + set(CMAKE_CXX_STANDARD 17) + set(CMAKE_CXX_STANDARD_REQUIRED ON) + # Adding IBEX # In case you installed IBEX in a local directory, you need diff --git a/examples/basics/03_ivp_msinx/CMakeLists.txt b/examples/basics/03_ivp_msinx/CMakeLists.txt index f4d63550d..6449af029 100644 --- a/examples/basics/03_ivp_msinx/CMakeLists.txt +++ b/examples/basics/03_ivp_msinx/CMakeLists.txt @@ -5,6 +5,9 @@ cmake_minimum_required(VERSION 3.0.2) project(codac_basics_03 LANGUAGES CXX) + set(CMAKE_CXX_STANDARD 17) + set(CMAKE_CXX_STANDARD_REQUIRED ON) + # Adding IBEX # In case you installed IBEX in a local directory, you need diff --git a/examples/basics/04_simple_eval/CMakeLists.txt b/examples/basics/04_simple_eval/CMakeLists.txt index 189540e73..239e47fa0 100644 --- a/examples/basics/04_simple_eval/CMakeLists.txt +++ b/examples/basics/04_simple_eval/CMakeLists.txt @@ -5,6 +5,9 @@ cmake_minimum_required(VERSION 3.0.2) project(codac_basics_04 LANGUAGES CXX) + set(CMAKE_CXX_STANDARD 17) + set(CMAKE_CXX_STANDARD_REQUIRED ON) + # Adding IBEX # In case you installed IBEX in a local directory, you need diff --git a/examples/basics/05_graphics/CMakeLists.txt b/examples/basics/05_graphics/CMakeLists.txt index b03a30669..0fb388410 100644 --- a/examples/basics/05_graphics/CMakeLists.txt +++ b/examples/basics/05_graphics/CMakeLists.txt @@ -5,6 +5,9 @@ cmake_minimum_required(VERSION 3.0.2) project(codac_basics_05 LANGUAGES CXX) + set(CMAKE_CXX_STANDARD 17) + set(CMAKE_CXX_STANDARD_REQUIRED ON) + # Adding IBEX # In case you installed IBEX in a local directory, you need diff --git a/examples/basics/06_ctc_comparisons/CMakeLists.txt b/examples/basics/06_ctc_comparisons/CMakeLists.txt index 885a8e847..36b72c8b0 100644 --- a/examples/basics/06_ctc_comparisons/CMakeLists.txt +++ b/examples/basics/06_ctc_comparisons/CMakeLists.txt @@ -5,6 +5,9 @@ cmake_minimum_required(VERSION 3.0.2) project(codac_basics_06 LANGUAGES CXX) + set(CMAKE_CXX_STANDARD 17) + set(CMAKE_CXX_STANDARD_REQUIRED ON) + # Adding IBEX # In case you installed IBEX in a local directory, you need diff --git a/examples/basics/07_temporal_ctc/CMakeLists.txt b/examples/basics/07_temporal_ctc/CMakeLists.txt index acfb11f37..bf29a2288 100644 --- a/examples/basics/07_temporal_ctc/CMakeLists.txt +++ b/examples/basics/07_temporal_ctc/CMakeLists.txt @@ -5,6 +5,9 @@ cmake_minimum_required(VERSION 3.0.2) project(codac_basics_07 LANGUAGES CXX) + set(CMAKE_CXX_STANDARD 17) + set(CMAKE_CXX_STANDARD_REQUIRED ON) + # Adding IBEX # In case you installed IBEX in a local directory, you need diff --git a/examples/basics/08_tube_paving/CMakeLists.txt b/examples/basics/08_tube_paving/CMakeLists.txt index a78dc8995..ea7bb6f79 100644 --- a/examples/basics/08_tube_paving/CMakeLists.txt +++ b/examples/basics/08_tube_paving/CMakeLists.txt @@ -5,6 +5,9 @@ cmake_minimum_required(VERSION 3.0.2) project(codac_basics_08 LANGUAGES CXX) + set(CMAKE_CXX_STANDARD 17) + set(CMAKE_CXX_STANDARD_REQUIRED ON) + # Adding IBEX # In case you installed IBEX in a local directory, you need diff --git a/examples/basics/09_cn_paving/CMakeLists.txt b/examples/basics/09_cn_paving/CMakeLists.txt index bdc4b90c6..e8ddd8f56 100644 --- a/examples/basics/09_cn_paving/CMakeLists.txt +++ b/examples/basics/09_cn_paving/CMakeLists.txt @@ -5,6 +5,9 @@ cmake_minimum_required(VERSION 3.0.2) project(codac_basics_09 LANGUAGES CXX) + set(CMAKE_CXX_STANDARD 17) + set(CMAKE_CXX_STANDARD_REQUIRED ON) + # Adding IBEX # In case you installed IBEX in a local directory, you need diff --git a/examples/brunovsky/CMakeLists.txt b/examples/brunovsky/CMakeLists.txt index e4e4bd373..ed4ee820d 100755 --- a/examples/brunovsky/CMakeLists.txt +++ b/examples/brunovsky/CMakeLists.txt @@ -5,6 +5,9 @@ cmake_minimum_required(VERSION 3.0.2) project(codac_brunov LANGUAGES CXX) + set(CMAKE_CXX_STANDARD 17) + set(CMAKE_CXX_STANDARD_REQUIRED ON) + # Compilation options set(CMAKE_CXX_STANDARD 17) diff --git a/examples/codac2/01/CMakeLists.txt b/examples/codac2/01/CMakeLists.txt index 40d8f265c..143ab46eb 100644 --- a/examples/codac2/01/CMakeLists.txt +++ b/examples/codac2/01/CMakeLists.txt @@ -5,6 +5,9 @@ cmake_minimum_required(VERSION 3.0.2) project(codac_basics_01 LANGUAGES CXX) + set(CMAKE_CXX_STANDARD 17) + set(CMAKE_CXX_STANDARD_REQUIRED ON) + # Adding IBEX # In case you installed IBEX in a local directory, you need @@ -35,7 +38,7 @@ # Compilation - add_executable(${PROJECT_NAME} main.cpp) + add_executable(${PROJECT_NAME} main_test_sample.cpp) target_compile_options(${PROJECT_NAME} PUBLIC ${CODAC_CXX_FLAGS}) target_include_directories(${PROJECT_NAME} SYSTEM PUBLIC ${CODAC_INCLUDE_DIRS} ${EIGEN3_INCLUDE_DIRS}) target_link_libraries(${PROJECT_NAME} PUBLIC ${CODAC_LIBRARIES} Ibex::ibex ${CODAC_LIBRARIES} Ibex::ibex) \ No newline at end of file diff --git a/examples/lie_group/05_loc/CMakeLists.txt b/examples/lie_group/05_loc/CMakeLists.txt index 2be59bfb3..5b982904b 100644 --- a/examples/lie_group/05_loc/CMakeLists.txt +++ b/examples/lie_group/05_loc/CMakeLists.txt @@ -5,6 +5,9 @@ cmake_minimum_required(VERSION 3.0.2) project(codac_lie_05 LANGUAGES CXX) + set(CMAKE_CXX_STANDARD 17) + set(CMAKE_CXX_STANDARD_REQUIRED ON) + # Adding IBEX # In case you installed IBEX in a local directory, you need diff --git a/examples/linobs/01_paper/CMakeLists.txt b/examples/linobs/01_paper/CMakeLists.txt index 8e7e15165..ed3131762 100644 --- a/examples/linobs/01_paper/CMakeLists.txt +++ b/examples/linobs/01_paper/CMakeLists.txt @@ -5,6 +5,9 @@ cmake_minimum_required(VERSION 3.0.2) project(codac_linobs LANGUAGES CXX) + set(CMAKE_CXX_STANDARD 17) + set(CMAKE_CXX_STANDARD_REQUIRED ON) + # Compilation options set(CMAKE_CXX_STANDARD 17) diff --git a/examples/robotics/01_causal_chain/CMakeLists.txt b/examples/robotics/01_causal_chain/CMakeLists.txt index 5bc663c25..52cdeeabc 100644 --- a/examples/robotics/01_causal_chain/CMakeLists.txt +++ b/examples/robotics/01_causal_chain/CMakeLists.txt @@ -5,6 +5,9 @@ cmake_minimum_required(VERSION 3.0.2) project(codac_rob_01 LANGUAGES CXX) + set(CMAKE_CXX_STANDARD 17) + set(CMAKE_CXX_STANDARD_REQUIRED ON) + # Adding IBEX # In case you installed IBEX in a local directory, you need diff --git a/examples/robotics/03_drifting_clock/CMakeLists.txt b/examples/robotics/03_drifting_clock/CMakeLists.txt index 547b492f9..3555c5cd8 100644 --- a/examples/robotics/03_drifting_clock/CMakeLists.txt +++ b/examples/robotics/03_drifting_clock/CMakeLists.txt @@ -5,6 +5,9 @@ cmake_minimum_required(VERSION 3.0.2) project(codac_rob_03 LANGUAGES CXX) + set(CMAKE_CXX_STANDARD 17) + set(CMAKE_CXX_STANDARD_REQUIRED ON) + # Adding IBEX # In case you installed IBEX in a local directory, you need diff --git a/examples/robotics/04_redermor_traj/CMakeLists.txt b/examples/robotics/04_redermor_traj/CMakeLists.txt index 48564d05e..e60e217cc 100644 --- a/examples/robotics/04_redermor_traj/CMakeLists.txt +++ b/examples/robotics/04_redermor_traj/CMakeLists.txt @@ -5,6 +5,9 @@ cmake_minimum_required(VERSION 3.0.2) project(codac_rob_04 LANGUAGES CXX) + set(CMAKE_CXX_STANDARD 17) + set(CMAKE_CXX_STANDARD_REQUIRED ON) + # Adding IBEX # In case you installed IBEX in a local directory, you need diff --git a/examples/robotics/05_loops_detec/CMakeLists.txt b/examples/robotics/05_loops_detec/CMakeLists.txt index e87c167a1..6502c2225 100644 --- a/examples/robotics/05_loops_detec/CMakeLists.txt +++ b/examples/robotics/05_loops_detec/CMakeLists.txt @@ -5,6 +5,9 @@ cmake_minimum_required(VERSION 3.0.2) project(codac_rob_05 LANGUAGES CXX) + set(CMAKE_CXX_STANDARD 17) + set(CMAKE_CXX_STANDARD_REQUIRED ON) + # Adding IBEX # In case you installed IBEX in a local directory, you need diff --git a/examples/robotics/06_loops_proofs/CMakeLists.txt b/examples/robotics/06_loops_proofs/CMakeLists.txt index a96881fba..e8c781795 100644 --- a/examples/robotics/06_loops_proofs/CMakeLists.txt +++ b/examples/robotics/06_loops_proofs/CMakeLists.txt @@ -5,6 +5,9 @@ cmake_minimum_required(VERSION 3.0.2) project(codac_rob_06 LANGUAGES CXX) + set(CMAKE_CXX_STANDARD 17) + set(CMAKE_CXX_STANDARD_REQUIRED ON) + # Adding IBEX # In case you installed IBEX in a local directory, you need diff --git a/examples/robotics/07_dynloc/CMakeLists.txt b/examples/robotics/07_dynloc/CMakeLists.txt index 93be85b80..e8197444b 100644 --- a/examples/robotics/07_dynloc/CMakeLists.txt +++ b/examples/robotics/07_dynloc/CMakeLists.txt @@ -5,6 +5,9 @@ cmake_minimum_required(VERSION 3.0.2) project(codac_rob_07 LANGUAGES CXX) + set(CMAKE_CXX_STANDARD 17) + set(CMAKE_CXX_STANDARD_REQUIRED ON) + # Adding IBEX # In case you installed IBEX in a local directory, you need diff --git a/examples/robotics/08_tubepaving/CMakeLists.txt b/examples/robotics/08_tubepaving/CMakeLists.txt index 68bb93c1b..34af68eb5 100644 --- a/examples/robotics/08_tubepaving/CMakeLists.txt +++ b/examples/robotics/08_tubepaving/CMakeLists.txt @@ -5,6 +5,9 @@ cmake_minimum_required(VERSION 3.0.2) project(codac_rob_08 LANGUAGES CXX) + set(CMAKE_CXX_STANDARD 17) + set(CMAKE_CXX_STANDARD_REQUIRED ON) + # Adding IBEX # In case you installed IBEX in a local directory, you need diff --git a/examples/robotics/09_lissajous/CMakeLists.txt b/examples/robotics/09_lissajous/CMakeLists.txt index 58cee6e1d..2243131e0 100644 --- a/examples/robotics/09_lissajous/CMakeLists.txt +++ b/examples/robotics/09_lissajous/CMakeLists.txt @@ -5,6 +5,9 @@ cmake_minimum_required(VERSION 3.0.2) project(codac_rob_09 LANGUAGES CXX) + set(CMAKE_CXX_STANDARD 17) + set(CMAKE_CXX_STANDARD_REQUIRED ON) + # Adding IBEX # In case you installed IBEX in a local directory, you need diff --git a/examples/robotics/10_datasso/CMakeLists.txt b/examples/robotics/10_datasso/CMakeLists.txt index 94196110d..5b0b6ee00 100644 --- a/examples/robotics/10_datasso/CMakeLists.txt +++ b/examples/robotics/10_datasso/CMakeLists.txt @@ -5,6 +5,9 @@ cmake_minimum_required(VERSION 3.0.2) project(codac_rob_10 LANGUAGES CXX) + set(CMAKE_CXX_STANDARD 17) + set(CMAKE_CXX_STANDARD_REQUIRED ON) + # Adding IBEX # In case you installed IBEX in a local directory, you need diff --git a/examples/robotics/11_explored_area/CMakeLists.txt b/examples/robotics/11_explored_area/CMakeLists.txt index 02561f5f6..b294df7d5 100644 --- a/examples/robotics/11_explored_area/CMakeLists.txt +++ b/examples/robotics/11_explored_area/CMakeLists.txt @@ -5,6 +5,9 @@ cmake_minimum_required(VERSION 3.0.2) project(codac_rob_11 LANGUAGES CXX) + set(CMAKE_CXX_STANDARD 17) + set(CMAKE_CXX_STANDARD_REQUIRED ON) + # Adding IBEX # In case you installed IBEX in a local directory, you need diff --git a/examples/tuto/01_getting_started/CMakeLists.txt b/examples/tuto/01_getting_started/CMakeLists.txt index e4c08cf53..d7b18c2de 100644 --- a/examples/tuto/01_getting_started/CMakeLists.txt +++ b/examples/tuto/01_getting_started/CMakeLists.txt @@ -5,6 +5,9 @@ cmake_minimum_required(VERSION 3.0.2) project(01_getting_started LANGUAGES CXX) + set(CMAKE_CXX_STANDARD 17) + set(CMAKE_CXX_STANDARD_REQUIRED ON) + # Adding IBEX # In case you installed IBEX in a local directory, you need diff --git a/examples/tuto/02_static_rangeonly/CMakeLists.txt b/examples/tuto/02_static_rangeonly/CMakeLists.txt index da0ba5d99..4276189be 100644 --- a/examples/tuto/02_static_rangeonly/CMakeLists.txt +++ b/examples/tuto/02_static_rangeonly/CMakeLists.txt @@ -5,6 +5,9 @@ cmake_minimum_required(VERSION 3.0.2) project(02_static_rangeonly LANGUAGES CXX) + set(CMAKE_CXX_STANDARD 17) + set(CMAKE_CXX_STANDARD_REQUIRED ON) + # Adding IBEX # In case you installed IBEX in a local directory, you need diff --git a/examples/tuto/03_static_rangebearing/CMakeLists.txt b/examples/tuto/03_static_rangebearing/CMakeLists.txt index 5ee96f0dd..0496fd765 100644 --- a/examples/tuto/03_static_rangebearing/CMakeLists.txt +++ b/examples/tuto/03_static_rangebearing/CMakeLists.txt @@ -5,6 +5,9 @@ cmake_minimum_required(VERSION 3.0.2) project(03_static_rangebearing LANGUAGES CXX) + set(CMAKE_CXX_STANDARD 17) + set(CMAKE_CXX_STANDARD_REQUIRED ON) + # Adding IBEX # In case you installed IBEX in a local directory, you need diff --git a/examples/tuto/04_dyn_rangeonly/CMakeLists.txt b/examples/tuto/04_dyn_rangeonly/CMakeLists.txt index 7bae7641b..5c94967f1 100644 --- a/examples/tuto/04_dyn_rangeonly/CMakeLists.txt +++ b/examples/tuto/04_dyn_rangeonly/CMakeLists.txt @@ -5,6 +5,9 @@ cmake_minimum_required(VERSION 3.0.2) project(04_dyn_rangeonly LANGUAGES CXX) + set(CMAKE_CXX_STANDARD 17) + set(CMAKE_CXX_STANDARD_REQUIRED ON) + # Adding IBEX # In case you installed IBEX in a local directory, you need diff --git a/examples/tuto/05_dyn_rangebearing/CMakeLists.txt b/examples/tuto/05_dyn_rangebearing/CMakeLists.txt index 8b512ca5b..5f25f65a1 100644 --- a/examples/tuto/05_dyn_rangebearing/CMakeLists.txt +++ b/examples/tuto/05_dyn_rangebearing/CMakeLists.txt @@ -5,6 +5,9 @@ cmake_minimum_required(VERSION 3.0.2) project(05_dyn_rangebearing LANGUAGES CXX) + set(CMAKE_CXX_STANDARD 17) + set(CMAKE_CXX_STANDARD_REQUIRED ON) + # Adding IBEX # In case you installed IBEX in a local directory, you need From 0a9af47defece6e0e5b3dfcb5e534a79bae6fe11 Mon Sep 17 00:00:00 2001 From: SimonRohou Date: Tue, 21 Jun 2022 14:34:51 +0200 Subject: [PATCH 021/256] [sivia] minor update --- src/core/sivia/codac_sivia.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/core/sivia/codac_sivia.cpp b/src/core/sivia/codac_sivia.cpp index f3c311c5a..8f0cc435a 100644 --- a/src/core/sivia/codac_sivia.cpp +++ b/src/core/sivia/codac_sivia.cpp @@ -114,6 +114,8 @@ namespace codac else { + if(display_result) + vibes::drawBox(x.subvector(0,1), "lightGray"); pair p = bisector.bisect(x); stack.push_back(p.first); stack.push_back(p.second); From 191fc940212562f9d9277041a0c271586c0d487e Mon Sep 17 00:00:00 2001 From: SimonRohou Date: Fri, 29 Jul 2022 23:30:02 +0200 Subject: [PATCH 022/256] [tube] new templated tubes --- examples/codac2/01/main.cpp | 4 +- examples/codac2/01/main_test_sample.cpp | 33 ++ .../2/contractors/codac2_CtcDiffInclusion.cpp | 11 +- .../2/contractors/codac2_CtcDiffInclusion.h | 10 +- ...act_const.h => codac2_AbstractConstTube.h} | 35 +- .../2/domains/tube/codac2_AbstractSlice.cpp | 50 ++ .../2/domains/tube/codac2_AbstractSlice.h | 56 +++ ...onst.cpp => codac2_AbstractSlicedTube.cpp} | 20 +- .../domains/tube/codac2_AbstractSlicedTube.h | 40 ++ src/core/2/domains/tube/codac2_Slice.h | 177 +++++++ .../2/domains/tube/codac2_SliceVector.cpp | 178 ------- src/core/2/domains/tube/codac2_SliceVector.h | 92 ---- src/core/2/domains/tube/codac2_TDomain.cpp | 18 +- src/core/2/domains/tube/codac2_TDomain.h | 14 +- src/core/2/domains/tube/codac2_TSlice.cpp | 11 +- src/core/2/domains/tube/codac2_TSlice.h | 18 +- src/core/2/domains/tube/codac2_Tube.h | 454 ++++++++++++++++++ src/core/2/domains/tube/codac2_TubeVector.cpp | 196 -------- src/core/2/domains/tube/codac2_TubeVector.h | 137 ------ .../tube/codac2_TubeVectorComponent.cpp | 108 ----- .../domains/tube/codac2_TubeVectorComponent.h | 66 --- .../tube/codac2_TubeVectorEvaluation.cpp | 60 --- .../tube/codac2_TubeVectorEvaluation.h | 49 -- src/core/CMakeLists.txt | 21 +- tests/core/tests_codac2_tubes.cpp | 140 +++++- 25 files changed, 1014 insertions(+), 984 deletions(-) create mode 100644 examples/codac2/01/main_test_sample.cpp rename src/core/2/domains/tube/{codac2_TubeAbstract_const.h => codac2_AbstractConstTube.h} (58%) create mode 100644 src/core/2/domains/tube/codac2_AbstractSlice.cpp create mode 100644 src/core/2/domains/tube/codac2_AbstractSlice.h rename src/core/2/domains/tube/{codac2_TubeAbstract_const.cpp => codac2_AbstractSlicedTube.cpp} (55%) create mode 100644 src/core/2/domains/tube/codac2_AbstractSlicedTube.h create mode 100644 src/core/2/domains/tube/codac2_Slice.h delete mode 100644 src/core/2/domains/tube/codac2_SliceVector.cpp delete mode 100644 src/core/2/domains/tube/codac2_SliceVector.h create mode 100644 src/core/2/domains/tube/codac2_Tube.h delete mode 100644 src/core/2/domains/tube/codac2_TubeVector.cpp delete mode 100644 src/core/2/domains/tube/codac2_TubeVector.h delete mode 100644 src/core/2/domains/tube/codac2_TubeVectorComponent.cpp delete mode 100644 src/core/2/domains/tube/codac2_TubeVectorComponent.h delete mode 100644 src/core/2/domains/tube/codac2_TubeVectorEvaluation.cpp delete mode 100644 src/core/2/domains/tube/codac2_TubeVectorEvaluation.h diff --git a/examples/codac2/01/main.cpp b/examples/codac2/01/main.cpp index 970deb7ff..4e9f2dc15 100644 --- a/examples/codac2/01/main.cpp +++ b/examples/codac2/01/main.cpp @@ -6,9 +6,9 @@ using namespace codac; int main() { codac2::TDomain tdomain(Interval(0,10), 0.01, true); // last argument creates "gates" (degenerated slices at scalar timesteps) - codac2::TubeVector x(2, tdomain, + codac2::Tube x(2, tdomain, TFunction("(sin(sqrt(t)+((t-5)^2)*[-0.01,0.01]) ; cos(t)+sin(t/0.2)*[-0.1,0.1])")); - codac2::TubeVector u(2, tdomain); + codac2::Tube u(2, tdomain); TFunction tf("x[2]", "u[2]", "(sin(x[1]) ; -sin(x[0]))"); codac2::CtcDiffInclusion ctc_diffincl(tf); diff --git a/examples/codac2/01/main_test_sample.cpp b/examples/codac2/01/main_test_sample.cpp new file mode 100644 index 000000000..e88470fea --- /dev/null +++ b/examples/codac2/01/main_test_sample.cpp @@ -0,0 +1,33 @@ +#include + +using namespace std; +using namespace codac; + +int main() +{ + { + codac2::TDomain tdomain(Interval(0,10), 1., false); + codac2::Tube x(2, tdomain); + + for(auto& sx : x) + { + if(sx.t0_tf().contains(5.2)) + { + cout << "sample" << endl; + tdomain.sample(5.2); + } + cout << sx << endl; + } + } + + { + TFunction f("x1", "x2", "u1", "u2", "(t+u1+x1;t+u2+x2)"); + IntervalVector x({{2,3},{5,6}}); + IntervalVector u({{0,0.1},{0,0.1}}); + Interval t(5.); + cout << f.eval_vector(t,x,u) << endl; + } + + + return EXIT_SUCCESS; +} \ No newline at end of file diff --git a/src/core/2/contractors/codac2_CtcDiffInclusion.cpp b/src/core/2/contractors/codac2_CtcDiffInclusion.cpp index 96e4641b4..d68dfdc3f 100644 --- a/src/core/2/contractors/codac2_CtcDiffInclusion.cpp +++ b/src/core/2/contractors/codac2_CtcDiffInclusion.cpp @@ -26,7 +26,7 @@ namespace codac2 return _f; } - void CtcDiffInclusion::contract(TubeVector& x, const TubeVector& u, TimePropag t_propa) + void CtcDiffInclusion::contract(Tube& x, const Tube& u, TimePropag t_propa) { // Verifying that x and u share exactly the same tdomain and slicing: assert(&x.tdomain() == &u.tdomain()); @@ -40,8 +40,9 @@ namespace codac2 continue; // su is a SliceVector of the TubeVector u: - const SliceVector& su = sx.tslice().slices().at(&u); - const double dt = sx.t0_tf().diam(); + const shared_ptr> su = static_pointer_cast>(sx.tslice().slices().at(&u)); + + //const double dt = sx.t0_tf().diam(); // sx.set(su.codomain()); @@ -63,7 +64,7 @@ namespace codac2 } } - void CtcDiffInclusion::contract(SliceVector& x, const SliceVector& u, TimePropag t_propa) + void CtcDiffInclusion::contract(Slice& x, const Slice& u, TimePropag t_propa) { // Verifying that x and u share exactly the same tdomain assert(&x.tslice() == &u.tslice()); @@ -71,7 +72,7 @@ namespace codac2 assert((size_t)_f.nb_var() == 2); assert((size_t)_f.image_dim() == x.size()); - const double dt = x.t0_tf().diam(); + //const double dt = x.t0_tf().diam(); // ... diff --git a/src/core/2/contractors/codac2_CtcDiffInclusion.h b/src/core/2/contractors/codac2_CtcDiffInclusion.h index 558d28138..8ded524e5 100644 --- a/src/core/2/contractors/codac2_CtcDiffInclusion.h +++ b/src/core/2/contractors/codac2_CtcDiffInclusion.h @@ -14,8 +14,8 @@ #include "codac_TFunction.h" #include "codac_DynCtc.h" -#include "codac2_TubeVector.h" -#include "codac2_SliceVector.h" +#include "codac2_Tube.h" +#include "codac2_Slice.h" namespace codac2 { @@ -31,13 +31,13 @@ namespace codac2 public: CtcDiffInclusion(const TFunction& t); - void contract(TubeVector& x, const TubeVector& u, TimePropag t_propa = TimePropag::FORWARD | TimePropag::BACKWARD); - void contract(SliceVector& x, const SliceVector& u, TimePropag t_propa = TimePropag::FORWARD | TimePropag::BACKWARD); + void contract(codac2::Tube& x, const codac2::Tube& u, TimePropag t_propa = TimePropag::FORWARD | TimePropag::BACKWARD); + void contract(Slice& x, const Slice& u, TimePropag t_propa = TimePropag::FORWARD | TimePropag::BACKWARD); const TFunction& f() const; protected: - friend class SliceVector; // to be removed + //friend class Slice; // to be removed const TFunction _f; }; } diff --git a/src/core/2/domains/tube/codac2_TubeAbstract_const.h b/src/core/2/domains/tube/codac2_AbstractConstTube.h similarity index 58% rename from src/core/2/domains/tube/codac2_TubeAbstract_const.h rename to src/core/2/domains/tube/codac2_AbstractConstTube.h index 8b8647bad..443799198 100644 --- a/src/core/2/domains/tube/codac2_TubeAbstract_const.h +++ b/src/core/2/domains/tube/codac2_AbstractConstTube.h @@ -9,8 +9,8 @@ * the GNU Lesser General Public License (LGPL). */ -#ifndef __CODAC2_TUBEVECTOR_CONST_H__ -#define __CODAC2_TUBEVECTOR_CONST_H__ +#ifndef __CODAC2_ABSTRACTCONSTTUBE_H__ +#define __CODAC2_ABSTRACTCONSTTUBE_H__ #include #include @@ -21,30 +21,29 @@ namespace codac2 { - using codac::Tube; using codac::Trajectory; using codac::TrajectoryVector; using codac::Interval; using codac::IntervalVector; - template - class TubeAbstract_const + template + class AbstractConstTube { public: - TubeAbstract_const() + AbstractConstTube() { } - explicit TubeAbstract_const(const T& x); + explicit AbstractConstTube(const T& x); virtual size_t size() const = 0; - virtual bool contains(const V& value) const = 0; + virtual bool contains(const TrajectoryVector& value) const = 0; virtual Interval t0_tf() const = 0; - virtual I codomain() const = 0; - // virtual I operator()(double t) const = 0; - //virtual I operator()(const Interval& t) const = 0; + virtual W codomain() const = 0; + // virtual W operator()(double t) const = 0; + //virtual W operator()(const Interval& t) const = 0; //TubeVectorComponent operator[](size_t index); //const TubeVectorComponent operator[](size_t index) const; @@ -58,20 +57,6 @@ namespace codac2 } }; - class Tube_const : public TubeAbstract_const - { - - }; - - class TubeVector_const : public TubeAbstract_const - { - - }; - - /*class TubeMatrix_const : public TubeAbstract_const - { - - };*/ } // namespace codac #endif \ No newline at end of file diff --git a/src/core/2/domains/tube/codac2_AbstractSlice.cpp b/src/core/2/domains/tube/codac2_AbstractSlice.cpp new file mode 100644 index 000000000..cb6d58324 --- /dev/null +++ b/src/core/2/domains/tube/codac2_AbstractSlice.cpp @@ -0,0 +1,50 @@ +/** + * \file + * + * ---------------------------------------------------------------------------- + * \date 2022 + * \author Simon Rohou + * \copyright Copyright 2022 Codac Team + * \license This program is distributed under the terms of + * the GNU Lesser General Public License (LGPL). + */ + +#include "codac2_AbstractSlice.h" +#include "codac2_TSlice.h" +#include "codac_Exception.h" + +using namespace std; + +namespace codac2 +{ + AbstractSlice::AbstractSlice(const AbstractSlicedTube& tubevector, const list::iterator& it_tslice) : + _tubevector(tubevector), _it_tslice(it_tslice) + { + + } + + const Interval& AbstractSlice::t0_tf() const + { + return _it_tslice->t0_tf(); + } + + const TSlice& AbstractSlice::tslice() const + { + return *_it_tslice; + } + + const std::shared_ptr AbstractSlice::prev_abstract_slice() const + { + if(&(*_tubevector.first_abstract_slice()) == this) + return nullptr; + return prev(_it_tslice)->slices().at(&_tubevector); + } + + const std::shared_ptr AbstractSlice::next_abstract_slice() const + { + if(&(*_tubevector.last_abstract_slice()) == this) + return nullptr; + return next(_it_tslice)->slices().at(&_tubevector); + } + +} // namespace codac \ No newline at end of file diff --git a/src/core/2/domains/tube/codac2_AbstractSlice.h b/src/core/2/domains/tube/codac2_AbstractSlice.h new file mode 100644 index 000000000..0a0164875 --- /dev/null +++ b/src/core/2/domains/tube/codac2_AbstractSlice.h @@ -0,0 +1,56 @@ +/** + * \file + * + * ---------------------------------------------------------------------------- + * \date 2022 + * \author Simon Rohou + * \copyright Copyright 2022 Codac Team + * \license This program is distributed under the terms of + * the GNU Lesser General Public License (LGPL). + */ + +#ifndef __CODAC2_ABSTRACTSLICE_H__ +#define __CODAC2_ABSTRACTSLICE_H__ + +#include +#include +#include +#include "codac_Interval.h" +#include "codac_IntervalVector.h" +#include "codac_TrajectoryVector.h" +#include "codac_Exception.h" +#include "codac2_AbstractSlicedTube.h" + +namespace codac2 +{ + using codac::Interval; + using codac::IntervalVector; + using codac::TrajectoryVector; + + class TSlice; + + class AbstractSlice + { + public: + + AbstractSlice(const AbstractSlicedTube& tubevector, const std::list::iterator& _it_tslice); + virtual std::shared_ptr duplicate() const = 0; + virtual size_t size() const = 0; + + const Interval& t0_tf() const; + const TSlice& tslice() const; + + const std::shared_ptr prev_abstract_slice() const; + const std::shared_ptr next_abstract_slice() const; + + + protected: + + const AbstractSlicedTube& _tubevector; + std::list::iterator _it_tslice; + + friend class TDomain; + }; +} + +#endif \ No newline at end of file diff --git a/src/core/2/domains/tube/codac2_TubeAbstract_const.cpp b/src/core/2/domains/tube/codac2_AbstractSlicedTube.cpp similarity index 55% rename from src/core/2/domains/tube/codac2_TubeAbstract_const.cpp rename to src/core/2/domains/tube/codac2_AbstractSlicedTube.cpp index ac4313983..713864216 100644 --- a/src/core/2/domains/tube/codac2_TubeAbstract_const.cpp +++ b/src/core/2/domains/tube/codac2_AbstractSlicedTube.cpp @@ -9,23 +9,25 @@ * the GNU Lesser General Public License (LGPL). */ -#include "codac2_TubeAbstract_const.h" +#include "codac2_AbstractSlicedTube.h" using namespace std; namespace codac2 { - /*template - TubeAbstract_const::TubeAbstract_const() + AbstractSlicedTube::AbstractSlicedTube(TDomain& tdomain) : + _tdomain(tdomain) { } - //ostream& operator<<(ostream& os, const TubeAbstract_const& x) - void TubeAbstract_const::print(ostream& os) const + TDomain& AbstractSlicedTube::tdomain() const { - os << tdomain() - << "↦" << codomain() - << flush; - }*/ + return _tdomain; + } + + Interval AbstractSlicedTube::t0_tf() const + { + return _tdomain.t0_tf(); + } } // namespace codac \ No newline at end of file diff --git a/src/core/2/domains/tube/codac2_AbstractSlicedTube.h b/src/core/2/domains/tube/codac2_AbstractSlicedTube.h new file mode 100644 index 000000000..5726bf538 --- /dev/null +++ b/src/core/2/domains/tube/codac2_AbstractSlicedTube.h @@ -0,0 +1,40 @@ +/** + * \file + * + * ---------------------------------------------------------------------------- + * \date 2022 + * \author Simon Rohou + * \copyright Copyright 2022 Codac Team + * \license This program is distributed under the terms of + * the GNU Lesser General Public License (LGPL). + */ + +#ifndef __CODAC2_ABSTRACTTUBE_H__ +#define __CODAC2_ABSTRACTTUBE_H__ + +#include "codac2_TDomain.h" + +namespace codac2 +{ + class AbstractSlice; + + class AbstractSlicedTube + { + public: + + AbstractSlicedTube(TDomain& tdomain); + + virtual const std::shared_ptr& first_abstract_slice() const = 0; + virtual const std::shared_ptr& last_abstract_slice() const = 0; + + TDomain& tdomain() const; + Interval t0_tf() const; + + + protected: + + TDomain& _tdomain; + }; +} // namespace codac + +#endif \ No newline at end of file diff --git a/src/core/2/domains/tube/codac2_Slice.h b/src/core/2/domains/tube/codac2_Slice.h new file mode 100644 index 000000000..39d121db2 --- /dev/null +++ b/src/core/2/domains/tube/codac2_Slice.h @@ -0,0 +1,177 @@ +/** + * \file + * + * ---------------------------------------------------------------------------- + * \date 2022 + * \author Simon Rohou + * \copyright Copyright 2022 Codac Team + * \license This program is distributed under the terms of + * the GNU Lesser General Public License (LGPL). + */ + +#ifndef __CODAC2_SLICE_H__ +#define __CODAC2_SLICE_H__ + +#include +#include +#include +#include "codac_Interval.h" +#include "codac_IntervalVector.h" +#include "codac_TrajectoryVector.h" +#include "codac_Exception.h" +#include "codac2_AbstractSlice.h" + +namespace codac2 +{ + using codac::Interval; + using codac::IntervalVector; + using codac::TrajectoryVector; + + class AbstractSlicedTube; + class TSlice; + + template + class Slice : public AbstractSlice + { + public: + + explicit Slice(size_t n, const AbstractSlicedTube& tube_vector, const std::list::iterator& it_tslice) : + Slice(T(n), tube_vector, it_tslice) + { + + } + + explicit Slice(const T& box, const AbstractSlicedTube& tube_vector, const std::list::iterator& it_tslice) : + AbstractSlice(tube_vector,it_tslice), _codomain(box) + { + + } + + Slice(const Slice& s) : + AbstractSlice(s._tubevector, s._it_tslice), _codomain(s._codomain) + { + + } + + ~Slice() + { + + } + + // Slice objects cannot be copyable or movable, + // as they are supposed to be connected to other Slice objects + Slice& operator=(const Slice&) = delete; + Slice(Slice&&) = delete; + Slice& operator=(Slice&&) = delete; + + const AbstractSlicedTube& tube_vector() const + { + return _tubevector; + } + + virtual std::shared_ptr duplicate() const + { + return std::make_shared(*this); + } + + virtual size_t size() const + { + return codomain().size(); + } + + bool is_gate() const + { + return t0_tf().is_degenerated(); + } + + bool is_empty() const + { + return input_gate().is_empty() || output_gate().is_empty(); + } + + bool is_unbounded() const + { + return codomain().is_unbounded(); + } + + bool contains(const TrajectoryVector& value) const + { + return true; // todo + } + + const std::shared_ptr> prev_slice() const + { + return std::static_pointer_cast>(prev_abstract_slice()); + } + + std::shared_ptr> prev_slice() + { + return std::const_pointer_cast>( + static_cast(*this).prev_slice()); + } + + const std::shared_ptr> next_slice() const + { + return std::static_pointer_cast>(next_abstract_slice()); + } + + std::shared_ptr> next_slice() + { + return std::const_pointer_cast>( + static_cast(*this).next_slice()); + } + + const T& codomain() const + { + return _codomain; + } + + T input_gate() const + { + T gate = codomain(); + if(prev_slice()) + gate &= prev_slice()->codomain(); + return gate; + } + + T output_gate() const + { + T gate = codomain(); + if(next_slice()) + gate &= next_slice()->codomain(); + return gate; + } + + void set(const T& x) + { + assert((size_t)codomain().size() == size()); + _codomain = x; + if(is_gate()) + _codomain &= prev_slice()->codomain() & next_slice()->codomain(); + } + + void set_component(size_t i, const Interval& xi) + { + assert((size_t)codomain().size() == size()); + _codomain[i] = xi; + if(is_gate()) + _codomain[i] &= prev_slice()->codomain()[i] & next_slice()->codomain()[i]; + } + + friend std::ostream& operator<<(std::ostream& os, const Slice& x) + { + os << x.t0_tf() + << "↦" << x.codomain() + << std::flush; + return os; + } + + + protected: + + T _codomain; + }; + +} // namespace codac + +#endif \ No newline at end of file diff --git a/src/core/2/domains/tube/codac2_SliceVector.cpp b/src/core/2/domains/tube/codac2_SliceVector.cpp deleted file mode 100644 index 963a4438f..000000000 --- a/src/core/2/domains/tube/codac2_SliceVector.cpp +++ /dev/null @@ -1,178 +0,0 @@ -/** - * \file - * - * ---------------------------------------------------------------------------- - * \date 2022 - * \author Simon Rohou - * \copyright Copyright 2022 Codac Team - * \license This program is distributed under the terms of - * the GNU Lesser General Public License (LGPL). - */ - -#include "codac2_TubeVector.h" -#include "codac2_SliceVector.h" -#include "codac_Exception.h" - -using namespace std; - -namespace codac2 -{ - SliceVector::SliceVector(size_t n, const TubeVector& tube_vector, const list::iterator& it_tslice) : - SliceVector(IntervalVector(n), tube_vector, it_tslice) - { - - } - - SliceVector::SliceVector(const IntervalVector& box, const TubeVector& tube_vector, const list::iterator& it_tslice) : - _tubevector(tube_vector), _it_tslice(it_tslice), _codomain(box) - { - assert(holds_alternative(_codomain)); - } - - SliceVector::SliceVector(const AbstractDomain& ad, const TubeVector& tube_vector, const list::iterator& it_tslice) : - _tubevector(tube_vector), _it_tslice(it_tslice), _codomain(ad) - { - assert(holds_alternative(_codomain)); - } - - SliceVector::SliceVector(const SliceVector& s) : - _tubevector(s._tubevector), _it_tslice(s._it_tslice), _codomain(s._codomain) - { - - } - - SliceVector::~SliceVector() - { - - } - - const TubeVector& SliceVector::tube_vector() const - { - return _tubevector; - } - - size_t SliceVector::size() const - { - return codomain().size(); - } - - bool SliceVector::is_gate() const - { - return t0_tf().is_degenerated(); - } - - bool SliceVector::is_empty() const - { - return input_gate().is_empty() || output_gate().is_empty(); - } - - bool SliceVector::is_unbounded() const - { - return codomain().is_unbounded(); - } - - bool SliceVector::contains(const TrajectoryVector& value) const - { - return true; - } - - const Interval& SliceVector::t0_tf() const - { - return _it_tslice->t0_tf(); - } - - const TSlice& SliceVector::tslice() const - { - return *_it_tslice; - } - - const SliceVector* SliceVector::prev_slice() const - { - if(&_tubevector.first_slice() == this) - return nullptr; - return &prev(_it_tslice)->slices().at(&_tubevector); - } - - SliceVector* SliceVector::prev_slice() - { - return const_cast( - static_cast(*this).prev_slice()); - } - - const SliceVector* SliceVector::next_slice() const - { - if(&_tubevector.last_slice() == this) - return nullptr; - return &next(_it_tslice)->slices().at(&_tubevector); - } - - SliceVector* SliceVector::next_slice() - { - return const_cast( - static_cast(*this).next_slice()); - } - - const IntervalVector SliceVector::codomain() const - { - if(holds_alternative(_codomain)) - return get(_codomain); - - else if(holds_alternative(_codomain)) - return get(_codomain).box(); - - assert(false && "unhandled case"); - return IntervalVector(0); // should not happen - } - - IntervalVector SliceVector::input_gate() const - { - IntervalVector gate = codomain(); - if(prev_slice()) - gate &= prev_slice()->codomain(); - return gate; - } - - IntervalVector SliceVector::output_gate() const - { - IntervalVector gate = codomain(); - if(next_slice()) - gate &= next_slice()->codomain(); - return gate; - } - - void SliceVector::set(const IntervalVector& x) - { - if(holds_alternative(_codomain)) - { - assert((size_t)codomain().size() == size()); - get(_codomain) = x; - if(is_gate()) - get(_codomain) &= prev_slice()->codomain() & next_slice()->codomain(); - } - - else - throw codac::Exception(__func__, "unable to set values for this AbstractDomain"); - } - - void SliceVector::set_component(size_t i, const Interval& xi) - { - if(holds_alternative(_codomain)) - { - assert((size_t)codomain().size() == size()); - get(_codomain)[i] = xi; - if(is_gate()) - get(_codomain)[i] &= prev_slice()->codomain()[i] & next_slice()->codomain()[i]; - } - - else - throw codac::Exception(__func__, "unable to set values for this AbstractDomain"); - } - - ostream& operator<<(ostream& os, const SliceVector& x) - { - os << x.t0_tf() - << "↦" << x.codomain() - << flush; - return os; - } -} // namespace codac \ No newline at end of file diff --git a/src/core/2/domains/tube/codac2_SliceVector.h b/src/core/2/domains/tube/codac2_SliceVector.h deleted file mode 100644 index 6c60f2f15..000000000 --- a/src/core/2/domains/tube/codac2_SliceVector.h +++ /dev/null @@ -1,92 +0,0 @@ -/** - * \file - * - * ---------------------------------------------------------------------------- - * \date 2022 - * \author Simon Rohou - * \copyright Copyright 2022 Codac Team - * \license This program is distributed under the terms of - * the GNU Lesser General Public License (LGPL). - */ - -#ifndef __CODAC2_SLICEVECTOR_H__ -#define __CODAC2_SLICEVECTOR_H__ - -#include -#include -#include "codac_Interval.h" -#include "codac_IntervalVector.h" -#include "codac_TrajectoryVector.h" -#include "codac2_TSlice.h" -#include "codac2_TDomain.h" -#include "codac2_AbstractDomain.h" - -namespace codac2 -{ - using codac::Interval; - using codac::IntervalVector; - using codac::TrajectoryVector; - - class TubeVector; - class TSlice; - - class SliceVector - { - public: - - explicit SliceVector(size_t n, const TubeVector& tube_vector, const std::list::iterator& it_tslice); - explicit SliceVector(const IntervalVector& box, const TubeVector& tube_vector, const std::list::iterator& it_tslice); - explicit SliceVector(const AbstractDomain& ad, const TubeVector& tube_vector, const std::list::iterator& it_tslice); - ~SliceVector(); - - SliceVector(const SliceVector& s); - - const TubeVector& tube_vector() const; - - // SliceVector objects cannot be copyable or movable, - // as they are supposed to be connected to other SliceVector objects - SliceVector& operator=(const SliceVector&) = delete; - SliceVector(SliceVector&&) = delete; - SliceVector& operator=(SliceVector&&) = delete; - - size_t size() const; - - bool is_gate() const; - bool is_empty() const; - bool is_unbounded() const; - bool contains(const TrajectoryVector& value) const; - - const SliceVector* prev_slice() const; - SliceVector* prev_slice(); - const SliceVector* next_slice() const; - SliceVector* next_slice(); - - const Interval& t0_tf() const; - const TSlice& tslice() const; - - const IntervalVector codomain() const; - IntervalVector input_gate() const; - IntervalVector output_gate() const; - - void set(const IntervalVector& x); - void set_component(size_t i, const Interval& xi); - - friend std::ostream& operator<<(std::ostream& os, const SliceVector& x); - - - protected: - - friend class TubeVector; - friend class TubeVectorComponent; - friend class TDomain; - friend class TSlice; - - const TubeVector& _tubevector; - std::list::iterator _it_tslice; - - // Several abstract domains related to guaranteed integration can be considered here - std::variant _codomain; - }; -} // namespace codac - -#endif \ No newline at end of file diff --git a/src/core/2/domains/tube/codac2_TDomain.cpp b/src/core/2/domains/tube/codac2_TDomain.cpp index e2a34f514..2c275dccc 100644 --- a/src/core/2/domains/tube/codac2_TDomain.cpp +++ b/src/core/2/domains/tube/codac2_TDomain.cpp @@ -10,8 +10,9 @@ */ #include +#include "codac2_TSlice.h" #include "codac2_TDomain.h" -#include "codac2_TubeVector.h" +#include "codac2_Slice.h" #include "codac_predef_values.h" using namespace std; @@ -19,6 +20,17 @@ using namespace codac; namespace codac2 { + TDomain::TDomain() + { + _tslices.push_back(TSlice(Interval())); + } + + TDomain::TDomain(const Interval& t0_tf, bool with_gates) : + TDomain(t0_tf, t0_tf.diam(), with_gates) + { + + } + TDomain::TDomain(const Interval& t0_tf, double dt, bool with_gates) { assert(!t0_tf.is_empty()); @@ -52,7 +64,7 @@ namespace codac2 size_t TDomain::nb_tubes() const { - return _tslices.front()._slices.size(); + return _tslices.front().slices().size(); } list::iterator TDomain::iterator_tslice(double t) @@ -88,7 +100,7 @@ namespace codac2 ++it; it = _tslices.insert(it, ts); for(auto& [k,s] : it->_slices) // adding the new iterator pointer to the new slices - s._it_tslice = it; + s->_it_tslice = it; return it; } diff --git a/src/core/2/domains/tube/codac2_TDomain.h b/src/core/2/domains/tube/codac2_TDomain.h index e3bd45dc3..382532f50 100644 --- a/src/core/2/domains/tube/codac2_TDomain.h +++ b/src/core/2/domains/tube/codac2_TDomain.h @@ -18,16 +18,18 @@ #include #include "codac_Interval.h" -#include "codac2_TSlice.h" namespace codac2 { using codac::Interval; + class TSlice; class TDomain { public: + explicit TDomain(); + explicit TDomain(const Interval& t0_tf, bool with_gates = false); explicit TDomain(const Interval& t0_tf, double dt, bool with_gates = false); const Interval t0_tf() const; // todo: keep this method? std::list::iterator iterator_tslice(double t); @@ -35,15 +37,15 @@ namespace codac2 size_t nb_tubes() const; std::list::iterator sample(double t, bool allow_gate = true); friend std::ostream& operator<<(std::ostream& os, const TDomain& x); + const std::list& tslices() const; protected: - - const std::list& tslices() const; - - friend class SliceVector; - friend class TubeVector; + std::list _tslices; + + template + friend class Tube; }; } // namespace codac diff --git a/src/core/2/domains/tube/codac2_TSlice.cpp b/src/core/2/domains/tube/codac2_TSlice.cpp index 12532624f..2e4e5e88c 100644 --- a/src/core/2/domains/tube/codac2_TSlice.cpp +++ b/src/core/2/domains/tube/codac2_TSlice.cpp @@ -10,8 +10,7 @@ */ #include "codac2_TSlice.h" -#include "codac2_SliceVector.h" -#include "codac2_TubeVector.h" +#include "codac2_Slice.h" using namespace std; using namespace codac; @@ -19,7 +18,7 @@ using namespace codac; namespace codac2 { TSlice::TSlice(const Interval& tdomain) : - _slices(map()) + _slices(map>()) { set_tdomain(tdomain); } @@ -28,8 +27,8 @@ namespace codac2 TSlice(tdomain) { for(const auto[k,s] : tslice._slices) - _slices.insert(pair( - k, SliceVector(s))); + _slices.insert(pair>( + k, s->duplicate())); } const Interval& TSlice::t0_tf() const @@ -43,7 +42,7 @@ namespace codac2 _t0_tf = tdomain; } - const map& TSlice::slices() const + const map>& TSlice::slices() const { return _slices; } diff --git a/src/core/2/domains/tube/codac2_TSlice.h b/src/core/2/domains/tube/codac2_TSlice.h index 5d92496a0..2ded9a5f7 100644 --- a/src/core/2/domains/tube/codac2_TSlice.h +++ b/src/core/2/domains/tube/codac2_TSlice.h @@ -18,13 +18,15 @@ #include #include "codac_Interval.h" +#include "codac2_Slice.h" namespace codac2 { using codac::Interval; - class SliceVector; - class TubeVector; + class TDomain; + class AbstractSlice; + class AbstractSlicedTube; class TSlice { @@ -33,19 +35,19 @@ namespace codac2 explicit TSlice(const Interval& tdomain); TSlice(const TSlice& tslice, const Interval& tdomain); // performs a deep copy on slices const Interval& t0_tf() const; - const std::map& slices() const; + const std::map>& slices() const; friend std::ostream& operator<<(std::ostream& os, const TSlice& x); protected: void set_tdomain(const Interval& tdomain); + + Interval _t0_tf; + std::map> _slices; - friend class TubeVector; - friend class TubeVectorComponent; - friend class TubeVectorEvaluation; friend class TDomain; - Interval _t0_tf; - std::map _slices; + template + friend class Tube; }; } // namespace codac diff --git a/src/core/2/domains/tube/codac2_Tube.h b/src/core/2/domains/tube/codac2_Tube.h new file mode 100644 index 000000000..7e9e66167 --- /dev/null +++ b/src/core/2/domains/tube/codac2_Tube.h @@ -0,0 +1,454 @@ +/** + * \file + * + * ---------------------------------------------------------------------------- + * \date 2022 + * \author Simon Rohou + * \copyright Copyright 2022 Codac Team + * \license This program is distributed under the terms of + * the GNU Lesser General Public License (LGPL). + */ + +#ifndef __CODAC2_TUBE_H__ +#define __CODAC2_TUBE_H__ + +#include +#include +#include "codac_TFnc.h" +#include "codac2_TSlice.h" +#include "codac_Tube.h" // to be removed +#include "codac2_AbstractSlicedTube.h" +#include "codac2_AbstractConstTube.h" +#include "codac2_TDomain.h" + +namespace codac2 +{ + using codac::TFnc; + + template + class Slice; + template + class TubeEvaluation; + template + class TubeComponent; + + + template + class Tube : public AbstractSlicedTube, public AbstractConstTube> + { + public: + + explicit Tube(size_t n, TDomain& tdomain) : + AbstractSlicedTube(tdomain) + { + for(std::list::iterator it = _tdomain._tslices.begin(); + it != _tdomain._tslices.end(); ++it) + { + it->_slices.insert( + std::pair>>(this, + std::make_shared>(n, *this, it))); + } + } + + explicit Tube(size_t n, TDomain& tdomain, const TFnc& f) : + Tube(n, tdomain) + { + assert(f.nb_var() == 0 && "function's inputs must be limited to system variable"); + assert((size_t)f.image_dim() == n); + + for(auto& s : *this) + s.set(f.eval_vector(s.t0_tf())); + } + + explicit Tube(const Tube& x) : + AbstractSlicedTube(x.tdomain()) + { + for(std::list::iterator it = _tdomain._tslices.begin(); + it != _tdomain._tslices.end(); ++it) + { + it->_slices.insert( + std::pair>>(this, + std::make_shared>(it->_slices.at(&x)->size(), *this, it))); + } + } + + ~Tube() + { + for(auto& s : _tdomain._tslices) + s._slices.erase(this); + } + + virtual Interval t0_tf() const + { + return AbstractSlicedTube::t0_tf(); + } + + size_t size() const + { + return first_slice().size(); + } + + size_t nb_slices() const + { + return _tdomain.nb_tslices(); + } + + virtual const std::shared_ptr& first_abstract_slice() const + { + return _tdomain.tslices().front().slices().at(this); + } + + virtual const std::shared_ptr& last_abstract_slice() const + { + return _tdomain.tslices().back().slices().at(this); + } + + const std::shared_ptr> first_slice_ptr() const + { + return std::static_pointer_cast>(first_abstract_slice()); + } + + const Slice& first_slice() const + { + return *first_slice_ptr(); + } + + Slice& first_slice() + { + return const_cast&>( + static_cast(*this).first_slice()); + } + + const std::shared_ptr> last_slice_ptr() const + { + return std::static_pointer_cast>(last_abstract_slice()); + } + + const Slice& last_slice() const + { + return *last_slice_ptr(); + } + + Slice& last_slice() + { + return const_cast&>( + static_cast(*this).last_slice()); + } + + bool is_empty() const + { + for(const auto& s : *this) + if(s.is_empty()) + return true; + return false; + } + + bool is_unbounded() const + { + for(const auto& s : *this) + if(s.is_unbounded()) + return true; + return false; + } + + bool contains(const TrajectoryVector& value) const + { + return true; + } + + IntervalVector codomain() const + { + IntervalVector codomain(size()); + codomain.set_empty(); + for(const auto& s : *this) + codomain |= s.codomain(); + return codomain; + } + + TubeEvaluation operator()(double t) + { + return TubeEvaluation(this, t); + } + + TubeEvaluation operator()(const Interval& t) + { + return TubeEvaluation(this, t); + } + + T eval(double t) const + { + return std::static_pointer_cast>(_tdomain.iterator_tslice(t)->_slices.at(this))->codomain(); + } + + T eval(const Interval& t) const + { + std::list::iterator it = _tdomain.iterator_tslice(t.lb()); + T codomain = std::static_pointer_cast>(it->_slices.at(this))->codomain(); + + while(it != _tdomain.iterator_tslice(t.ub())) + { + codomain |= std::static_pointer_cast>(it->_slices.at(this))->codomain(); + it++; + } + + return codomain; + } + + void set(const T& codomain) + { + assert((size_t)codomain.size() == size()); + for(auto& s : *this) + s.set(codomain); + } + + TubeComponent operator[](size_t i) + { + assert(i >= 0 && i < size()); + return TubeComponent(*this, i); + } + + codac::TubeVector to_codac1() const + { + codac::TubeVector x(t0_tf(), size()); + for(const auto& s : *this) + if(!s.t0_tf().is_unbounded()) + x.set(s.codomain(), s.t0_tf()); + for(const auto& s : *this) // setting gate (were overwritten) + if(s.t0_tf().is_degenerated()) + x.set(s.codomain(), s.t0_tf()); + return x; + } + + friend std::ostream& operator<<(std::ostream& os, const Tube& x) + { + x.AbstractConstTube>::print(os); + // Adding information related to sliced structure + os << ", " << x.nb_slices() + << " slice" << (x.nb_slices() > 1 ? "s" : "") + << std::flush; + return os; + } + + + public: + + using base_container = std::list; + + struct iterator : public base_container::iterator + { + using iterator_category = typename base_container::iterator::iterator_category; + using difference_type = typename base_container::iterator::difference_type; + + using value_type = Slice; + using pointer = Slice*; + using reference = Slice&; + + public: + + iterator(const Tube& tube_vector, base_container::iterator it) : + base_container::iterator(it), _tube_vector(tube_vector) { } + + reference operator*() + { + return static_cast(*((*this)->_slices.at(&_tube_vector))); + } + + protected: + + const Tube& _tube_vector; + }; + + iterator begin() { return iterator(*this, _tdomain._tslices.begin()); } + iterator end() { return iterator(*this, _tdomain._tslices.end()); } + + + struct const_iterator : public base_container::const_iterator + { + using iterator_category = typename base_container::const_iterator::iterator_category; + using difference_type = typename base_container::const_iterator::difference_type; + + using value_type = Slice; + using pointer = const Slice*; + using reference = const Slice&; + + public: + + const_iterator(const Tube& tube_vector, base_container::const_iterator it) : + base_container::const_iterator(it), _tube_vector(tube_vector) { } + + reference operator*() const + { + return static_cast(*((*this)->_slices.at(&_tube_vector))); + } + + protected: + + const Tube& _tube_vector; + }; + + const_iterator begin() const { return const_iterator(*this, _tdomain._tslices.cbegin()); } + const_iterator end() const { return const_iterator(*this, _tdomain._tslices.cend()); } + }; + + + template + class TubeEvaluation + { + public: + + const TubeEvaluation& operator=(const T& x) + { + // Sampling the tube only if affectation is performed + // (i.e. this is not done in the constructor) + std::list::iterator it_lb = _tubevector->_tdomain.sample(_t.lb(), false); + std::list::iterator it_ub = _tubevector->_tdomain.sample(_t.ub(), _t.is_degenerated()); + + do + { + std::static_pointer_cast>(it_lb->_slices.at(_tubevector))->set(x); + it_lb++; + } while(it_lb != it_ub); + + return *this; + } + + explicit operator T() + { + return _tubevector->eval(_t); + } + + friend std::ostream& operator<<(std::ostream& os, const TubeEvaluation& x) + { + os << x._tubevector->eval(x._t) << std::flush; + return os; + } + + + protected: + + explicit TubeEvaluation(const Tube* tubevector, double t) : + _t(Interval(t)), _tubevector(tubevector) + { + + } + + explicit TubeEvaluation(const Tube* tubevector, const Interval& t) : + _t(t), _tubevector(tubevector) + { + + } + + const Interval _t; + const Tube* _tubevector; + }; + + + template + class TubeComponent //: public AbstractConstTube> + { + protected: + + TubeComponent(Tube& tubevector, size_t i) : + _i(i), _tubevector(tubevector) + { + assert(i >= 0 && i < tubevector.size()); + } + + public: + + TubeComponent(const TubeComponent& tubevector_i) : + _i(tubevector_i._i), _tubevector(tubevector_i._tubevector) + { + + } + + size_t size() const + { + return 1; + } + + const TDomain& tdomain() const + { + return _tubevector.tdomain(); + } + + Interval t0_tf() const + { + return _tubevector.t0_tf(); + } + + Interval codomain() const + { + Interval codomain(Interval::EMPTY_SET); + for(const auto& s : _tubevector) + codomain |= s.codomain()[_i]; + return codomain; + } + + bool contains(const Trajectory& value) const + { + assert(false); + return true; + } + + void set(const Interval& codomain) + { + for(auto& s : _tubevector) + s.set_component(_i, codomain); + } + + const TubeComponent& operator=(const TubeComponent& x) + { + assert(&x.tdomain() == &tdomain()); + for(auto& s : _tubevector) + s.set_component(_i, std::static_pointer_cast>(s._it_tslice->_slices.at(&x._tubevector))->codomain()[x._i]); + return *this; + } + + const TubeComponent& operator=(std::pair,const TubeComponent> rel) + { + assert(&rel.second.tdomain() == &tdomain()); + for(auto& s : _tubevector) + s.set_component(_i, rel.first(std::static_pointer_cast>(s._it_tslice->_slices.at(&rel.second._tubevector))->codomain()[rel.second._i])); + return *this; + } + + friend std::ostream& operator<<(std::ostream& os, const TubeComponent& x) + { + os << "Component " << x._i << " of: " << x._tubevector << std::flush; + return os; + } + + std::pair,const TubeComponent> cos(const TubeComponent& x) + { + return std::make_pair(static_cast(ibex::cos), x); + } + + codac::Tube to_codac1() const + { + codac::Tube x(t0_tf()); + for(const auto& s : _tubevector) + if(!s.t0_tf().is_unbounded()) + x.set(s.codomain()[_i], s.t0_tf()); + for(const auto& s : _tubevector) // setting gate (were overwritten) + if(s.t0_tf().is_degenerated()) + x.set(s.codomain()[_i], s.t0_tf()); + return x; + } + + + protected: + + size_t _i; + Tube& _tubevector; + + template + friend class Tube; + }; + + template + std::pair,const TubeComponent> cos(const TubeComponent& x); + +} // namespace codac + +#endif \ No newline at end of file diff --git a/src/core/2/domains/tube/codac2_TubeVector.cpp b/src/core/2/domains/tube/codac2_TubeVector.cpp deleted file mode 100644 index 47a4d95bd..000000000 --- a/src/core/2/domains/tube/codac2_TubeVector.cpp +++ /dev/null @@ -1,196 +0,0 @@ -/** - * \file - * - * ---------------------------------------------------------------------------- - * \date 2022 - * \author Simon Rohou - * \copyright Copyright 2022 Codac Team - * \license This program is distributed under the terms of - * the GNU Lesser General Public License (LGPL). - */ - -#include -#include "codac2_TubeVector.h" -#include "codac2_SliceVector.h" -#include "codac2_TubeVectorComponent.h" - -using namespace std; - -namespace codac2 -{ - TubeVector::TubeVector(size_t n, TDomain& tdomain) : - _tdomain(tdomain) - { - for(list::iterator it = _tdomain._tslices.begin(); - it != _tdomain._tslices.end(); ++it) - { - it->_slices.insert( - pair(this, - SliceVector(n, *this, it))); - } - } - - TubeVector::TubeVector(size_t n, TDomain& tdomain, const TFnc& f) : - TubeVector(n, tdomain) - { - assert(f.nb_var() == 0 && "function's inputs must be limited to system variable"); - assert((size_t)f.image_dim() == n); - - for(auto& s : *this) - s.set(f.eval_vector(s.t0_tf())); - } - - TubeVector::TubeVector(const TubeVector& x) : - _tdomain(x.tdomain()) - { - for(list::iterator it = _tdomain._tslices.begin(); - it != _tdomain._tslices.end(); ++it) - { - it->_slices.insert( - pair(this, - SliceVector(it->_slices.at(&x).size(), *this, it))); - } - } - - TubeVector::~TubeVector() - { - for(auto& s : _tdomain._tslices) - s._slices.erase(this); - } - - size_t TubeVector::size() const - { - return first_slice().size(); - } - - size_t TubeVector::nb_slices() const - { - return _tdomain.nb_tslices(); - } - - const SliceVector& TubeVector::first_slice() const - { - return _tdomain.tslices().front().slices().at(this); - } - - SliceVector& TubeVector::first_slice() - { - return const_cast( - static_cast(*this).first_slice()); - } - - const SliceVector& TubeVector::last_slice() const - { - return _tdomain.tslices().back().slices().at(this); - } - - SliceVector& TubeVector::last_slice() - { - return const_cast( - static_cast(*this).last_slice()); - } - - bool TubeVector::is_empty() const - { - for(const auto& s : *this) - if(s.is_empty()) - return true; - return false; - } - - bool TubeVector::is_unbounded() const - { - for(const auto& s : *this) - if(s.is_unbounded()) - return true; - return false; - } - - bool TubeVector::contains(const TrajectoryVector& value) const - { - return true; - } - - TDomain& TubeVector::tdomain() const - { - return _tdomain; - } - - Interval TubeVector::t0_tf() const - { - return _tdomain.t0_tf(); - } - - IntervalVector TubeVector::codomain() const - { - IntervalVector codomain(size()); - codomain.set_empty(); - for(const auto& s : *this) - codomain |= s.codomain(); - return codomain; - } - - TubeVectorEvaluation TubeVector::operator()(double t) - { - return TubeVectorEvaluation(*this, t); - } - - TubeVectorEvaluation TubeVector::operator()(const Interval& t) - { - return TubeVectorEvaluation(*this, t); - } - - IntervalVector TubeVector::eval(double t) const - { - return _tdomain.iterator_tslice(t)->_slices.at(this).codomain(); - } - - IntervalVector TubeVector::eval(const Interval& t) const - { - list::iterator it = _tdomain.iterator_tslice(t.lb()); - IntervalVector codomain = it->_slices.at(this).codomain(); - - while(it != _tdomain.iterator_tslice(t.ub())) - { - codomain |= it->_slices.at(this).codomain(); - it++; - } - - return codomain; - } - - void TubeVector::set(const IntervalVector& codomain) - { - assert((size_t)codomain.size() == size()); - for(auto& s : *this) - s.set(codomain); - } - - TubeVectorComponent TubeVector::operator[](size_t i) - { - assert(i >= 0 && i < size()); - return TubeVectorComponent(*this, i); - } - - ostream& operator<<(ostream& os, const TubeVector& x) - { - x.TubeVector_const::print(os); - // Adding information related to sliced structure - os << ", " << x.nb_slices() - << " slice" << (x.nb_slices() > 1 ? "s" : "") - << flush; - return os; - } - - codac::TubeVector TubeVector::to_codac1() const - { - codac::TubeVector x(t0_tf(), size()); - for(const auto& s : *this) - if(!s.t0_tf().is_unbounded()) - x.set(s.codomain(), s.t0_tf()); - for(const auto& s : *this) // setting gate (were overwritten) - if(s.t0_tf().is_degenerated()) - x.set(s.codomain(), s.t0_tf()); - return x; - } -} // namespace codac \ No newline at end of file diff --git a/src/core/2/domains/tube/codac2_TubeVector.h b/src/core/2/domains/tube/codac2_TubeVector.h deleted file mode 100644 index 1b39810c8..000000000 --- a/src/core/2/domains/tube/codac2_TubeVector.h +++ /dev/null @@ -1,137 +0,0 @@ -/** - * \file - * - * ---------------------------------------------------------------------------- - * \date 2022 - * \author Simon Rohou - * \copyright Copyright 2022 Codac Team - * \license This program is distributed under the terms of - * the GNU Lesser General Public License (LGPL). - */ - -#ifndef __CODAC2_TUBEVECTOR_H__ -#define __CODAC2_TUBEVECTOR_H__ - -#include -#include -#include "codac_TFnc.h" -#include "codac2_TDomain.h" -#include "codac2_SliceVector.h" -#include "codac2_TubeVectorComponent.h" -#include "codac2_TubeVectorEvaluation.h" -#include "codac2_TubeAbstract_const.h" -#include "codac_TubeVector.h" // to be removed - -namespace codac2 -{ - using codac::TFnc; - class TDomain; - class TubeVectorComponent; - class TubeVectorEvaluation; - - class TubeVector : public TubeVector_const - { - public: - - explicit TubeVector(size_t n, TDomain& tdomain); - explicit TubeVector(size_t n, TDomain& tdomain, const TFnc& f); - explicit TubeVector(const TubeVector& x); - ~TubeVector(); - size_t size() const; - - size_t nb_slices() const; - const SliceVector& first_slice() const; - SliceVector& first_slice(); - const SliceVector& last_slice() const; - SliceVector& last_slice(); - - TubeVectorComponent operator[](size_t i); - //const TubeVectorComponent operator[](size_t i) const; - - bool is_empty() const; - bool is_unbounded() const; - bool contains(const TrajectoryVector& value) const; - - TDomain& tdomain() const; - Interval t0_tf() const; - IntervalVector codomain() const; - TubeVectorEvaluation operator()(double t); - TubeVectorEvaluation operator()(const Interval& t); - IntervalVector eval(double t) const; - IntervalVector eval(const Interval& t) const; - void set(const IntervalVector& codomain); - - friend std::ostream& operator<<(std::ostream& os, const TubeVector& x); - - codac::TubeVector to_codac1() const; // to be removed - - - protected: - - friend class TubeVectorComponent; - friend class TubeVectorEvaluation; - TDomain& _tdomain; - - - public: - - using base_container = std::list; - - struct iterator : public base_container::iterator - { - using iterator_category = typename base_container::iterator::iterator_category; - using difference_type = typename base_container::iterator::difference_type; - - using value_type = SliceVector; - using pointer = SliceVector*; - using reference = SliceVector&; - - public: - - iterator(const TubeVector& tube_vector, base_container::iterator it) : - base_container::iterator(it), _tube_vector(tube_vector) { } - - reference operator*() - { - return ((*this)->_slices.at(&_tube_vector)); - } - - protected: - - const TubeVector& _tube_vector; - }; - - iterator begin() { return iterator(*this, _tdomain._tslices.begin()); } - iterator end() { return iterator(*this, _tdomain._tslices.end()); } - - - struct const_iterator : public base_container::const_iterator - { - using iterator_category = typename base_container::const_iterator::iterator_category; - using difference_type = typename base_container::const_iterator::difference_type; - - using value_type = SliceVector; - using pointer = const SliceVector*; - using reference = const SliceVector&; - - public: - - const_iterator(const TubeVector& tube_vector, base_container::const_iterator it) : - base_container::const_iterator(it), _tube_vector(tube_vector) { } - - reference operator*() const - { - return ((*this)->_slices.at(&_tube_vector)); - } - - protected: - - const TubeVector& _tube_vector; - }; - - const_iterator begin() const { return const_iterator(*this, _tdomain._tslices.cbegin()); } - const_iterator end() const { return const_iterator(*this, _tdomain._tslices.cend()); } - }; -} // namespace codac - -#endif \ No newline at end of file diff --git a/src/core/2/domains/tube/codac2_TubeVectorComponent.cpp b/src/core/2/domains/tube/codac2_TubeVectorComponent.cpp deleted file mode 100644 index 6a527c0e1..000000000 --- a/src/core/2/domains/tube/codac2_TubeVectorComponent.cpp +++ /dev/null @@ -1,108 +0,0 @@ -/** - * \file - * - * ---------------------------------------------------------------------------- - * \date 2022 - * \author Simon Rohou - * \copyright Copyright 2022 Codac Team - * \license This program is distributed under the terms of - * the GNU Lesser General Public License (LGPL). - */ - -#include -#include "codac2_TubeVectorComponent.h" -#include "codac2_TubeVector.h" -#include "codac2_SliceVector.h" -#include "ibex_Interval.h" - -using namespace std; - -namespace codac2 -{ - TubeVectorComponent::TubeVectorComponent(TubeVector& tubevector, size_t i) : - _i(i), _tubevector(tubevector) - { - assert(i >= 0 && i < tubevector.size()); - } - - TubeVectorComponent::TubeVectorComponent(const TubeVectorComponent& tubevector_i) : - _i(tubevector_i._i), _tubevector(tubevector_i._tubevector) - { - - } - - size_t TubeVectorComponent::size() const - { - return 1; - } - - const TDomain& TubeVectorComponent::tdomain() const - { - return _tubevector.tdomain(); - } - - Interval TubeVectorComponent::t0_tf() const - { - return _tubevector.t0_tf(); - } - - Interval TubeVectorComponent::codomain() const - { - Interval codomain(Interval::EMPTY_SET); - for(const auto& s : _tubevector) - codomain |= s.codomain()[_i]; - return codomain; - } - - bool TubeVectorComponent::contains(const Trajectory& value) const - { - assert(false); - return true; - } - - void TubeVectorComponent::set(const Interval& codomain) - { - for(auto& s : _tubevector) - s.set_component(_i, codomain); - } - - const TubeVectorComponent& TubeVectorComponent::operator=(const TubeVectorComponent& x) - { - assert(&x.tdomain() == &tdomain()); - for(auto& s : _tubevector) - s.set_component(_i, s._it_tslice->_slices.at(&x._tubevector).codomain()[x._i]); - return *this; - } - - const TubeVectorComponent& TubeVectorComponent::operator=(pair,const TubeVectorComponent> rel) - { - assert(&rel.second.tdomain() == &tdomain()); - for(auto& s : _tubevector) - s.set_component(_i, rel.first(s._it_tslice->_slices.at(&rel.second._tubevector).codomain()[rel.second._i])); - return *this; - } - - ostream& operator<<(ostream& os, const TubeVectorComponent& x) - { - os << "Component " << x._i << " of: " << x._tubevector << flush; - return os; - } - - pair,const TubeVectorComponent> cos(const TubeVectorComponent& x) - { - return make_pair(static_cast(ibex::cos), x); - } - - codac::Tube TubeVectorComponent::to_codac1() const - { - codac::Tube x(t0_tf()); - for(const auto& s : _tubevector) - if(!s.t0_tf().is_unbounded()) - x.set(s.codomain()[_i], s.t0_tf()); - for(const auto& s : _tubevector) // setting gate (were overwritten) - if(s.t0_tf().is_degenerated()) - x.set(s.codomain()[_i], s.t0_tf()); - return x; - } - -} // namespace codac \ No newline at end of file diff --git a/src/core/2/domains/tube/codac2_TubeVectorComponent.h b/src/core/2/domains/tube/codac2_TubeVectorComponent.h deleted file mode 100644 index 9029bf566..000000000 --- a/src/core/2/domains/tube/codac2_TubeVectorComponent.h +++ /dev/null @@ -1,66 +0,0 @@ -/** - * \file - * - * ---------------------------------------------------------------------------- - * \date 2022 - * \author Simon Rohou - * \copyright Copyright 2022 Codac Team - * \license This program is distributed under the terms of - * the GNU Lesser General Public License (LGPL). - */ - -#ifndef __CODAC2_TUBEVECTORCOMPONENT_H__ -#define __CODAC2_TUBEVECTORCOMPONENT_H__ - -#include -#include "codac_Interval.h" -#include "codac_Trajectory.h" -#include "codac2_TubeVector.h" -#include "codac2_TubeAbstract_const.h" -#include "ibex_Interval.h" -#include "codac_Tube.h" // to be removed - -namespace codac2 -{ - using codac::Interval; - using codac::Trajectory; - - class TubeVector; - - class TubeVectorComponent : public Tube_const - { - public: - - TubeVectorComponent(const TubeVectorComponent& tubevector_i); - - size_t size() const; - const TDomain& tdomain() const; - Interval t0_tf() const; - Interval codomain() const; - - bool contains(const Trajectory& value) const; - - void set(const Interval& codomain); - const TubeVectorComponent& operator=(const TubeVectorComponent& x); - const TubeVectorComponent& operator=(std::pair,const TubeVectorComponent> x); - - friend std::ostream& operator<<(std::ostream& os, const TubeVectorComponent& x); - - codac::Tube to_codac1() const; // to be removed - - - protected: - - friend class TubeVector; - explicit TubeVectorComponent(TubeVector& tubevector, size_t i); - - size_t _i; - TubeVector& _tubevector; - }; - - std::pair,const TubeVectorComponent> cos(const TubeVectorComponent& x); - - -} // namespace codac - -#endif \ No newline at end of file diff --git a/src/core/2/domains/tube/codac2_TubeVectorEvaluation.cpp b/src/core/2/domains/tube/codac2_TubeVectorEvaluation.cpp deleted file mode 100644 index fa69ca2bc..000000000 --- a/src/core/2/domains/tube/codac2_TubeVectorEvaluation.cpp +++ /dev/null @@ -1,60 +0,0 @@ -/** - * \file - * - * ---------------------------------------------------------------------------- - * \date 2022 - * \author Simon Rohou - * \copyright Copyright 2022 Codac Team - * \license This program is distributed under the terms of - * the GNU Lesser General Public License (LGPL). - */ - -#include "codac2_TubeVectorEvaluation.h" -#include "codac2_TubeVector.h" -#include "codac2_SliceVector.h" -#include "ibex_Interval.h" - -using namespace std; - -namespace codac2 -{ - TubeVectorEvaluation::TubeVectorEvaluation(TubeVector& tubevector, double t) : - _t(Interval(t)), _tubevector(tubevector) - { - - } - - TubeVectorEvaluation::TubeVectorEvaluation(TubeVector& tubevector, const Interval& t) : - _t(t), _tubevector(tubevector) - { - - } - - TubeVectorEvaluation::operator IntervalVector() const - { - return _tubevector.eval(_t); - } - - const TubeVectorEvaluation& TubeVectorEvaluation::operator=(const IntervalVector& x) - { - // Sampling the tube only if affectation is performed - // (i.e. this is not done in the constructor) - list::iterator it_lb = _tubevector._tdomain.sample(_t.lb(), false); - list::iterator it_ub = _tubevector._tdomain.sample(_t.ub(), _t.is_degenerated()); - - do - { - it_lb->_slices.at(&_tubevector).set(x); - it_lb++; - } while(it_lb != it_ub); - - return *this; - } - - ostream& operator<<(ostream& os, const TubeVectorEvaluation& x) - { - os << x._tubevector.eval(x._t) << flush; - return os; - } - -} // namespace codac \ No newline at end of file diff --git a/src/core/2/domains/tube/codac2_TubeVectorEvaluation.h b/src/core/2/domains/tube/codac2_TubeVectorEvaluation.h deleted file mode 100644 index 3cb63030e..000000000 --- a/src/core/2/domains/tube/codac2_TubeVectorEvaluation.h +++ /dev/null @@ -1,49 +0,0 @@ -/** - * \file - * - * ---------------------------------------------------------------------------- - * \date 2022 - * \author Simon Rohou - * \copyright Copyright 2022 Codac Team - * \license This program is distributed under the terms of - * the GNU Lesser General Public License (LGPL). - */ - -#ifndef __CODAC2_TUBEVECTOREVALUATION_H__ -#define __CODAC2_TUBEVECTOREVALUATION_H__ - -#include -#include "codac_Interval.h" -#include "codac_Trajectory.h" -#include "codac2_TubeVector.h" -#include "ibex_Interval.h" - -namespace codac2 -{ - using codac::Interval; - - class TubeVector; - - class TubeVectorEvaluation - { - public: - - const TubeVectorEvaluation& operator=(const IntervalVector& x); - operator IntervalVector() const; - - friend std::ostream& operator<<(std::ostream& os, const TubeVectorEvaluation& x); - - - protected: - - friend class TubeVector; - explicit TubeVectorEvaluation(TubeVector& tubevector, double t); - explicit TubeVectorEvaluation(TubeVector& tubevector, const Interval& t); - - const Interval _t; - TubeVector& _tubevector; - }; - -} // namespace codac - -#endif \ No newline at end of file diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index e2376f488..777d5cb36 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -155,20 +155,17 @@ ${CMAKE_CURRENT_SOURCE_DIR}/sivia/codac_sivia.h # Files related to codac2 - ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/tube/codac2_SliceVector.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/tube/codac2_SliceVector.h - ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/tube/codac2_TSlice.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/tube/codac2_TSlice.h + ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/tube/codac2_AbstractConstTube.h + ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/tube/codac2_AbstractSlice.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/tube/codac2_AbstractSlice.h + ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/tube/codac2_AbstractSlicedTube.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/tube/codac2_AbstractSlicedTube.h + ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/tube/codac2_Slice.h ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/tube/codac2_TDomain.cpp ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/tube/codac2_TDomain.h - ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/tube/codac2_TubeAbstract_const.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/tube/codac2_TubeAbstract_const.h - ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/tube/codac2_TubeVector.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/tube/codac2_TubeVector.h - ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/tube/codac2_TubeVectorComponent.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/tube/codac2_TubeVectorComponent.h - ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/tube/codac2_TubeVectorEvaluation.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/tube/codac2_TubeVectorEvaluation.h + ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/tube/codac2_TSlice.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/tube/codac2_TSlice.h + ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/tube/codac2_Tube.h ${CMAKE_CURRENT_SOURCE_DIR}/2/contractors/codac2_CtcDiffInclusion.cpp ${CMAKE_CURRENT_SOURCE_DIR}/2/contractors/codac2_CtcDiffInclusion.h ${CMAKE_CURRENT_SOURCE_DIR}/2/integration/codac2_AbstractDomain.cpp diff --git a/tests/core/tests_codac2_tubes.cpp b/tests/core/tests_codac2_tubes.cpp index c6f5605ff..4746b869d 100644 --- a/tests/core/tests_codac2_tubes.cpp +++ b/tests/core/tests_codac2_tubes.cpp @@ -7,7 +7,8 @@ #include "codac2_TDomain.h" #include "codac_predef_values.h" -#include "codac2_TubeVector.h" +#include "codac2_Tube.h" +#include "codac2_CtcDiffInclusion.h" using namespace Catch; using namespace Detail; @@ -41,11 +42,11 @@ TEST_CASE("Test codac2::tubes") CHECK(tdomain.iterator_tslice(5540.2)->t0_tf() == Interval(1,oo)); CHECK(tdomain.nb_tubes() == 0); - TubeVector x(2, tdomain); // adding a tubevector to the tdomain + Tube x(2, tdomain); // adding a tubevector to the tdomain CHECK(tdomain.nb_tubes() == 1); { // new scope - TubeVector v(3, tdomain); + Tube v(3, tdomain); CHECK(tdomain.nb_tubes() == 2); } // end of scope, auto removing the tube @@ -121,23 +122,77 @@ TEST_CASE("Test codac2::tubes") CHECK(vector_tslices[3].t0_tf() == Interval(10,oo)); } - SECTION("Test basic TubeVector") + SECTION("Test unbounded TDomain") + { + TDomain tdomain; + const list& tslices = tdomain.tslices(); + vector vector_tslices{ + make_move_iterator(tslices.begin()), + make_move_iterator(tslices.end()) }; + CHECK(tdomain.nb_tslices() == 1); + CHECK(vector_tslices.size() == 1); + CHECK(vector_tslices[0].t0_tf() == Interval(-oo,oo)); + } + + SECTION("Test TDomain with sampling and values") + { + TDomain tdomain; + CHECK(tdomain.nb_tslices() == 1); + Tube x(1, tdomain); + x.set(Interval(4)); + x(Interval(0,1)) = IntervalVector({{1,2}}); + x(Interval(1,2)) = IntervalVector({{5,6}}); + x(Interval(2,3)) = IntervalVector({{8,9}}); + CHECK(x.codomain() == IntervalVector({{1,9}})); + CHECK(static_cast(x(0.5)) == IntervalVector({{1,2}})); + CHECK(static_cast(x(1.5)) == IntervalVector({{5,6}})); + CHECK(static_cast(x(2.5)) == IntervalVector({{8,9}})); + + const std::shared_ptr> s0 = x.first_slice_ptr(); + CHECK(s0->t0_tf() == Interval(-oo,0)); + CHECK(s0->codomain() == IntervalVector({{4}})); + const std::shared_ptr> s1 = s0->next_slice(); + CHECK(s1->t0_tf() == Interval(0,1)); + CHECK(s1->codomain() == IntervalVector({{1,2}})); + const std::shared_ptr> s2 = s1->next_slice(); + CHECK(s2->t0_tf() == Interval(1,2)); + CHECK(s2->codomain() == IntervalVector({{5,6}})); + const std::shared_ptr> s3 = s2->next_slice(); + CHECK(s3->t0_tf() == Interval(2,3)); + CHECK(s3->codomain() == IntervalVector({{8,9}})); + const std::shared_ptr> s4 = s3->next_slice(); + CHECK(s4->t0_tf() == Interval(3,oo)); + CHECK(s4->codomain() == IntervalVector({{4}})); + + CHECK(tdomain.nb_tslices() == 5); + tdomain.sample(1.3); + CHECK(tdomain.nb_tslices() == 6); + CHECK(s2->t0_tf() == Interval(1,1.3)); + CHECK(s2->codomain() == IntervalVector({{5,6}})); + const std::shared_ptr> s2bis = s2->next_slice(); + CHECK(s2bis->t0_tf() == Interval(1.3,2)); + CHECK(s2bis->codomain() == IntervalVector({{5,6}})); + CHECK(s3->t0_tf() == Interval(2,3)); + CHECK(s3->codomain() == IntervalVector({{8,9}})); + } + + SECTION("Test basic Tube") { TDomain tdomain(Interval(0,1), 0.1, false); - TubeVector x(3, tdomain); + Tube x(3, tdomain); CHECK(x.size() == 3); CHECK(&x.tdomain() == &tdomain); CHECK(x.t0_tf() == Interval(0,1)); CHECK(x.nb_slices() == tdomain.nb_tslices()); CHECK(x.nb_slices() == 12); - CHECK(x.first_slice().t0_tf() == Interval(-oo,0)); - CHECK(x.last_slice().t0_tf() == Interval(1,oo)); + CHECK(x.first_slice_ptr()->t0_tf() == Interval(-oo,0)); + CHECK(x.last_slice_ptr()->t0_tf() == Interval(1,oo)); CHECK(x.codomain() == IntervalVector(3)); x.set(IntervalVector(3, Interval(-10,10))); CHECK(x.codomain() == IntervalVector(3, Interval(-10,10))); - // TubeVectorComponent + // TubeComponent CHECK(x[0].codomain() == Interval(-10,10)); CHECK(x[0].t0_tf() == Interval(0,1)); CHECK(&x[0].tdomain() == &tdomain); @@ -172,21 +227,21 @@ TEST_CASE("Test codac2::tubes") // Iterators tests { - SliceVector* s_ = &x.first_slice(); + shared_ptr> s_ = x.first_slice_ptr(); for(auto& s : x) { - CHECK(&s == s_); + CHECK(&s == &(*s_)); s_ = s_->next_slice(); } } // Iterators tests (const) { - const TubeVector y(x); // copy constructor - const SliceVector* s_ = &x.first_slice(); + const Tube y(x); // copy constructor + shared_ptr> s_ = x.first_slice_ptr(); for(const auto& s : x) { - CHECK(&s == s_); + CHECK(&s == &(*s_)); s_ = s_->next_slice(); } } @@ -195,15 +250,15 @@ TEST_CASE("Test codac2::tubes") SECTION("Test SliceVector") { TDomain tdomain(Interval(0,1), 0.1); - TubeVector x(2, tdomain); + Tube x(2, tdomain); CHECK(x.nb_slices() == 12); - CHECK(&x.first_slice() == &tdomain.iterator_tslice(-oo)->_slices.at(&x)); - CHECK(&x.last_slice() == &tdomain.iterator_tslice(oo)->_slices.at(&x)); + CHECK(x.first_slice_ptr() == tdomain.iterator_tslice(-oo)->_slices.at(&x)); + CHECK(x.last_slice_ptr() == tdomain.iterator_tslice(oo)->_slices.at(&x)); for(auto& s : x) s.set(IntervalVector(2,s.t0_tf())); - vector v; + vector*> v; for(const auto& s : x) v.push_back(&s); @@ -224,4 +279,55 @@ TEST_CASE("Test codac2::tubes") CHECK(v[11]->input_gate() == IntervalVector(2,Interval(1))); CHECK(v[11]->output_gate() == IntervalVector(2,Interval(1,oo))); } + + SECTION("Test again 1") + { + TDomain tdomain(Interval(0,10), 0.01, true); // last argument creates "gates" (degenerated slices at scalar timesteps) + Tube x(2, tdomain, + codac::TFunction("(sin(sqrt(t)+((t-5)^2)*[-0.01,0.01]) ; cos(t)+sin(t/0.2)*[-0.1,0.1])")); + Tube u(2, tdomain); + + codac::TFunction tf("x[2]", "u[2]", "(sin(x[1]) ; -sin(x[0]))"); + CtcDiffInclusion ctc_diffincl(tf); + ctc_diffincl.contract(x,u); + + //vibes::beginDrawing(); + + codac::TubeVector x_codac1 = x.to_codac1(); // may take time + codac::Tube xi_codac1 = x[1].to_codac1(); // may take time + + //codac::VIBesFigTube fig("Tube"); + //fig.set_properties(100, 100, 600, 300); + //fig.add_tube(&xi_codac1, "x"); + //fig.show(true); + + //vibes::endDrawing(); + } + + SECTION("Test again 2") + { + /*{ + TDomain tdomain(Interval(0,10), 1., false); + Tube x(2, tdomain); + + for(auto& sx : x) + { + if(sx.t0_tf().contains(5.2)) + { + cout << "sample" << endl; + tdomain.sample(5.2); + } + cout << sx << endl; + } + }*/ + + { + codac::TFunction f("x1", "x2", "u1", "u2", "(t+u1+x1;t+u2+x2)"); + IntervalVector x({{2,3},{5,6}}); + IntervalVector u({{0,0.1},{0,0.1}}); + Interval t(5.); + //cout << f.eval_vector(t,x,u) << endl; + CHECK(f.eval_vector(t,x,u) == ApproxIntvVector(IntervalVector({{7, 8.100000000000002},{10, 11.10000000000001}}))); + } + } } \ No newline at end of file From a15c801fcbd8e277143d76daa4bc27b7ff49b4e6 Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Fri, 10 Jun 2022 00:16:48 +0200 Subject: [PATCH 023/256] Minor updates in choco package --- packages/choco/codac/codac.nuspec | 4 ++-- packages/choco/codac/tools/chocolateyinstall.ps1 | 4 ++-- packages/choco/codac/tools/chocolateyuninstall.ps1 | 6 +++--- packages/choco/codac/tools/{regKeys.ps1 => sharedVars.ps1} | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) rename packages/choco/codac/tools/{regKeys.ps1 => sharedVars.ps1} (81%) diff --git a/packages/choco/codac/codac.nuspec b/packages/choco/codac/codac.nuspec index 98ca2ac48..c6620641f 100644 --- a/packages/choco/codac/codac.nuspec +++ b/packages/choco/codac/codac.nuspec @@ -21,8 +21,8 @@ Codac is a library providing tools for constraint programming over reals, trajec - Optionally, download and run https://github.com/ENSTABretagneRobotics/VIBES/releases/download/0.2.3/VIBes-0.2.3-win32.exe before running the project, and check that a tube appears in VIBes window. ## Troubleshooting -- Check that all the packages and their dependencies were installed, if one failed (e.g. due to network-related errors) try to reinstall it using --force or try a previous version... -- 32 bit versions of latest Qt Creator do not seem available, if needed use `choco install -y qtcreator --version=4.13.3 --x86 --force` +- Check that all the packages and their dependencies were installed, if one failed (e.g. due to network-related errors) try to reinstall it using `--force` or try a previous version... +- 32 bit versions of Qt Creator do not seem available any more, see https://github.com/AdmiringWorm/chocolatey-packages/issues/362. - If multiple compilers are already installed, Qt Creator might show multiple possibilities in the Configure Project panel, ensure you choose one compatible with https://chocolatey.org/packages/codac#dependencies. https://github.com/codac-team/codac/releases diff --git a/packages/choco/codac/tools/chocolateyinstall.ps1 b/packages/choco/codac/tools/chocolateyinstall.ps1 index 1f8d009f9..7b030baa2 100644 --- a/packages/choco/codac/tools/chocolateyinstall.ps1 +++ b/packages/choco/codac/tools/chocolateyinstall.ps1 @@ -1,7 +1,7 @@ $ErrorActionPreference = 'Stop'; # Stop on all errors. -# Source registry key values which are shared between install and uninstall. -. $PSScriptRoot\regKeys.ps1 +# Source variables which are shared between install and uninstall. +. $PSScriptRoot\sharedVars.ps1 New-Item "$CMakeSystemRepositoryPath\$CMakePackageName" -ItemType directory -Force New-ItemProperty -Name "CMakePackageDir" -PropertyType String -Value "$env:ChocolateyPackageFolder\share\$CMakePackageName\cmake" -Path "$CMakeSystemRepositoryPath\$CMakePackageName" -Force diff --git a/packages/choco/codac/tools/chocolateyuninstall.ps1 b/packages/choco/codac/tools/chocolateyuninstall.ps1 index 2bd9f0c14..37cecf4ea 100644 --- a/packages/choco/codac/tools/chocolateyuninstall.ps1 +++ b/packages/choco/codac/tools/chocolateyuninstall.ps1 @@ -1,13 +1,13 @@ $ErrorActionPreference = 'Stop'; # Stop on all errors. -# Source registry key values which are shared between install and uninstall. -. $PSScriptRoot\regKeys.ps1 +# Source variables which are shared between install and uninstall. +. $PSScriptRoot\sharedVars.ps1 #Uninstall-BinFile -Name libcodac-rob.a #Uninstall-BinFile -Name libcodac.a if (Test-Path $CMakeRegistryPath) { - if (Test-Path $CMakeSystemRepositoryPath) { + if (Test-Path $CMakeSystemRepositoryPath\$CMakePackageName) { Remove-Item "$CMakeSystemRepositoryPath\$CMakePackageName" } } diff --git a/packages/choco/codac/tools/regKeys.ps1 b/packages/choco/codac/tools/sharedVars.ps1 similarity index 81% rename from packages/choco/codac/tools/regKeys.ps1 rename to packages/choco/codac/tools/sharedVars.ps1 index bd2b02620..1a1a238d4 100644 --- a/packages/choco/codac/tools/regKeys.ps1 +++ b/packages/choco/codac/tools/sharedVars.ps1 @@ -1,3 +1,3 @@ $CMakeRegistryPath = "HKCU:\SOFTWARE\Kitware\CMake" $CMakeSystemRepositoryPath = "HKLM:\SOFTWARE\Kitware\CMake\Packages" -$CMakePackageName = "Codac" \ No newline at end of file +$CMakePackageName = "Codac" From e6e1448bf253b59e1f8e904996823b5c521cbd85 Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Wed, 3 Aug 2022 00:30:02 +0200 Subject: [PATCH 024/256] EIGEN3_VERSION should be Eigen3_VERSION --- CMakeLists.txt | 2 +- doc/doc/install/03-start-cpp-project.rst | 2 +- examples/basics/01_arithmetic/CMakeLists.txt | 2 +- examples/basics/02_simple_ctc/CMakeLists.txt | 2 +- examples/basics/03_ivp_msinx/CMakeLists.txt | 2 +- examples/basics/04_simple_eval/CMakeLists.txt | 2 +- examples/basics/05_graphics/CMakeLists.txt | 2 +- examples/basics/06_ctc_comparisons/CMakeLists.txt | 2 +- examples/basics/07_temporal_ctc/CMakeLists.txt | 2 +- examples/basics/08_tube_paving/CMakeLists.txt | 2 +- examples/basics/09_cn_paving/CMakeLists.txt | 2 +- examples/brunovsky/CMakeLists.txt | 2 +- examples/lie_group/05_loc/CMakeLists.txt | 2 +- examples/linobs/01_paper/CMakeLists.txt | 2 +- examples/robotics/01_causal_chain/CMakeLists.txt | 2 +- examples/robotics/03_drifting_clock/CMakeLists.txt | 2 +- examples/robotics/04_redermor_traj/CMakeLists.txt | 2 +- examples/robotics/05_loops_detec/CMakeLists.txt | 2 +- examples/robotics/06_loops_proofs/CMakeLists.txt | 2 +- examples/robotics/07_dynloc/CMakeLists.txt | 2 +- examples/robotics/08_tubepaving/CMakeLists.txt | 2 +- examples/robotics/09_lissajous/CMakeLists.txt | 2 +- examples/robotics/10_datasso/CMakeLists.txt | 2 +- examples/robotics/11_explored_area/CMakeLists.txt | 2 +- examples/robotics/old_ex_02_lowcost_beacons/CMakeLists.txt | 2 +- examples/tuto/01_getting_started/CMakeLists.txt | 2 +- examples/tuto/02_static_rangeonly/CMakeLists.txt | 2 +- examples/tuto/03_static_rangebearing/CMakeLists.txt | 2 +- examples/tuto/04_dyn_rangeonly/CMakeLists.txt | 2 +- examples/tuto/05_dyn_rangebearing/CMakeLists.txt | 2 +- tests/test_codac/CMakeLists.txt | 2 +- 31 files changed, 31 insertions(+), 31 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index bd2086804..af26de3a3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -105,7 +105,7 @@ ################################################################################ find_package(Eigen3 REQUIRED NO_MODULE) - message(STATUS "Found Eigen3 version ${EIGEN3_VERSION}") + message(STATUS "Found Eigen3 version ${Eigen3_VERSION}") add_definitions(${EIGEN3_DEFINITIONS}) include_directories(${EIGEN3_INCLUDE_DIRS}) diff --git a/doc/doc/install/03-start-cpp-project.rst b/doc/doc/install/03-start-cpp-project.rst index b2d469dd2..357004ece 100644 --- a/doc/doc/install/03-start-cpp-project.rst +++ b/doc/doc/install/03-start-cpp-project.rst @@ -56,7 +56,7 @@ For the compilation of your project, you can use CMake with the following file : # set(CMAKE_PREFIX_PATH "~/eigen/build_install") find_package(Eigen3 REQUIRED NO_MODULE) - message(STATUS "Found Eigen3 version ${EIGEN3_VERSION}") + message(STATUS "Found Eigen3 version ${Eigen3_VERSION}") # Adding Codac diff --git a/examples/basics/01_arithmetic/CMakeLists.txt b/examples/basics/01_arithmetic/CMakeLists.txt index 1891afbb6..d6ff347ac 100644 --- a/examples/basics/01_arithmetic/CMakeLists.txt +++ b/examples/basics/01_arithmetic/CMakeLists.txt @@ -22,7 +22,7 @@ # set(CMAKE_PREFIX_PATH "~/eigen/build_install") find_package(Eigen3 REQUIRED NO_MODULE) - message(STATUS "Found Eigen3 version ${EIGEN3_VERSION}") + message(STATUS "Found Eigen3 version ${Eigen3_VERSION}") # Adding Codac diff --git a/examples/basics/02_simple_ctc/CMakeLists.txt b/examples/basics/02_simple_ctc/CMakeLists.txt index 6c9f32d38..25a26cc1d 100644 --- a/examples/basics/02_simple_ctc/CMakeLists.txt +++ b/examples/basics/02_simple_ctc/CMakeLists.txt @@ -22,7 +22,7 @@ # set(CMAKE_PREFIX_PATH "~/eigen/build_install") find_package(Eigen3 REQUIRED NO_MODULE) - message(STATUS "Found Eigen3 version ${EIGEN3_VERSION}") + message(STATUS "Found Eigen3 version ${Eigen3_VERSION}") # Adding Codac diff --git a/examples/basics/03_ivp_msinx/CMakeLists.txt b/examples/basics/03_ivp_msinx/CMakeLists.txt index f4d63550d..ee88055de 100644 --- a/examples/basics/03_ivp_msinx/CMakeLists.txt +++ b/examples/basics/03_ivp_msinx/CMakeLists.txt @@ -22,7 +22,7 @@ # set(CMAKE_PREFIX_PATH "~/eigen/build_install") find_package(Eigen3 REQUIRED NO_MODULE) - message(STATUS "Found Eigen3 version ${EIGEN3_VERSION}") + message(STATUS "Found Eigen3 version ${Eigen3_VERSION}") # Adding Codac diff --git a/examples/basics/04_simple_eval/CMakeLists.txt b/examples/basics/04_simple_eval/CMakeLists.txt index 189540e73..afd4d6846 100644 --- a/examples/basics/04_simple_eval/CMakeLists.txt +++ b/examples/basics/04_simple_eval/CMakeLists.txt @@ -22,7 +22,7 @@ # set(CMAKE_PREFIX_PATH "~/eigen/build_install") find_package(Eigen3 REQUIRED NO_MODULE) - message(STATUS "Found Eigen3 version ${EIGEN3_VERSION}") + message(STATUS "Found Eigen3 version ${Eigen3_VERSION}") # Adding Codac diff --git a/examples/basics/05_graphics/CMakeLists.txt b/examples/basics/05_graphics/CMakeLists.txt index b03a30669..ce17103b8 100644 --- a/examples/basics/05_graphics/CMakeLists.txt +++ b/examples/basics/05_graphics/CMakeLists.txt @@ -22,7 +22,7 @@ # set(CMAKE_PREFIX_PATH "~/eigen/build_install") find_package(Eigen3 REQUIRED NO_MODULE) - message(STATUS "Found Eigen3 version ${EIGEN3_VERSION}") + message(STATUS "Found Eigen3 version ${Eigen3_VERSION}") # Adding Codac diff --git a/examples/basics/06_ctc_comparisons/CMakeLists.txt b/examples/basics/06_ctc_comparisons/CMakeLists.txt index 885a8e847..fe609601b 100644 --- a/examples/basics/06_ctc_comparisons/CMakeLists.txt +++ b/examples/basics/06_ctc_comparisons/CMakeLists.txt @@ -22,7 +22,7 @@ # set(CMAKE_PREFIX_PATH "~/eigen/build_install") find_package(Eigen3 REQUIRED NO_MODULE) - message(STATUS "Found Eigen3 version ${EIGEN3_VERSION}") + message(STATUS "Found Eigen3 version ${Eigen3_VERSION}") # Adding Codac diff --git a/examples/basics/07_temporal_ctc/CMakeLists.txt b/examples/basics/07_temporal_ctc/CMakeLists.txt index acfb11f37..ca6cc42c5 100644 --- a/examples/basics/07_temporal_ctc/CMakeLists.txt +++ b/examples/basics/07_temporal_ctc/CMakeLists.txt @@ -22,7 +22,7 @@ # set(CMAKE_PREFIX_PATH "~/eigen/build_install") find_package(Eigen3 REQUIRED NO_MODULE) - message(STATUS "Found Eigen3 version ${EIGEN3_VERSION}") + message(STATUS "Found Eigen3 version ${Eigen3_VERSION}") # Adding Codac diff --git a/examples/basics/08_tube_paving/CMakeLists.txt b/examples/basics/08_tube_paving/CMakeLists.txt index a78dc8995..493af73e3 100644 --- a/examples/basics/08_tube_paving/CMakeLists.txt +++ b/examples/basics/08_tube_paving/CMakeLists.txt @@ -22,7 +22,7 @@ # set(CMAKE_PREFIX_PATH "~/eigen/build_install") find_package(Eigen3 REQUIRED NO_MODULE) - message(STATUS "Found Eigen3 version ${EIGEN3_VERSION}") + message(STATUS "Found Eigen3 version ${Eigen3_VERSION}") # Adding Codac diff --git a/examples/basics/09_cn_paving/CMakeLists.txt b/examples/basics/09_cn_paving/CMakeLists.txt index bdc4b90c6..b41f34686 100644 --- a/examples/basics/09_cn_paving/CMakeLists.txt +++ b/examples/basics/09_cn_paving/CMakeLists.txt @@ -22,7 +22,7 @@ # set(CMAKE_PREFIX_PATH "~/eigen/build_install") find_package(Eigen3 REQUIRED NO_MODULE) - message(STATUS "Found Eigen3 version ${EIGEN3_VERSION}") + message(STATUS "Found Eigen3 version ${Eigen3_VERSION}") # Adding Codac diff --git a/examples/brunovsky/CMakeLists.txt b/examples/brunovsky/CMakeLists.txt index e4e4bd373..d597134e0 100755 --- a/examples/brunovsky/CMakeLists.txt +++ b/examples/brunovsky/CMakeLists.txt @@ -33,7 +33,7 @@ # set(CMAKE_PREFIX_PATH "~/eigen/build_install") find_package(Eigen3 REQUIRED NO_MODULE) - message(STATUS "Found Eigen3 version ${EIGEN3_VERSION}") + message(STATUS "Found Eigen3 version ${Eigen3_VERSION}") # Adding Codac diff --git a/examples/lie_group/05_loc/CMakeLists.txt b/examples/lie_group/05_loc/CMakeLists.txt index 2be59bfb3..803c4cc82 100644 --- a/examples/lie_group/05_loc/CMakeLists.txt +++ b/examples/lie_group/05_loc/CMakeLists.txt @@ -22,7 +22,7 @@ # set(CMAKE_PREFIX_PATH "~/eigen/build_install") find_package(Eigen3 REQUIRED NO_MODULE) - message(STATUS "Found Eigen3 version ${EIGEN3_VERSION}") + message(STATUS "Found Eigen3 version ${Eigen3_VERSION}") # Adding Codac diff --git a/examples/linobs/01_paper/CMakeLists.txt b/examples/linobs/01_paper/CMakeLists.txt index 8e7e15165..fa1588248 100644 --- a/examples/linobs/01_paper/CMakeLists.txt +++ b/examples/linobs/01_paper/CMakeLists.txt @@ -32,7 +32,7 @@ # set(CMAKE_PREFIX_PATH "~/eigen/build_install") find_package(Eigen3 REQUIRED NO_MODULE) - message(STATUS "Found Eigen3 version ${EIGEN3_VERSION}") + message(STATUS "Found Eigen3 version ${Eigen3_VERSION}") # Adding Codac diff --git a/examples/robotics/01_causal_chain/CMakeLists.txt b/examples/robotics/01_causal_chain/CMakeLists.txt index 5bc663c25..bf6ea5e14 100644 --- a/examples/robotics/01_causal_chain/CMakeLists.txt +++ b/examples/robotics/01_causal_chain/CMakeLists.txt @@ -22,7 +22,7 @@ # set(CMAKE_PREFIX_PATH "~/eigen/build_install") find_package(Eigen3 REQUIRED NO_MODULE) - message(STATUS "Found Eigen3 version ${EIGEN3_VERSION}") + message(STATUS "Found Eigen3 version ${Eigen3_VERSION}") # Adding Codac diff --git a/examples/robotics/03_drifting_clock/CMakeLists.txt b/examples/robotics/03_drifting_clock/CMakeLists.txt index 547b492f9..e209c48fd 100644 --- a/examples/robotics/03_drifting_clock/CMakeLists.txt +++ b/examples/robotics/03_drifting_clock/CMakeLists.txt @@ -22,7 +22,7 @@ # set(CMAKE_PREFIX_PATH "~/eigen/build_install") find_package(Eigen3 REQUIRED NO_MODULE) - message(STATUS "Found Eigen3 version ${EIGEN3_VERSION}") + message(STATUS "Found Eigen3 version ${Eigen3_VERSION}") # Adding Codac diff --git a/examples/robotics/04_redermor_traj/CMakeLists.txt b/examples/robotics/04_redermor_traj/CMakeLists.txt index 48564d05e..40e74c9ac 100644 --- a/examples/robotics/04_redermor_traj/CMakeLists.txt +++ b/examples/robotics/04_redermor_traj/CMakeLists.txt @@ -22,7 +22,7 @@ # set(CMAKE_PREFIX_PATH "~/eigen/build_install") find_package(Eigen3 REQUIRED NO_MODULE) - message(STATUS "Found Eigen3 version ${EIGEN3_VERSION}") + message(STATUS "Found Eigen3 version ${Eigen3_VERSION}") # Adding Codac diff --git a/examples/robotics/05_loops_detec/CMakeLists.txt b/examples/robotics/05_loops_detec/CMakeLists.txt index e87c167a1..7ae0bd07a 100644 --- a/examples/robotics/05_loops_detec/CMakeLists.txt +++ b/examples/robotics/05_loops_detec/CMakeLists.txt @@ -22,7 +22,7 @@ # set(CMAKE_PREFIX_PATH "~/eigen/build_install") find_package(Eigen3 REQUIRED NO_MODULE) - message(STATUS "Found Eigen3 version ${EIGEN3_VERSION}") + message(STATUS "Found Eigen3 version ${Eigen3_VERSION}") # Adding Codac diff --git a/examples/robotics/06_loops_proofs/CMakeLists.txt b/examples/robotics/06_loops_proofs/CMakeLists.txt index a96881fba..1ccc07c8b 100644 --- a/examples/robotics/06_loops_proofs/CMakeLists.txt +++ b/examples/robotics/06_loops_proofs/CMakeLists.txt @@ -22,7 +22,7 @@ # set(CMAKE_PREFIX_PATH "~/eigen/build_install") find_package(Eigen3 REQUIRED NO_MODULE) - message(STATUS "Found Eigen3 version ${EIGEN3_VERSION}") + message(STATUS "Found Eigen3 version ${Eigen3_VERSION}") # Adding Codac diff --git a/examples/robotics/07_dynloc/CMakeLists.txt b/examples/robotics/07_dynloc/CMakeLists.txt index 93be85b80..cfd40c968 100644 --- a/examples/robotics/07_dynloc/CMakeLists.txt +++ b/examples/robotics/07_dynloc/CMakeLists.txt @@ -22,7 +22,7 @@ # set(CMAKE_PREFIX_PATH "~/eigen/build_install") find_package(Eigen3 REQUIRED NO_MODULE) - message(STATUS "Found Eigen3 version ${EIGEN3_VERSION}") + message(STATUS "Found Eigen3 version ${Eigen3_VERSION}") # Adding Codac diff --git a/examples/robotics/08_tubepaving/CMakeLists.txt b/examples/robotics/08_tubepaving/CMakeLists.txt index 68bb93c1b..c6bdde470 100644 --- a/examples/robotics/08_tubepaving/CMakeLists.txt +++ b/examples/robotics/08_tubepaving/CMakeLists.txt @@ -22,7 +22,7 @@ # set(CMAKE_PREFIX_PATH "~/eigen/build_install") find_package(Eigen3 REQUIRED NO_MODULE) - message(STATUS "Found Eigen3 version ${EIGEN3_VERSION}") + message(STATUS "Found Eigen3 version ${Eigen3_VERSION}") # Adding Codac diff --git a/examples/robotics/09_lissajous/CMakeLists.txt b/examples/robotics/09_lissajous/CMakeLists.txt index 58cee6e1d..2a05be0dd 100644 --- a/examples/robotics/09_lissajous/CMakeLists.txt +++ b/examples/robotics/09_lissajous/CMakeLists.txt @@ -22,7 +22,7 @@ # set(CMAKE_PREFIX_PATH "~/eigen/build_install") find_package(Eigen3 REQUIRED NO_MODULE) - message(STATUS "Found Eigen3 version ${EIGEN3_VERSION}") + message(STATUS "Found Eigen3 version ${Eigen3_VERSION}") # Adding Codac diff --git a/examples/robotics/10_datasso/CMakeLists.txt b/examples/robotics/10_datasso/CMakeLists.txt index 94196110d..b6fe31adc 100644 --- a/examples/robotics/10_datasso/CMakeLists.txt +++ b/examples/robotics/10_datasso/CMakeLists.txt @@ -22,7 +22,7 @@ # set(CMAKE_PREFIX_PATH "~/eigen/build_install") find_package(Eigen3 REQUIRED NO_MODULE) - message(STATUS "Found Eigen3 version ${EIGEN3_VERSION}") + message(STATUS "Found Eigen3 version ${Eigen3_VERSION}") # Adding Codac diff --git a/examples/robotics/11_explored_area/CMakeLists.txt b/examples/robotics/11_explored_area/CMakeLists.txt index 02561f5f6..8fa92bfa0 100644 --- a/examples/robotics/11_explored_area/CMakeLists.txt +++ b/examples/robotics/11_explored_area/CMakeLists.txt @@ -22,7 +22,7 @@ # set(CMAKE_PREFIX_PATH "~/eigen/build_install") find_package(Eigen3 REQUIRED NO_MODULE) - message(STATUS "Found Eigen3 version ${EIGEN3_VERSION}") + message(STATUS "Found Eigen3 version ${Eigen3_VERSION}") # Adding Codac diff --git a/examples/robotics/old_ex_02_lowcost_beacons/CMakeLists.txt b/examples/robotics/old_ex_02_lowcost_beacons/CMakeLists.txt index 15e033f33..01db341ef 100644 --- a/examples/robotics/old_ex_02_lowcost_beacons/CMakeLists.txt +++ b/examples/robotics/old_ex_02_lowcost_beacons/CMakeLists.txt @@ -22,7 +22,7 @@ # set(CMAKE_PREFIX_PATH "~/eigen/build_install") find_package(Eigen3 REQUIRED NO_MODULE) - message(STATUS "Found Eigen3 version ${EIGEN3_VERSION}") + message(STATUS "Found Eigen3 version ${Eigen3_VERSION}") # Adding Codac diff --git a/examples/tuto/01_getting_started/CMakeLists.txt b/examples/tuto/01_getting_started/CMakeLists.txt index e4c08cf53..962185fbe 100644 --- a/examples/tuto/01_getting_started/CMakeLists.txt +++ b/examples/tuto/01_getting_started/CMakeLists.txt @@ -22,7 +22,7 @@ # set(CMAKE_PREFIX_PATH "~/eigen/build_install") find_package(Eigen3 REQUIRED NO_MODULE) - message(STATUS "Found Eigen3 version ${EIGEN3_VERSION}") + message(STATUS "Found Eigen3 version ${Eigen3_VERSION}") # Adding Codac diff --git a/examples/tuto/02_static_rangeonly/CMakeLists.txt b/examples/tuto/02_static_rangeonly/CMakeLists.txt index da0ba5d99..65664cbdc 100644 --- a/examples/tuto/02_static_rangeonly/CMakeLists.txt +++ b/examples/tuto/02_static_rangeonly/CMakeLists.txt @@ -22,7 +22,7 @@ # set(CMAKE_PREFIX_PATH "~/eigen/build_install") find_package(Eigen3 REQUIRED NO_MODULE) - message(STATUS "Found Eigen3 version ${EIGEN3_VERSION}") + message(STATUS "Found Eigen3 version ${Eigen3_VERSION}") # Adding Codac diff --git a/examples/tuto/03_static_rangebearing/CMakeLists.txt b/examples/tuto/03_static_rangebearing/CMakeLists.txt index 5ee96f0dd..1a1c05427 100644 --- a/examples/tuto/03_static_rangebearing/CMakeLists.txt +++ b/examples/tuto/03_static_rangebearing/CMakeLists.txt @@ -22,7 +22,7 @@ # set(CMAKE_PREFIX_PATH "~/eigen/build_install") find_package(Eigen3 REQUIRED NO_MODULE) - message(STATUS "Found Eigen3 version ${EIGEN3_VERSION}") + message(STATUS "Found Eigen3 version ${Eigen3_VERSION}") # Adding Codac diff --git a/examples/tuto/04_dyn_rangeonly/CMakeLists.txt b/examples/tuto/04_dyn_rangeonly/CMakeLists.txt index 7bae7641b..f9fd5729a 100644 --- a/examples/tuto/04_dyn_rangeonly/CMakeLists.txt +++ b/examples/tuto/04_dyn_rangeonly/CMakeLists.txt @@ -22,7 +22,7 @@ # set(CMAKE_PREFIX_PATH "~/eigen/build_install") find_package(Eigen3 REQUIRED NO_MODULE) - message(STATUS "Found Eigen3 version ${EIGEN3_VERSION}") + message(STATUS "Found Eigen3 version ${Eigen3_VERSION}") # Adding Codac diff --git a/examples/tuto/05_dyn_rangebearing/CMakeLists.txt b/examples/tuto/05_dyn_rangebearing/CMakeLists.txt index 8b512ca5b..e3984a7f2 100644 --- a/examples/tuto/05_dyn_rangebearing/CMakeLists.txt +++ b/examples/tuto/05_dyn_rangebearing/CMakeLists.txt @@ -22,7 +22,7 @@ # set(CMAKE_PREFIX_PATH "~/eigen/build_install") find_package(Eigen3 REQUIRED NO_MODULE) - message(STATUS "Found Eigen3 version ${EIGEN3_VERSION}") + message(STATUS "Found Eigen3 version ${Eigen3_VERSION}") # Adding Codac diff --git a/tests/test_codac/CMakeLists.txt b/tests/test_codac/CMakeLists.txt index a58e96bba..2d5ee4323 100644 --- a/tests/test_codac/CMakeLists.txt +++ b/tests/test_codac/CMakeLists.txt @@ -23,7 +23,7 @@ cmake_minimum_required(VERSION 3.0.2) set(CMAKE_PREFIX_PATH "../eigen") find_package(Eigen3 REQUIRED NO_MODULE) - message(STATUS "Found Eigen3 version ${EIGEN3_VERSION}") + message(STATUS "Found Eigen3 version ${Eigen3_VERSION}") # Adding Codac From 4c3607a30664e5b8bdf049841525651128ee630a Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Tue, 9 Aug 2022 20:34:10 +0200 Subject: [PATCH 025/256] Updated workflows --- .github/workflows/macosmatrix.yml | 1 + .github/workflows/tests.yml | 1 + .github/workflows/unixmatrix.yml | 1 + .github/workflows/vcmatrix.yml | 1 + 4 files changed, 4 insertions(+) diff --git a/.github/workflows/macosmatrix.yml b/.github/workflows/macosmatrix.yml index 6f0cb2de2..83953c682 100644 --- a/.github/workflows/macosmatrix.yml +++ b/.github/workflows/macosmatrix.yml @@ -13,6 +13,7 @@ jobs: run: shell: ${{ matrix.cfg.shell }} strategy: + fail-fast: false matrix: cfg: # - { os: macos-11, shell: bash, arch: x64, runtime: bigsur, cmake_flags: '-fPIC', cpcfg: '-macosx_11_0_x86_64', py_v_maj: 3, py_v_min: 10, desc: 'macOS Big Sur Python 3.10' } diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 3f1c68c34..92b8bbad8 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -9,6 +9,7 @@ jobs: tests: runs-on: ${{ matrix.cfg.os }} strategy: + fail-fast: false matrix: cfg: - { os: ubuntu-18.04, gcc_v: 8, py_v_maj: 3, py_v_min: 6, desc: 'Ubuntu 18.04 GCC 8 Python 3.6 tests' } diff --git a/.github/workflows/unixmatrix.yml b/.github/workflows/unixmatrix.yml index 5fee6b491..e0e2dff2c 100644 --- a/.github/workflows/unixmatrix.yml +++ b/.github/workflows/unixmatrix.yml @@ -26,6 +26,7 @@ jobs: run: shell: ${{ matrix.cfg.shell }} strategy: + fail-fast: false matrix: cfg: - { os: windows-2022, shell: cmd, arch: x64, runtime: vc17, cmake_params: '-G "Visual Studio 17" -A x64', cmake_flags: ' /wd4267 /wd4244 /wd4305 /wd4996', cmake_config: '--config Release', cmake_codac_config: '--config Debug', test_config: 'Release/', desc: 'Windows Visual Studio 2022 x64' } diff --git a/.github/workflows/vcmatrix.yml b/.github/workflows/vcmatrix.yml index 5830b2afb..91804df75 100644 --- a/.github/workflows/vcmatrix.yml +++ b/.github/workflows/vcmatrix.yml @@ -13,6 +13,7 @@ jobs: run: shell: ${{ matrix.cfg.shell }} strategy: + fail-fast: false matrix: cfg: - { os: windows-2019, shell: cmd, arch: x86, runtime: vc16, cmake_params: '-G "Visual Studio 16" -A Win32', choco_flags: '--x86', cpcfg: '-win32', py_v_maj: 3, py_v_min: 10, desc: 'Windows Visual Studio 2019 x86 Python 3.10' } From b1229aa91481c393ef2235435036785e67a7fb99 Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Tue, 9 Aug 2022 21:14:08 +0200 Subject: [PATCH 026/256] Updated workflows --- .github/workflows/unixmatrix.yml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/unixmatrix.yml b/.github/workflows/unixmatrix.yml index e0e2dff2c..441e424cf 100644 --- a/.github/workflows/unixmatrix.yml +++ b/.github/workflows/unixmatrix.yml @@ -29,12 +29,12 @@ jobs: fail-fast: false matrix: cfg: - - { os: windows-2022, shell: cmd, arch: x64, runtime: vc17, cmake_params: '-G "Visual Studio 17" -A x64', cmake_flags: ' /wd4267 /wd4244 /wd4305 /wd4996', cmake_config: '--config Release', cmake_codac_config: '--config Debug', test_config: 'Release/', desc: 'Windows Visual Studio 2022 x64' } - - { os: windows-2022, shell: cmd, arch: x86, runtime: vc17, cmake_params: '-G "Visual Studio 17" -A Win32', choco_flags: '--x86', cmake_flags: ' /wd4267 /wd4244 /wd4305 /wd4996', cmake_config: '--config Release', cmake_codac_config: '--config Debug', test_config: 'Release/', desc: 'Windows Visual Studio 2022 x86' } - - { os: windows-2019, shell: cmd, arch: x64, runtime: vc16, cmake_params: '-G "Visual Studio 16" -A x64', cmake_flags: ' /wd4267 /wd4244 /wd4305 /wd4996', cmake_config: '--config Release', cmake_codac_config: '--config Debug', test_config: 'Release/', desc: 'Windows Visual Studio 2019 x64' } - - { os: windows-2019, shell: cmd, arch: x86, runtime: vc16, cmake_params: '-G "Visual Studio 16" -A Win32', choco_flags: '--x86', cmake_flags: ' /wd4267 /wd4244 /wd4305 /wd4996', cmake_config: '--config Release', cmake_codac_config: '--config Debug', test_config: 'Release/', desc: 'Windows Visual Studio 2019 x86' } - - { os: windows-2019, shell: cmd, arch: x64, runtime: vc15, cmake_params: '-G "Visual Studio 16" -T v141 -A x64', cmake_flags: ' /wd4267 /wd4244 /wd4305 /wd4996', cmake_config: '--config Release', cmake_codac_config: '--config Debug', test_config: 'Release/', desc: 'Windows Visual Studio 2017 x64' } - - { os: windows-2019, shell: cmd, arch: x86, runtime: vc15, cmake_params: '-G "Visual Studio 16" -T v141 -A Win32', choco_flags: '--x86', cmake_flags: ' /wd4267 /wd4244 /wd4305 /wd4996', cmake_config: '--config Release', cmake_codac_config: '--config Debug', test_config: 'Release/', desc: 'Windows Visual Studio 2017 x86' } + - { os: windows-2022, shell: cmd, arch: x64, runtime: vc17, cmake_params: '-G "Visual Studio 17" -A x64', cmake_flags: ' /wd4267 /wd4244 /wd4305 /wd4996', test_config: 'Release/', desc: 'Windows Visual Studio 2022 x64' } + - { os: windows-2022, shell: cmd, arch: x86, runtime: vc17, cmake_params: '-G "Visual Studio 17" -A Win32', choco_flags: '--x86', cmake_flags: ' /wd4267 /wd4244 /wd4305 /wd4996', test_config: 'Release/', desc: 'Windows Visual Studio 2022 x86' } + - { os: windows-2019, shell: cmd, arch: x64, runtime: vc16, cmake_params: '-G "Visual Studio 16" -A x64', cmake_flags: ' /wd4267 /wd4244 /wd4305 /wd4996', test_config: 'Release/', desc: 'Windows Visual Studio 2019 x64' } + - { os: windows-2019, shell: cmd, arch: x86, runtime: vc16, cmake_params: '-G "Visual Studio 16" -A Win32', choco_flags: '--x86', cmake_flags: ' /wd4267 /wd4244 /wd4305 /wd4996', test_config: 'Release/', desc: 'Windows Visual Studio 2019 x86' } + - { os: windows-2019, shell: cmd, arch: x64, runtime: vc15, cmake_params: '-G "Visual Studio 16" -T v141 -A x64', cmake_flags: ' /wd4267 /wd4244 /wd4305 /wd4996', test_config: 'Release/', desc: 'Windows Visual Studio 2017 x64' } + - { os: windows-2019, shell: cmd, arch: x86, runtime: vc15, cmake_params: '-G "Visual Studio 16" -T v141 -A Win32', choco_flags: '--x86', cmake_flags: ' /wd4267 /wd4244 /wd4305 /wd4996', test_config: 'Release/', desc: 'Windows Visual Studio 2017 x86' } - { os: windows-2019, shell: cmd, arch: x64, runtime: mingw8, cmake_params: '-G "MSYS Makefiles"', cmake_flags: '-fPIC', desc: 'Windows MinGW 8.1.0 x64' } - { os: windows-2019, shell: cmd, arch: x86, runtime: mingw8, cmake_params: '-G "MSYS Makefiles"', cmake_flags: '-fPIC', choco_flags: '--x86', desc: 'Windows MinGW 8.1.0 x86' } - { os: windows-2019, shell: cmd, arch: x86, runtime: mingw7, cmake_params: '-G "MSYS Makefiles"', cmake_flags: '-fPIC', choco_flags: '--x86', desc: 'Windows Qt 5.12.6 MinGW 7.3.0 x86' } @@ -107,13 +107,13 @@ jobs: if: runner.os=='Linux' - run: brew install eigen if: runner.os=='macOS' - - run: git clone -b actions https://github.com/lebarsfa/ibex-lib.git ; cd ibex-lib ; mkdir build ; cd build ; cmake -E env CXXFLAGS="${{ matrix.cfg.cmake_flags }}" CFLAGS="${{ matrix.cfg.cmake_flags }}" cmake ${{ matrix.cfg.cmake_params }} -D CMAKE_INSTALL_PREFIX="../../ibex" .. ; cmake --build . ${{ matrix.cfg.cmake_config }} --target install ; cd ../.. + - run: git clone -b actions https://github.com/lebarsfa/ibex-lib.git ; cd ibex-lib ; mkdir build ; cd build ; cmake -E env CXXFLAGS="${{ matrix.cfg.cmake_flags }}" CFLAGS="${{ matrix.cfg.cmake_flags }}" cmake ${{ matrix.cfg.cmake_params }} -D CMAKE_INSTALL_PREFIX="../../ibex" .. ; cmake --build . --config Release --target install ; cd ../.. shell: bash if: ((runner.os=='Windows')&&(matrix.cfg.runtime!='mingw8'))||((runner.os!='Windows')&&(runner.os!='Linux')) - run: | mkdir build ; cd build cmake -E env CXXFLAGS="${{ matrix.cfg.cmake_flags }}" CFLAGS="${{ matrix.cfg.cmake_flags }}" cmake ${{ matrix.cfg.cmake_params }} -D CMAKE_PREFIX_PATH="ibex" -D CMAKE_INSTALL_PREFIX="../codac" .. - cmake --build . ${{ matrix.cfg.cmake_codac_config }} --target install + cmake --build . --config Debug --target install cd .. shell: bash - run: | @@ -121,7 +121,7 @@ jobs: wget https://community.chocolatey.org/api/v2/package/eigen/3.4.0 --no-check-certificate -nv ; unzip -q 3.4.0 -d eigen ; rm -Rf 3.4.0 eigen/*.xml eigen/*.nuspec eigen/_* eigen/package eigen/tools cp -Rf ../ibex . ; cp -Rf ../codac . ; cp -Rf ../tests/test_codac/* ./example/ ; cd .. ; zip -q -r codac_standalone_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip codac_standalone ; cd codac_standalone/example cmake ${{ matrix.cfg.cmake_params }} . - cmake --build . ${{ matrix.cfg.cmake_config }} + cmake --build . --config Release ./${{ matrix.cfg.test_config }}my_project cd ../.. shell: bash @@ -156,7 +156,7 @@ jobs: rm -Rf codac cd tests/test_codac cmake ${{ matrix.cfg.cmake_params }} . - cmake --build . ${{ matrix.cfg.cmake_config }} + cmake --build . --config Release ./${{ matrix.cfg.test_config }}my_project shell: bash if: ((runner.os=='Windows')&&(matrix.cfg.runtime=='mingw8'))||(runner.os=='Linux') From 2cebdea03bb58146656238e4201875cc4d66beb6 Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Fri, 10 Jun 2022 00:47:35 +0200 Subject: [PATCH 027/256] Updated catch.hpp for C++17 compatibility --- tests/catch/catch.hpp | 368 ++++++++++++++++++++++++++++++------------ 1 file changed, 261 insertions(+), 107 deletions(-) diff --git a/tests/catch/catch.hpp b/tests/catch/catch.hpp index ca410f41a..6bcdd0450 100644 --- a/tests/catch/catch.hpp +++ b/tests/catch/catch.hpp @@ -1,6 +1,6 @@ /* - * Catch v1.5.6 - * Generated: 2016-06-09 19:20:41.460328 + * Catch v1.6.1 + * Generated: 2017-01-20 12:33:53.497767 * ---------------------------------------------------------- * This file has been merged from multiple headers. Please don't edit it directly * Copyright (c) 2012 Two Blue Cubes Ltd. All rights reserved. @@ -60,21 +60,6 @@ // #included from: catch_common.h #define TWOBLUECUBES_CATCH_COMMON_H_INCLUDED -#define INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) name##line -#define INTERNAL_CATCH_UNIQUE_NAME_LINE( name, line ) INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) -#ifdef CATCH_CONFIG_COUNTER -# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __COUNTER__ ) -#else -# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ ) -#endif - -#define INTERNAL_CATCH_STRINGIFY2( expr ) #expr -#define INTERNAL_CATCH_STRINGIFY( expr ) INTERNAL_CATCH_STRINGIFY2( expr ) - -#include -#include -#include - // #included from: catch_compiler_capabilities.h #define TWOBLUECUBES_CATCH_COMPILER_CAPABILITIES_HPP_INCLUDED @@ -181,6 +166,7 @@ #if (_MSC_VER >= 1900 ) // (VC++ 13 (VS2015)) #define CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT #define CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS +#define CATCH_INTERNAL_CONFIG_CPP11_SHUFFLE #endif #endif // _MSC_VER @@ -246,6 +232,9 @@ # if !defined(CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR) # define CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR # endif +# if !defined(CATCH_INTERNAL_CONFIG_CPP11_SHUFFLE) +# define CATCH_INTERNAL_CONFIG_CPP11_SHUFFLE +# endif #endif // __cplusplus >= 201103L @@ -268,18 +257,24 @@ #if defined(CATCH_INTERNAL_CONFIG_VARIADIC_MACROS) && !defined(CATCH_CONFIG_NO_VARIADIC_MACROS) && !defined(CATCH_CONFIG_VARIADIC_MACROS) # define CATCH_CONFIG_VARIADIC_MACROS #endif -#if defined(CATCH_INTERNAL_CONFIG_CPP11_LONG_LONG) && !defined(CATCH_CONFIG_NO_LONG_LONG) && !defined(CATCH_CONFIG_CPP11_LONG_LONG) && !defined(CATCH_CONFIG_NO_CPP11) +#if defined(CATCH_INTERNAL_CONFIG_CPP11_LONG_LONG) && !defined(CATCH_CONFIG_CPP11_NO_LONG_LONG) && !defined(CATCH_CONFIG_CPP11_LONG_LONG) && !defined(CATCH_CONFIG_NO_CPP11) # define CATCH_CONFIG_CPP11_LONG_LONG #endif -#if defined(CATCH_INTERNAL_CONFIG_CPP11_OVERRIDE) && !defined(CATCH_CONFIG_NO_OVERRIDE) && !defined(CATCH_CONFIG_CPP11_OVERRIDE) && !defined(CATCH_CONFIG_NO_CPP11) +#if defined(CATCH_INTERNAL_CONFIG_CPP11_OVERRIDE) && !defined(CATCH_CONFIG_CPP11_NO_OVERRIDE) && !defined(CATCH_CONFIG_CPP11_OVERRIDE) && !defined(CATCH_CONFIG_NO_CPP11) # define CATCH_CONFIG_CPP11_OVERRIDE #endif -#if defined(CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR) && !defined(CATCH_CONFIG_NO_UNIQUE_PTR) && !defined(CATCH_CONFIG_CPP11_UNIQUE_PTR) && !defined(CATCH_CONFIG_NO_CPP11) +#if defined(CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR) && !defined(CATCH_CONFIG_CPP11_NO_UNIQUE_PTR) && !defined(CATCH_CONFIG_CPP11_UNIQUE_PTR) && !defined(CATCH_CONFIG_NO_CPP11) # define CATCH_CONFIG_CPP11_UNIQUE_PTR #endif -#if defined(CATCH_INTERNAL_CONFIG_COUNTER) && !defined(CATCH_CONFIG_NO_COUNTER) && !defined(CATCH_CONFIG_COUNTER) +// Use of __COUNTER__ is suppressed if __JETBRAINS_IDE__ is #defined (meaning we're being parsed by a JetBrains IDE for +// analytics) because, at time of writing, __COUNTER__ is not properly handled by it. +// This does not affect compilation +#if defined(CATCH_INTERNAL_CONFIG_COUNTER) && !defined(CATCH_CONFIG_NO_COUNTER) && !defined(CATCH_CONFIG_COUNTER) && !defined(__JETBRAINS_IDE__) # define CATCH_CONFIG_COUNTER #endif +#if defined(CATCH_INTERNAL_CONFIG_CPP11_SHUFFLE) && !defined(CATCH_CONFIG_CPP11_NO_SHUFFLE) && !defined(CATCH_CONFIG_CPP11_SHUFFLE) && !defined(CATCH_CONFIG_NO_CPP11) +# define CATCH_CONFIG_CPP11_SHUFFLE +#endif #if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS) # define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS @@ -315,6 +310,21 @@ # define CATCH_AUTO_PTR( T ) std::auto_ptr #endif +#define INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) name##line +#define INTERNAL_CATCH_UNIQUE_NAME_LINE( name, line ) INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) +#ifdef CATCH_CONFIG_COUNTER +# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __COUNTER__ ) +#else +# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ ) +#endif + +#define INTERNAL_CATCH_STRINGIFY2( expr ) #expr +#define INTERNAL_CATCH_STRINGIFY( expr ) INTERNAL_CATCH_STRINGIFY2( expr ) + +#include +#include +#include + namespace Catch { struct IConfig; @@ -404,9 +414,8 @@ namespace Catch { std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ); // This is just here to avoid compiler warnings with macro constants and boolean literals - inline bool isTrue( bool value ){ return value; } - inline bool alwaysTrue() { return true; } - inline bool alwaysFalse() { return false; } + inline bool alwaysTrue( std::size_t = 0 ) { return true; } + inline bool alwaysFalse( std::size_t = 0 ) { return false; } void throwLogicError( std::string const& message, SourceLineInfo const& locationInfo ); @@ -669,7 +678,7 @@ struct NameAndDesc { void registerTestCase ( ITestCase* testCase, - char const* class_name, + char const* className, NameAndDesc const& nameAndDesc, SourceLineInfo const& lineInfo ); @@ -683,13 +692,13 @@ struct AutoReg { template AutoReg ( void (C::*method)(), - char const* class_name, + char const* className, NameAndDesc const& nameAndDesc, SourceLineInfo const& lineInfo ) { registerTestCase ( new MethodTestCase( method ), - class_name, + className, nameAndDesc, lineInfo ); } @@ -1998,11 +2007,19 @@ namespace Catch { #define TWOBLUECUBES_CATCH_PLATFORM_H_INCLUDED #if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) -#define CATCH_PLATFORM_MAC +# define CATCH_PLATFORM_MAC #elif defined(__IPHONE_OS_VERSION_MIN_REQUIRED) -#define CATCH_PLATFORM_IPHONE +# define CATCH_PLATFORM_IPHONE +#elif defined(linux) || defined(__linux) || defined(__linux__) +# define CATCH_PLATFORM_LINUX #elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) -#define CATCH_PLATFORM_WINDOWS +# define CATCH_PLATFORM_WINDOWS +# if !defined(NOMINMAX) && !defined(CATCH_CONFIG_NO_NOMINMAX) +# define CATCH_DEFINES_NOMINMAX +# endif +# if !defined(WIN32_LEAN_AND_MEAN) && !defined(CATCH_CONFIG_NO_WIN32_LEAN_AND_MEAN) +# define CATCH_DEFINES_WIN32_LEAN_AND_MEAN +# endif #endif #include @@ -2019,25 +2036,36 @@ namespace Catch{ // http://cocoawithlove.com/2008/03/break-into-debugger.html #ifdef DEBUG #if defined(__ppc64__) || defined(__ppc__) - #define CATCH_BREAK_INTO_DEBUGGER() \ - if( Catch::isDebuggerActive() ) { \ + #define CATCH_TRAP() \ __asm__("li r0, 20\nsc\nnop\nli r0, 37\nli r4, 2\nsc\nnop\n" \ - : : : "memory","r0","r3","r4" ); \ - } + : : : "memory","r0","r3","r4" ) #else - #define CATCH_BREAK_INTO_DEBUGGER() if( Catch::isDebuggerActive() ) {__asm__("int $3\n" : : );} + #define CATCH_TRAP() _asm__("int $3\n" : : ) #endif #endif +#elif defined(CATCH_PLATFORM_LINUX) + // If we can use inline assembler, do it because this allows us to break + // directly at the location of the failing check instead of breaking inside + // raise() called from it, i.e. one stack frame below. + #if defined(__GNUC__) && (defined(__i386) || defined(__x86_64)) + #define CATCH_TRAP() asm volatile ("int $3") + #else // Fall back to the generic way. + #include + + #define CATCH_TRAP() raise(SIGTRAP) + #endif #elif defined(_MSC_VER) - #define CATCH_BREAK_INTO_DEBUGGER() if( Catch::isDebuggerActive() ) { __debugbreak(); } + #define CATCH_TRAP() __debugbreak() #elif defined(__MINGW32__) extern "C" __declspec(dllimport) void __stdcall DebugBreak(); - #define CATCH_BREAK_INTO_DEBUGGER() if( Catch::isDebuggerActive() ) { DebugBreak(); } + #define CATCH_TRAP() DebugBreak() #endif -#ifndef CATCH_BREAK_INTO_DEBUGGER -#define CATCH_BREAK_INTO_DEBUGGER() Catch::alwaysTrue(); +#ifdef CATCH_TRAP + #define CATCH_BREAK_INTO_DEBUGGER() if( Catch::isDebuggerActive() ) { CATCH_TRAP(); } +#else + #define CATCH_BREAK_INTO_DEBUGGER() Catch::alwaysTrue(); #endif // #included from: catch_interfaces_runner.h @@ -2073,7 +2101,7 @@ namespace Catch { __catchResult.useActiveException( Catch::ResultDisposition::Normal ); \ } \ INTERNAL_CATCH_REACT( __catchResult ) \ - } while( Catch::isTrue( false && !!(expr) ) ) // expr here is never evaluated at runtime but it forces the compiler to give it a look + } while( Catch::alwaysFalse( sizeof(expr) ) ) // expr here is never evaluated at runtime but it forces the compiler to give it a look /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_IF( expr, resultDisposition, macroName ) \ @@ -2668,6 +2696,26 @@ namespace Detail { return !operator==( rhs, lhs ); } + friend bool operator <= ( double lhs, Approx const& rhs ) + { + return lhs < rhs.m_value || lhs == rhs; + } + + friend bool operator <= ( Approx const& lhs, double rhs ) + { + return lhs.m_value < rhs || lhs == rhs; + } + + friend bool operator >= ( double lhs, Approx const& rhs ) + { + return lhs > rhs.m_value || lhs == rhs; + } + + friend bool operator >= ( Approx const& lhs, double rhs ) + { + return lhs.m_value > rhs || lhs == rhs; + } + Approx& epsilon( double newEpsilon ) { m_epsilon = newEpsilon; return *this; @@ -2826,7 +2874,7 @@ namespace Catch { }; TestCaseInfo( std::string const& _name, - std::string const& _class_name, + std::string const& _className, std::string const& _description, std::set const& _tags, SourceLineInfo const& _lineInfo ); @@ -2841,7 +2889,7 @@ namespace Catch { bool expectedToFail() const; std::string name; - std::string class_name; + std::string className; std::string description; std::set tags; std::set lcaseTags; @@ -2872,7 +2920,7 @@ namespace Catch { }; TestCase makeTestCase( ITestCase* testCase, - std::string const& class_name, + std::string const& className, std::string const& name, std::string const& description, SourceLineInfo const& lineInfo ); @@ -2964,9 +3012,9 @@ namespace Catch { std::string testCaseName = methodName.substr( 15 ); std::string name = Detail::getAnnotation( cls, "Name", testCaseName ); std::string desc = Detail::getAnnotation( cls, "Description", testCaseName ); - const char* class_name = class_getName( cls ); + const char* className = class_getName( cls ); - getMutableRegistryHub().registerTest( makeTestCase( new OcMethod( cls, selector ), class_name, name.c_str(), desc.c_str(), SourceLineInfo() ) ); + getMutableRegistryHub().registerTest( makeTestCase( new OcMethod( cls, selector ), className, name.c_str(), desc.c_str(), SourceLineInfo() ) ); noTestMethods++; } } @@ -3223,10 +3271,11 @@ namespace Catch { bool matches( TestCaseInfo const& testCase ) const { // All patterns in a filter must match for the filter to be a match - for( std::vector >::const_iterator it = m_patterns.begin(), itEnd = m_patterns.end(); it != itEnd; ++it ) + for( std::vector >::const_iterator it = m_patterns.begin(), itEnd = m_patterns.end(); it != itEnd; ++it ) { if( !(*it)->matches( testCase ) ) return false; - return true; + } + return true; } }; @@ -3256,11 +3305,12 @@ namespace Catch { namespace Catch { class TestSpecParser { - enum Mode{ None, Name, QuotedName, Tag }; + enum Mode{ None, Name, QuotedName, Tag, EscapedName }; Mode m_mode; bool m_exclusion; std::size_t m_start, m_pos; std::string m_arg; + std::vector m_escapeChars; TestSpec::Filter m_currentFilter; TestSpec m_testSpec; ITagAliasRegistry const* m_tagAliases; @@ -3273,6 +3323,7 @@ namespace Catch { m_exclusion = false; m_start = std::string::npos; m_arg = m_tagAliases->expandAliases( arg ); + m_escapeChars.clear(); for( m_pos = 0; m_pos < m_arg.size(); ++m_pos ) visitChar( m_arg[m_pos] ); if( m_mode == Name ) @@ -3291,6 +3342,7 @@ namespace Catch { case '~': m_exclusion = true; return; case '[': return startNewMode( Tag, ++m_pos ); case '"': return startNewMode( QuotedName, ++m_pos ); + case '\\': return escape(); default: startNewMode( Name, m_pos ); break; } } @@ -3306,7 +3358,11 @@ namespace Catch { addPattern(); startNewMode( Tag, ++m_pos ); } + else if( c == '\\' ) + escape(); } + else if( m_mode == EscapedName ) + m_mode = Name; else if( m_mode == QuotedName && c == '"' ) addPattern(); else if( m_mode == Tag && c == ']' ) @@ -3316,10 +3372,17 @@ namespace Catch { m_mode = mode; m_start = start; } + void escape() { + m_mode = EscapedName; + m_escapeChars.push_back( m_pos ); + } std::string subString() const { return m_arg.substr( m_start, m_pos - m_start ); } template void addPattern() { std::string token = subString(); + for( size_t i = 0; i < m_escapeChars.size(); ++i ) + token = token.substr( 0, m_escapeChars[i] ) + token.substr( m_escapeChars[i]+1 ); + m_escapeChars.clear(); if( startsWith( token, "exclude:" ) ) { m_exclusion = true; token = token.substr( 8 ); @@ -3427,6 +3490,7 @@ namespace Catch { #include #include #include +#include namespace Catch { @@ -3994,9 +4058,12 @@ namespace Clara { inline void convertInto( std::string const& _source, std::string& _dest ) { _dest = _source; } + char toLowerCh(char c) { + return static_cast( ::tolower( c ) ); + } inline void convertInto( std::string const& _source, bool& _dest ) { std::string sourceLC = _source; - std::transform( sourceLC.begin(), sourceLC.end(), sourceLC.begin(), ::tolower ); + std::transform( sourceLC.begin(), sourceLC.end(), sourceLC.begin(), toLowerCh ); if( sourceLC == "y" || sourceLC == "1" || sourceLC == "true" || sourceLC == "yes" || sourceLC == "on" ) _dest = true; else if( sourceLC == "n" || sourceLC == "0" || sourceLC == "false" || sourceLC == "no" || sourceLC == "off" ) @@ -4719,8 +4786,11 @@ namespace Catch { std::string line; while( std::getline( f, line ) ) { line = trim(line); - if( !line.empty() && !startsWith( line, "#" ) ) - addTestOrTags( config, "\"" + line + "\"," ); + if( !line.empty() && !startsWith( line, "#" ) ) { + if( !startsWith( line, "\"" ) ) + line = "\"" + line + "\""; + addTestOrTags( config, line + "," ); + } } } @@ -5368,7 +5438,10 @@ namespace Catch { ++it ) { matchedTests++; TestCaseInfo const& testCaseInfo = it->getTestCaseInfo(); - Catch::cout() << testCaseInfo.name << std::endl; + if( startsWith( testCaseInfo.name, "#" ) ) + Catch::cout() << "\"" << testCaseInfo.name << "\"" << std::endl; + else + Catch::cout() << testCaseInfo.name << std::endl; } return matchedTests; } @@ -6447,18 +6520,14 @@ namespace Catch { #include #include -#ifdef CATCH_CPP14_OR_GREATER -#include -#endif - namespace Catch { struct RandomNumberGenerator { - typedef int result_type; + typedef std::ptrdiff_t result_type; result_type operator()( result_type n ) const { return std::rand() % n; } -#ifdef CATCH_CPP14_OR_GREATER +#ifdef CATCH_CONFIG_CPP11_SHUFFLE static constexpr result_type min() { return 0; } static constexpr result_type max() { return 1000000; } result_type operator()() const { return std::rand() % max(); } @@ -6466,7 +6535,7 @@ namespace Catch { template static void shuffle( V& vector ) { RandomNumberGenerator rng; -#ifdef CATCH_CPP14_OR_GREATER +#ifdef CATCH_CONFIG_CPP11_SHUFFLE std::shuffle( vector.begin(), vector.end(), rng ); #else std::random_shuffle( vector.begin(), vector.end(), rng ); @@ -6589,16 +6658,16 @@ namespace Catch { }; inline std::string extractClassName( std::string const& classOrQualifiedMethodName ) { - std::string class_name = classOrQualifiedMethodName; - if( startsWith( class_name, "&" ) ) + std::string className = classOrQualifiedMethodName; + if( startsWith( className, "&" ) ) { - std::size_t lastColons = class_name.rfind( "::" ); - std::size_t penultimateColons = class_name.rfind( "::", lastColons-1 ); + std::size_t lastColons = className.rfind( "::" ); + std::size_t penultimateColons = className.rfind( "::", lastColons-1 ); if( penultimateColons == std::string::npos ) penultimateColons = 1; - class_name = class_name.substr( penultimateColons, lastColons-penultimateColons ); + className = className.substr( penultimateColons, lastColons-penultimateColons ); } - return class_name; + return className; } void registerTestCase @@ -6933,6 +7002,11 @@ namespace Catch { Context( Context const& ); void operator=( Context const& ); + public: + virtual ~Context() { + deleteAllValues( m_generatorsByTestName ); + } + public: // IContext virtual IResultCapture* getResultCapture() { return m_resultCapture; @@ -7046,8 +7120,15 @@ namespace Catch { #if defined ( CATCH_CONFIG_COLOUR_WINDOWS ) ///////////////////////////////////////// -#ifndef NOMINMAX -#define NOMINMAX +// #included from: catch_windows_h_proxy.h + +#define TWOBLUECUBES_CATCH_WINDOWS_H_PROXY_H_INCLUDED + +#ifdef CATCH_DEFINES_NOMINMAX +# define NOMINMAX +#endif +#ifdef CATCH_DEFINES_WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN #endif #ifdef __AFXDLL @@ -7056,6 +7137,13 @@ namespace Catch { #include #endif +#ifdef CATCH_DEFINES_NOMINMAX +# undef NOMINMAX +#endif +#ifdef CATCH_DEFINES_WIN32_LEAN_AND_MEAN +# undef WIN32_LEAN_AND_MEAN +#endif + namespace Catch { namespace { @@ -7136,7 +7224,7 @@ namespace { case Colour::White: return setColour( "[0m" ); case Colour::Red: return setColour( "[0;31m" ); case Colour::Green: return setColour( "[0;32m" ); - case Colour::Blue: return setColour( "[0:34m" ); + case Colour::Blue: return setColour( "[0;34m" ); case Colour::Cyan: return setColour( "[0;36m" ); case Colour::Yellow: return setColour( "[0;33m" ); case Colour::Grey: return setColour( "[1;30m" ); @@ -7393,7 +7481,7 @@ namespace Catch { } TestCase makeTestCase( ITestCase* _testCase, - std::string const& _class_name, + std::string const& _className, std::string const& _name, std::string const& _descOrTags, SourceLineInfo const& _lineInfo ) @@ -7433,7 +7521,7 @@ namespace Catch { tags.insert( "." ); } - TestCaseInfo info( _name, _class_name, desc, tags, _lineInfo ); + TestCaseInfo info( _name, _className, desc, tags, _lineInfo ); return TestCase( _testCase, info ); } @@ -7453,12 +7541,12 @@ namespace Catch { } TestCaseInfo::TestCaseInfo( std::string const& _name, - std::string const& _class_name, + std::string const& _className, std::string const& _description, std::set const& _tags, SourceLineInfo const& _lineInfo ) : name( _name ), - class_name( _class_name ), + className( _className ), description( _description ), lineInfo( _lineInfo ), properties( None ) @@ -7468,7 +7556,7 @@ namespace Catch { TestCaseInfo::TestCaseInfo( TestCaseInfo const& other ) : name( other.name ), - class_name( other.class_name ), + className( other.className ), description( other.description ), tags( other.tags ), lcaseTags( other.lcaseTags ), @@ -7506,7 +7594,7 @@ namespace Catch { void TestCase::swap( TestCase& other ) { test.swap( other.test ); name.swap( other.name ); - class_name.swap( other.class_name ); + className.swap( other.className ); description.swap( other.description ); tags.swap( other.tags ); lcaseTags.swap( other.lcaseTags ); @@ -7522,7 +7610,7 @@ namespace Catch { bool TestCase::operator == ( TestCase const& other ) const { return test.get() == other.test.get() && name == other.name && - class_name == other.class_name; + className == other.className; } bool TestCase::operator < ( TestCase const& other ) const { @@ -7571,7 +7659,7 @@ namespace Catch { return os; } - Version libraryVersion( 1, 5, 6, "", 0 ); + Version libraryVersion( 1, 6, 1, "", 0 ); } @@ -7742,7 +7830,6 @@ namespace Catch #endif #ifdef CATCH_PLATFORM_WINDOWS -#include #else #include #endif @@ -7802,8 +7889,11 @@ namespace Catch { bool contains( std::string const& s, std::string const& infix ) { return s.find( infix ) != std::string::npos; } + char toLowerCh(char c) { + return static_cast( ::tolower( c ) ); + } void toLowerInPlace( std::string& s ) { - std::transform( s.begin(), s.end(), s.begin(), ::tolower ); + std::transform( s.begin(), s.end(), s.begin(), toLowerCh ); } std::string toLower( std::string const& s ) { std::string lc = s; @@ -7979,6 +8069,33 @@ namespace Catch { } } // namespace Catch +#elif defined(CATCH_PLATFORM_LINUX) + #include + #include + + namespace Catch{ + // The standard POSIX way of detecting a debugger is to attempt to + // ptrace() the process, but this needs to be done from a child and not + // this process itself to still allow attaching to this process later + // if wanted, so is rather heavy. Under Linux we have the PID of the + // "debugger" (which doesn't need to be gdb, of course, it could also + // be strace, for example) in /proc/$PID/status, so just get it from + // there instead. + bool isDebuggerActive(){ + std::ifstream in("/proc/self/status"); + for( std::string line; std::getline(in, line); ) { + static const int PREFIX_LEN = 11; + if( line.compare(0, PREFIX_LEN, "TracerPid:\t") == 0 ) { + // We're traced if the PID is not 0 and no other PID starts + // with 0 digit, so it's enough to check for just a single + // character. + return line.length() > PREFIX_LEN && line[PREFIX_LEN] != '0'; + } + } + + return false; + } + } // namespace Catch #elif defined(_MSC_VER) extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent(); namespace Catch { @@ -8335,7 +8452,7 @@ namespace Catch { } std::string ResultBuilder::reconstructExpression() const { if( m_exprComponents.op == "" ) - return m_exprComponents.lhs.empty() ? m_assertionInfo.capturedExpression : m_exprComponents.op + m_exprComponents.lhs; + return m_exprComponents.lhs.empty() ? m_assertionInfo.capturedExpression : m_exprComponents.lhs; else if( m_exprComponents.op == "matches" ) return m_exprComponents.lhs + " " + m_exprComponents.rhs; else if( m_exprComponents.op != "!" ) { @@ -8951,9 +9068,10 @@ namespace Catch { break; default: - // Escape control chars - based on contribution by @espenalb in PR #465 - if ( ( c < '\x09' ) || ( c > '\x0D' && c < '\x20') || c=='\x7F' ) - os << "&#x" << std::uppercase << std::hex << static_cast( c ); + // Escape control chars - based on contribution by @espenalb in PR #465 and + // by @mrpi PR #588 + if ( ( c >= 0 && c < '\x09' ) || ( c > '\x0D' && c < '\x20') || c=='\x7F' ) + os << "&#x" << std::uppercase << std::hex << std::setfill('0') << std::setw(2) << static_cast( c ) << ';'; else os << c; } @@ -9008,13 +9126,20 @@ namespace Catch { : m_tagIsOpen( false ), m_needsNewline( false ), m_os( &Catch::cout() ) - {} + { + // We encode control characters, which requires + // XML 1.1 + // see http://stackoverflow.com/questions/404107/why-are-control-characters-illegal-in-xml-1-0 + *m_os << "\n"; + } XmlWriter( std::ostream& os ) : m_tagIsOpen( false ), m_needsNewline( false ), m_os( &os ) - {} + { + *m_os << "\n"; + } ~XmlWriter() { while( !m_tags.empty() ) @@ -9148,6 +9273,7 @@ namespace Catch { public: XmlReporter( ReporterConfig const& _config ) : StreamingReporterBase( _config ), + m_xml(_config.stream()), m_sectionDepth( 0 ) { m_reporterPrefs.shouldRedirectStdOut = true; @@ -9167,7 +9293,6 @@ namespace Catch { virtual void testRunStarting( TestRunInfo const& testInfo ) CATCH_OVERRIDE { StreamingReporterBase::testRunStarting( testInfo ); - m_xml.setStream( stream ); m_xml.startElement( "Catch" ); if( !m_config->name().empty() ) m_xml.writeAttribute( "name", m_config->name() ); @@ -9181,7 +9306,7 @@ namespace Catch { virtual void testCaseStarting( TestCaseInfo const& testInfo ) CATCH_OVERRIDE { StreamingReporterBase::testCaseStarting(testInfo); - m_xml.startElement( "TestCase" ).writeAttribute( "name", trim( testInfo.name ) ); + m_xml.startElement( "TestCase" ).writeAttribute( "name", testInfo.name ); if ( m_config->showDurations() == ShowDurations::Always ) m_testCaseTimer.start(); @@ -9243,7 +9368,7 @@ namespace Catch { .writeText( assertionResult.getMessage() ); break; case ResultWas::FatalErrorCondition: - m_xml.scopedElement( "Fatal Error Condition" ) + m_xml.scopedElement( "FatalErrorCondition" ) .writeAttribute( "filename", assertionResult.getSourceInfo().file ) .writeAttribute( "line", assertionResult.getSourceInfo().line ) .writeText( assertionResult.getMessage() ); @@ -9331,6 +9456,35 @@ namespace Catch { namespace Catch { + namespace { + std::string getCurrentTimestamp() { + // Beware, this is not reentrant because of backward compatibility issues + // Also, UTC only, again because of backward compatibility (%z is C++11) + time_t rawtime; + std::time(&rawtime); + const size_t timeStampSize = sizeof("2017-01-16T17:06:45Z"); + +#ifdef CATCH_PLATFORM_WINDOWS + std::tm timeInfo = {}; + gmtime_s(&timeInfo, &rawtime); +#else + std::tm* timeInfo; + timeInfo = std::gmtime(&rawtime); +#endif + + char timeStamp[timeStampSize]; + const char * const fmt = "%Y-%m-%dT%H:%M:%SZ"; + +#ifdef CATCH_PLATFORM_WINDOWS + std::strftime(timeStamp, timeStampSize, fmt, &timeInfo); +#else + std::strftime(timeStamp, timeStampSize, fmt, timeInfo); +#endif + return std::string(timeStamp); + } + + } + class JunitReporter : public CumulativeReporterBase { public: JunitReporter( ReporterConfig const& _config ) @@ -9395,7 +9549,7 @@ namespace Catch { xml.writeAttribute( "time", "" ); else xml.writeAttribute( "time", suiteTime ); - xml.writeAttribute( "timestamp", "tbd" ); // !TBD + xml.writeAttribute( "timestamp", getCurrentTimestamp() ); // Write test cases for( TestGroupNode::ChildNodes::const_iterator @@ -9416,16 +9570,16 @@ namespace Catch { assert( testCaseNode.children.size() == 1 ); SectionNode const& rootSection = *testCaseNode.children.front(); - std::string class_name = stats.testInfo.class_name; + std::string className = stats.testInfo.className; - if( class_name.empty() ) { + if( className.empty() ) { if( rootSection.childSections.empty() ) - class_name = "global"; + className = "global"; } - writeSection( class_name, "", rootSection ); + writeSection( className, "", rootSection ); } - void writeSection( std::string const& class_name, + void writeSection( std::string const& className, std::string const& rootName, SectionNode const& sectionNode ) { std::string name = trim( sectionNode.stats.sectionInfo.name ); @@ -9436,12 +9590,12 @@ namespace Catch { !sectionNode.stdOut.empty() || !sectionNode.stdErr.empty() ) { XmlWriter::ScopedElement e = xml.scopedElement( "testcase" ); - if( class_name.empty() ) { + if( className.empty() ) { xml.writeAttribute( "classname", name ); xml.writeAttribute( "name", "root" ); } else { - xml.writeAttribute( "classname", class_name ); + xml.writeAttribute( "classname", className ); xml.writeAttribute( "name", name ); } xml.writeAttribute( "time", Catch::toString( sectionNode.stats.durationInSeconds ) ); @@ -9458,10 +9612,10 @@ namespace Catch { itEnd = sectionNode.childSections.end(); it != itEnd; ++it ) - if( class_name.empty() ) + if( className.empty() ) writeSection( name, "", **it ); else - writeSection( class_name, name, **it ); + writeSection( className, name, **it ); } void writeAssertions( SectionNode const& sectionNode ) { @@ -10378,12 +10532,12 @@ int main (int argc, char * const argv[]) { #define CATCH_CHECKED_ELSE( expr ) INTERNAL_CATCH_ELSE( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECKED_ELSE" ) #define CATCH_CHECK_NOFAIL( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, "CATCH_CHECK_NOFAIL" ) -#define CATCH_CHECK_THROWS( expr ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_THROWS" ) +#define CATCH_CHECK_THROWS( expr ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::ContinueOnFailure, "", "CATCH_CHECK_THROWS" ) #define CATCH_CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_THROWS_AS" ) #define CATCH_CHECK_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::ContinueOnFailure, matcher, "CATCH_CHECK_THROWS_WITH" ) #define CATCH_CHECK_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_NOTHROW" ) -#define CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_THAT" ) +#define CATCH_CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_THAT" ) #define CATCH_REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::Normal, "CATCH_REQUIRE_THAT" ) #define CATCH_INFO( msg ) INTERNAL_CATCH_INFO( msg, "CATCH_INFO" ) @@ -10394,7 +10548,7 @@ int main (int argc, char * const argv[]) { #ifdef CATCH_CONFIG_VARIADIC_MACROS #define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ ) - #define CATCH_TEST_CASE_METHOD( class_name, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( class_name, __VA_ARGS__ ) + #define CATCH_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ ) #define CATCH_METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ ) #define CATCH_REGISTER_TEST_CASE( Function, ... ) INTERNAL_CATCH_REGISTER_TESTCASE( Function, __VA_ARGS__ ) #define CATCH_SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ ) @@ -10402,7 +10556,7 @@ int main (int argc, char * const argv[]) { #define CATCH_SUCCEED( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "CATCH_SUCCEED", __VA_ARGS__ ) #else #define CATCH_TEST_CASE( name, description ) INTERNAL_CATCH_TESTCASE( name, description ) - #define CATCH_TEST_CASE_METHOD( class_name, name, description ) INTERNAL_CATCH_TEST_CASE_METHOD( class_name, name, description ) + #define CATCH_TEST_CASE_METHOD( className, name, description ) INTERNAL_CATCH_TEST_CASE_METHOD( className, name, description ) #define CATCH_METHOD_AS_TEST_CASE( method, name, description ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, name, description ) #define CATCH_REGISTER_TEST_CASE( function, name, description ) INTERNAL_CATCH_REGISTER_TESTCASE( function, name, description ) #define CATCH_SECTION( name, description ) INTERNAL_CATCH_SECTION( name, description ) @@ -10419,10 +10573,10 @@ int main (int argc, char * const argv[]) { // "BDD-style" convenience wrappers #ifdef CATCH_CONFIG_VARIADIC_MACROS #define CATCH_SCENARIO( ... ) CATCH_TEST_CASE( "Scenario: " __VA_ARGS__ ) -#define CATCH_SCENARIO_METHOD( class_name, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( class_name, "Scenario: " __VA_ARGS__ ) +#define CATCH_SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " __VA_ARGS__ ) #else #define CATCH_SCENARIO( name, tags ) CATCH_TEST_CASE( "Scenario: " name, tags ) -#define CATCH_SCENARIO_METHOD( class_name, name, tags ) INTERNAL_CATCH_TEST_CASE_METHOD( class_name, "Scenario: " name, tags ) +#define CATCH_SCENARIO_METHOD( className, name, tags ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " name, tags ) #endif #define CATCH_GIVEN( desc ) CATCH_SECTION( std::string( "Given: ") + desc, "" ) #define CATCH_WHEN( desc ) CATCH_SECTION( std::string( " When: ") + desc, "" ) @@ -10463,7 +10617,7 @@ int main (int argc, char * const argv[]) { #ifdef CATCH_CONFIG_VARIADIC_MACROS #define TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ ) - #define TEST_CASE_METHOD( class_name, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( class_name, __VA_ARGS__ ) + #define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ ) #define METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ ) #define REGISTER_TEST_CASE( Function, ... ) INTERNAL_CATCH_REGISTER_TESTCASE( Function, __VA_ARGS__ ) #define SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ ) @@ -10471,7 +10625,7 @@ int main (int argc, char * const argv[]) { #define SUCCEED( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "SUCCEED", __VA_ARGS__ ) #else #define TEST_CASE( name, description ) INTERNAL_CATCH_TESTCASE( name, description ) - #define TEST_CASE_METHOD( class_name, name, description ) INTERNAL_CATCH_TEST_CASE_METHOD( class_name, name, description ) + #define TEST_CASE_METHOD( className, name, description ) INTERNAL_CATCH_TEST_CASE_METHOD( className, name, description ) #define METHOD_AS_TEST_CASE( method, name, description ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, name, description ) #define REGISTER_TEST_CASE( method, name, description ) INTERNAL_CATCH_REGISTER_TESTCASE( method, name, description ) #define SECTION( name, description ) INTERNAL_CATCH_SECTION( name, description ) @@ -10492,10 +10646,10 @@ int main (int argc, char * const argv[]) { // "BDD-style" convenience wrappers #ifdef CATCH_CONFIG_VARIADIC_MACROS #define SCENARIO( ... ) TEST_CASE( "Scenario: " __VA_ARGS__ ) -#define SCENARIO_METHOD( class_name, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( class_name, "Scenario: " __VA_ARGS__ ) +#define SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " __VA_ARGS__ ) #else #define SCENARIO( name, tags ) TEST_CASE( "Scenario: " name, tags ) -#define SCENARIO_METHOD( class_name, name, tags ) INTERNAL_CATCH_TEST_CASE_METHOD( class_name, "Scenario: " name, tags ) +#define SCENARIO_METHOD( className, name, tags ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " name, tags ) #endif #define GIVEN( desc ) SECTION( std::string(" Given: ") + desc, "" ) #define WHEN( desc ) SECTION( std::string(" When: ") + desc, "" ) From 995bdf81f40321096507c2ded8165efcab496f30 Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Tue, 9 Aug 2022 20:46:15 +0200 Subject: [PATCH 028/256] Added more tests --- .github/workflows/macosmatrix.yml | 5 +++-- .github/workflows/unixmatrix.yml | 3 ++- .github/workflows/vcmatrix.yml | 5 +++-- scripts/docker/build_pybinding.sh | 5 ++++- src/unsupported/CMakeLists.txt | 2 +- 5 files changed, 13 insertions(+), 7 deletions(-) diff --git a/.github/workflows/macosmatrix.yml b/.github/workflows/macosmatrix.yml index 83953c682..0a51ae7c0 100644 --- a/.github/workflows/macosmatrix.yml +++ b/.github/workflows/macosmatrix.yml @@ -46,9 +46,10 @@ jobs: shell: bash - run: | mkdir build ; cd build - cmake -E env CXXFLAGS="${{ matrix.cfg.cmake_flags }}" CFLAGS="${{ matrix.cfg.cmake_flags }}" cmake ${{ matrix.cfg.cmake_params }} -D CMAKE_PREFIX_PATH="ibex" -D CMAKE_INSTALL_PREFIX="../codac" -D WITH_PYTHON=ON .. + cmake -E env CXXFLAGS="${{ matrix.cfg.cmake_flags }}" CFLAGS="${{ matrix.cfg.cmake_flags }}" cmake ${{ matrix.cfg.cmake_params }} -D CMAKE_PREFIX_PATH="ibex" -D CMAKE_INSTALL_PREFIX="../codac" -D BUILD_TESTS=ON -D WITH_PYTHON=ON .. cmake --build . --config Release --target install - cmake --build . --config Release --target pip_package ; cp `ls *.whl` ../`ls *.whl | sed "s/py3-none-any/cp${{ matrix.cfg.py_v_maj }}${{ matrix.cfg.py_v_min }}-cp${{ matrix.cfg.py_v_maj }}${{ matrix.cfg.py_v_min }}${{ matrix.cfg.cpcfg }}/"` ; pip install ../*.whl ; python -c "import sys; print(sys.version)" ; python ../examples/tuto/01_getting_started/01_getting_started.py + cmake --build . --config Release --target test -- ARGS="-V --output-on-failure" + cmake --build . --config Release --target pip_package ; cp `ls *.whl` ../`ls *.whl | sed "s/py3-none-any/cp${{ matrix.cfg.py_v_maj }}${{ matrix.cfg.py_v_min }}-cp${{ matrix.cfg.py_v_maj }}${{ matrix.cfg.py_v_min }}${{ matrix.cfg.cpcfg }}/"` ; pip install ../*.whl ; python -c "import sys; print(sys.version)" ; python ../examples/tuto/01_getting_started/01_getting_started.py ; pip install numpy ; python -m unittest discover codac.tests cd .. shell: bash - uses: xresloader/upload-to-github-release@v1 diff --git a/.github/workflows/unixmatrix.yml b/.github/workflows/unixmatrix.yml index 441e424cf..161e88a9b 100644 --- a/.github/workflows/unixmatrix.yml +++ b/.github/workflows/unixmatrix.yml @@ -112,8 +112,9 @@ jobs: if: ((runner.os=='Windows')&&(matrix.cfg.runtime!='mingw8'))||((runner.os!='Windows')&&(runner.os!='Linux')) - run: | mkdir build ; cd build - cmake -E env CXXFLAGS="${{ matrix.cfg.cmake_flags }}" CFLAGS="${{ matrix.cfg.cmake_flags }}" cmake ${{ matrix.cfg.cmake_params }} -D CMAKE_PREFIX_PATH="ibex" -D CMAKE_INSTALL_PREFIX="../codac" .. + cmake -E env CXXFLAGS="${{ matrix.cfg.cmake_flags }}" CFLAGS="${{ matrix.cfg.cmake_flags }}" cmake ${{ matrix.cfg.cmake_params }} -D CMAKE_PREFIX_PATH="ibex" -D CMAKE_INSTALL_PREFIX="../codac" -D BUILD_TESTS=ON .. cmake --build . --config Debug --target install + cmake --build . --config Debug --target test -- ARGS="-V --output-on-failure" cd .. shell: bash - run: | diff --git a/.github/workflows/vcmatrix.yml b/.github/workflows/vcmatrix.yml index 91804df75..8b465e999 100644 --- a/.github/workflows/vcmatrix.yml +++ b/.github/workflows/vcmatrix.yml @@ -59,9 +59,10 @@ jobs: shell: bash - run: | mkdir build ; cd build - cmake -E env CXXFLAGS=" /wd4267 /wd4244 /wd4305 /wd4996" CFLAGS=" /wd4267 /wd4244 /wd4305 /wd4996" cmake ${{ matrix.cfg.cmake_params }} -D CMAKE_PREFIX_PATH="ibex" -D CMAKE_INSTALL_PREFIX="../codac" -D WITH_PYTHON=ON .. + cmake -E env CXXFLAGS=" /wd4267 /wd4244 /wd4305 /wd4996" CFLAGS=" /wd4267 /wd4244 /wd4305 /wd4996" cmake ${{ matrix.cfg.cmake_params }} -D CMAKE_PREFIX_PATH="ibex" -D CMAKE_INSTALL_PREFIX="../codac" -D BUILD_TESTS=ON -D WITH_PYTHON=ON .. cmake --build . --config Release --target install - cmake --build . --config Release --target pip_package ; cp `ls *.whl` ../`ls *.whl | sed "s/py3-none-any/cp${{ matrix.cfg.py_v_maj }}${{ matrix.cfg.py_v_min }}-cp${{ matrix.cfg.py_v_maj }}${{ matrix.cfg.py_v_min }}${{ matrix.cfg.cpcfg }}/"` ; pip install ../*.whl ; python -c "import sys; print(sys.version)" ; python ../examples/tuto/01_getting_started/01_getting_started.py + cmake --build . --config Release --target test -- ARGS="-V --output-on-failure" + cmake --build . --config Release --target pip_package ; cp `ls *.whl` ../`ls *.whl | sed "s/py3-none-any/cp${{ matrix.cfg.py_v_maj }}${{ matrix.cfg.py_v_min }}-cp${{ matrix.cfg.py_v_maj }}${{ matrix.cfg.py_v_min }}${{ matrix.cfg.cpcfg }}/"` ; pip install ../*.whl ; python -c "import sys; print(sys.version)" ; python ../examples/tuto/01_getting_started/01_getting_started.py ; pip install numpy ; python -m unittest discover codac.tests cd .. shell: bash - uses: xresloader/upload-to-github-release@v1 diff --git a/scripts/docker/build_pybinding.sh b/scripts/docker/build_pybinding.sh index be0daf235..67f806577 100644 --- a/scripts/docker/build_pybinding.sh +++ b/scripts/docker/build_pybinding.sh @@ -15,7 +15,10 @@ for PYBIN in /opt/python/cp3*/bin; do cmake -E env CXXFLAGS="-fPIC" CFLAGS="-fPIC" cmake -DPYTHON_EXECUTABLE=${PYBIN}/python -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTS=ON -DWITH_TUBE_TREE=OFF -DWITH_CAPD=OFF -DWITH_PYTHON=ON .. make -j2 - make test ARGS="-V"s + make test ARGS="-V --output-on-failure" + echo "start of Testing/Temporary/LastTest.log" + cat Testing/Temporary/LastTest.log + echo "end of Testing/Temporary/LastTest.log" make pip_package echo "copy wheel and clean build_dir" for whl in *.whl; do diff --git a/src/unsupported/CMakeLists.txt b/src/unsupported/CMakeLists.txt index 8d8af4335..758fa4604 100644 --- a/src/unsupported/CMakeLists.txt +++ b/src/unsupported/CMakeLists.txt @@ -66,7 +66,7 @@ ################################################################################ add_library(codac-unsupported ${SRC}) - target_compile_options(codac-unsupported PUBLIC -w) + #target_compile_options(codac-unsupported PUBLIC -w) # todo: find a clean way to access codac header files? set(CODAC_HEADERS_DIR ${CMAKE_CURRENT_BINARY_DIR}/../../include) From b121210b3eb4e84feb101be6308a3bb2f26b73ac Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Tue, 9 Aug 2022 22:17:59 +0200 Subject: [PATCH 029/256] Disabled tests for Windows and macOS --- .github/workflows/macosmatrix.yml | 5 ++--- .github/workflows/unixmatrix.yml | 3 +-- .github/workflows/vcmatrix.yml | 5 ++--- 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/.github/workflows/macosmatrix.yml b/.github/workflows/macosmatrix.yml index 0a51ae7c0..83953c682 100644 --- a/.github/workflows/macosmatrix.yml +++ b/.github/workflows/macosmatrix.yml @@ -46,10 +46,9 @@ jobs: shell: bash - run: | mkdir build ; cd build - cmake -E env CXXFLAGS="${{ matrix.cfg.cmake_flags }}" CFLAGS="${{ matrix.cfg.cmake_flags }}" cmake ${{ matrix.cfg.cmake_params }} -D CMAKE_PREFIX_PATH="ibex" -D CMAKE_INSTALL_PREFIX="../codac" -D BUILD_TESTS=ON -D WITH_PYTHON=ON .. + cmake -E env CXXFLAGS="${{ matrix.cfg.cmake_flags }}" CFLAGS="${{ matrix.cfg.cmake_flags }}" cmake ${{ matrix.cfg.cmake_params }} -D CMAKE_PREFIX_PATH="ibex" -D CMAKE_INSTALL_PREFIX="../codac" -D WITH_PYTHON=ON .. cmake --build . --config Release --target install - cmake --build . --config Release --target test -- ARGS="-V --output-on-failure" - cmake --build . --config Release --target pip_package ; cp `ls *.whl` ../`ls *.whl | sed "s/py3-none-any/cp${{ matrix.cfg.py_v_maj }}${{ matrix.cfg.py_v_min }}-cp${{ matrix.cfg.py_v_maj }}${{ matrix.cfg.py_v_min }}${{ matrix.cfg.cpcfg }}/"` ; pip install ../*.whl ; python -c "import sys; print(sys.version)" ; python ../examples/tuto/01_getting_started/01_getting_started.py ; pip install numpy ; python -m unittest discover codac.tests + cmake --build . --config Release --target pip_package ; cp `ls *.whl` ../`ls *.whl | sed "s/py3-none-any/cp${{ matrix.cfg.py_v_maj }}${{ matrix.cfg.py_v_min }}-cp${{ matrix.cfg.py_v_maj }}${{ matrix.cfg.py_v_min }}${{ matrix.cfg.cpcfg }}/"` ; pip install ../*.whl ; python -c "import sys; print(sys.version)" ; python ../examples/tuto/01_getting_started/01_getting_started.py cd .. shell: bash - uses: xresloader/upload-to-github-release@v1 diff --git a/.github/workflows/unixmatrix.yml b/.github/workflows/unixmatrix.yml index 161e88a9b..441e424cf 100644 --- a/.github/workflows/unixmatrix.yml +++ b/.github/workflows/unixmatrix.yml @@ -112,9 +112,8 @@ jobs: if: ((runner.os=='Windows')&&(matrix.cfg.runtime!='mingw8'))||((runner.os!='Windows')&&(runner.os!='Linux')) - run: | mkdir build ; cd build - cmake -E env CXXFLAGS="${{ matrix.cfg.cmake_flags }}" CFLAGS="${{ matrix.cfg.cmake_flags }}" cmake ${{ matrix.cfg.cmake_params }} -D CMAKE_PREFIX_PATH="ibex" -D CMAKE_INSTALL_PREFIX="../codac" -D BUILD_TESTS=ON .. + cmake -E env CXXFLAGS="${{ matrix.cfg.cmake_flags }}" CFLAGS="${{ matrix.cfg.cmake_flags }}" cmake ${{ matrix.cfg.cmake_params }} -D CMAKE_PREFIX_PATH="ibex" -D CMAKE_INSTALL_PREFIX="../codac" .. cmake --build . --config Debug --target install - cmake --build . --config Debug --target test -- ARGS="-V --output-on-failure" cd .. shell: bash - run: | diff --git a/.github/workflows/vcmatrix.yml b/.github/workflows/vcmatrix.yml index 8b465e999..91804df75 100644 --- a/.github/workflows/vcmatrix.yml +++ b/.github/workflows/vcmatrix.yml @@ -59,10 +59,9 @@ jobs: shell: bash - run: | mkdir build ; cd build - cmake -E env CXXFLAGS=" /wd4267 /wd4244 /wd4305 /wd4996" CFLAGS=" /wd4267 /wd4244 /wd4305 /wd4996" cmake ${{ matrix.cfg.cmake_params }} -D CMAKE_PREFIX_PATH="ibex" -D CMAKE_INSTALL_PREFIX="../codac" -D BUILD_TESTS=ON -D WITH_PYTHON=ON .. + cmake -E env CXXFLAGS=" /wd4267 /wd4244 /wd4305 /wd4996" CFLAGS=" /wd4267 /wd4244 /wd4305 /wd4996" cmake ${{ matrix.cfg.cmake_params }} -D CMAKE_PREFIX_PATH="ibex" -D CMAKE_INSTALL_PREFIX="../codac" -D WITH_PYTHON=ON .. cmake --build . --config Release --target install - cmake --build . --config Release --target test -- ARGS="-V --output-on-failure" - cmake --build . --config Release --target pip_package ; cp `ls *.whl` ../`ls *.whl | sed "s/py3-none-any/cp${{ matrix.cfg.py_v_maj }}${{ matrix.cfg.py_v_min }}-cp${{ matrix.cfg.py_v_maj }}${{ matrix.cfg.py_v_min }}${{ matrix.cfg.cpcfg }}/"` ; pip install ../*.whl ; python -c "import sys; print(sys.version)" ; python ../examples/tuto/01_getting_started/01_getting_started.py ; pip install numpy ; python -m unittest discover codac.tests + cmake --build . --config Release --target pip_package ; cp `ls *.whl` ../`ls *.whl | sed "s/py3-none-any/cp${{ matrix.cfg.py_v_maj }}${{ matrix.cfg.py_v_min }}-cp${{ matrix.cfg.py_v_maj }}${{ matrix.cfg.py_v_min }}${{ matrix.cfg.cpcfg }}/"` ; pip install ../*.whl ; python -c "import sys; print(sys.version)" ; python ../examples/tuto/01_getting_started/01_getting_started.py cd .. shell: bash - uses: xresloader/upload-to-github-release@v1 From 1e6be728ea2052626723c61f737a74f573de809c Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Sat, 6 Aug 2022 14:34:05 +0200 Subject: [PATCH 030/256] macos-10.15 deprecated, see https://github.blog/changelog/2022-07-20-github-actions-the-macos-10-15-actions-runner-image-is-being-deprecated-and-will-be-removed-by-8-30-22/ --- .github/workflows/macosmatrix.yml | 31 +++++++++++++++++++------------ .github/workflows/unixmatrix.yml | 1 - .github/workflows/vcmatrix.yml | 2 +- 3 files changed, 20 insertions(+), 14 deletions(-) diff --git a/.github/workflows/macosmatrix.yml b/.github/workflows/macosmatrix.yml index 83953c682..8eb5be8c8 100644 --- a/.github/workflows/macosmatrix.yml +++ b/.github/workflows/macosmatrix.yml @@ -7,8 +7,11 @@ on: pull_request: jobs: - vcmatrix: + macosmatrix: runs-on: ${{ matrix.cfg.os }} + env: + MACOSX_DEPLOYMENT_TARGET: ${{ matrix.cfg.trgt }} + _PYTHON_HOST_PLATFORM: macosx-${{ matrix.cfg.trgt }}-${{ matrix.cfg.arch }} defaults: run: shell: ${{ matrix.cfg.shell }} @@ -16,12 +19,13 @@ jobs: fail-fast: false matrix: cfg: -# - { os: macos-11, shell: bash, arch: x64, runtime: bigsur, cmake_flags: '-fPIC', cpcfg: '-macosx_11_0_x86_64', py_v_maj: 3, py_v_min: 10, desc: 'macOS Big Sur Python 3.10' } - - { os: macos-10.15, shell: bash, arch: x64, runtime: catalina, cmake_flags: '-fPIC', cpcfg: '-macosx_10_15_x86_64', py_v_maj: 3, py_v_min: 10, desc: 'macOS Catalina Python 3.10' } - - { os: macos-10.15, shell: bash, arch: x64, runtime: catalina, cmake_flags: '-fPIC', cpcfg: '-macosx_10_15_x86_64', py_v_maj: 3, py_v_min: 9, desc: 'macOS Catalina Python 3.9' } - - { os: macos-10.15, shell: bash, arch: x64, runtime: catalina, cmake_flags: '-fPIC', cpcfg: '-macosx_10_15_x86_64', py_v_maj: 3, py_v_min: 8, desc: 'macOS Catalina Python 3.8' } - - { os: macos-10.15, shell: bash, arch: x64, runtime: catalina, cmake_flags: '-fPIC', cpcfg: '-macosx_10_15_x86_64', py_v_maj: 3, py_v_min: 7, desc: 'macOS Catalina Python 3.7' } - - { os: macos-10.15, shell: bash, arch: x64, runtime: catalina, cmake_flags: '-fPIC', cpcfg: '-macosx_10_15_x86_64', py_v_maj: 3, py_v_min: 6, desc: 'macOS Catalina Python 3.6' } + - { os: macos-11, shell: bash, arch: arm64, runtime: bigsur, cmake_flags: '-fPIC', ilib: 'filib', trgt: '11.0', cpcfg: '-macosx_11_0_arm64', py_v_maj: 3, py_v_min: 10, desc: 'macOS Big Sur Python 3.10 arm64' } + - { os: macos-11, shell: bash, arch: arm64, runtime: bigsur, cmake_flags: '-fPIC', ilib: 'filib', trgt: '11.0', cpcfg: '-macosx_11_0_arm64', py_v_maj: 3, py_v_min: 9, desc: 'macOS Big Sur Python 3.9 arm64' } + - { os: macos-11, shell: bash, arch: arm64, runtime: bigsur, cmake_flags: '-fPIC', ilib: 'filib', trgt: '11.0', cpcfg: '-macosx_11_0_arm64', py_v_maj: 3, py_v_min: 8, desc: 'macOS Big Sur Python 3.8 arm64' } + - { os: macos-11, shell: bash, arch: x86_64, runtime: bigsur, cmake_flags: '-fPIC', ilib: 'gaol', trgt: '10.9', cpcfg: '-macosx_10_9_x86_64', py_v_maj: 3, py_v_min: 10, desc: 'macOS Big Sur Python 3.10' } + - { os: macos-11, shell: bash, arch: x86_64, runtime: bigsur, cmake_flags: '-fPIC', ilib: 'gaol', trgt: '10.9', cpcfg: '-macosx_10_9_x86_64', py_v_maj: 3, py_v_min: 9, desc: 'macOS Big Sur Python 3.9' } + - { os: macos-11, shell: bash, arch: x86_64, runtime: bigsur, cmake_flags: '-fPIC', ilib: 'gaol', trgt: '10.9', cpcfg: '-macosx_10_9_x86_64', py_v_maj: 3, py_v_min: 8, desc: 'macOS Big Sur Python 3.8' } + - { os: macos-11, shell: bash, arch: x86_64, runtime: bigsur, cmake_flags: '-fPIC', ilib: 'gaol', trgt: '10.9', cpcfg: '-macosx_10_9_x86_64', py_v_maj: 3, py_v_min: 7, desc: 'macOS Big Sur Python 3.7' } name: ${{ matrix.cfg.desc }} steps: - uses: actions/checkout@v2 @@ -29,10 +33,10 @@ jobs: submodules: true fetch-depth: 0 clean: false - - uses: actions/setup-python@v2 + - uses: actions/setup-python@v4 with: python-version: ${{ matrix.cfg.py_v_maj }}.${{ matrix.cfg.py_v_min }} - architecture: ${{ matrix.cfg.arch }} + architecture: x64 if: matrix.cfg.py_v_maj!='' - run: | python -c "import sys; print(sys.version)" @@ -42,15 +46,18 @@ jobs: if: runner.os=='macOS' - run: brew install doxygen ; brew install graphviz ; python -m pip install --upgrade pip ; pip install --upgrade wheel setuptools sphinx breathe sphinx_rtd_theme sphinx-tabs sphinx-issues sphinx-reredirects if: runner.os=='macOS' - - run: git clone -b actions https://github.com/lebarsfa/ibex-lib.git ; cd ibex-lib ; mkdir build ; cd build ; cmake -E env CXXFLAGS="${{ matrix.cfg.cmake_flags }}" CFLAGS="${{ matrix.cfg.cmake_flags }}" cmake ${{ matrix.cfg.cmake_params }} -D CMAKE_INSTALL_PREFIX="../../ibex" .. ; cmake --build . --config Release --target install ; cd ../.. + - run: git clone -b actions https://github.com/lebarsfa/ibex-lib.git ; cd ibex-lib ; mkdir build ; cd build ; cmake -E env CXXFLAGS="${{ matrix.cfg.cmake_flags }}" CFLAGS="${{ matrix.cfg.cmake_flags }}" cmake ${{ matrix.cfg.cmake_params }} -D CMAKE_SYSTEM_NAME=Darwin -D CMAKE_OSX_ARCHITECTURES=${{ matrix.cfg.arch }} -D CMAKE_INSTALL_PREFIX="../../ibex" -D INTERVAL_LIB=${{ matrix.cfg.ilib }} .. ; cmake --build . --config Release --target install ; cd ../.. shell: bash - run: | mkdir build ; cd build - cmake -E env CXXFLAGS="${{ matrix.cfg.cmake_flags }}" CFLAGS="${{ matrix.cfg.cmake_flags }}" cmake ${{ matrix.cfg.cmake_params }} -D CMAKE_PREFIX_PATH="ibex" -D CMAKE_INSTALL_PREFIX="../codac" -D WITH_PYTHON=ON .. + cmake -E env CXXFLAGS="${{ matrix.cfg.cmake_flags }}" CFLAGS="${{ matrix.cfg.cmake_flags }}" cmake ${{ matrix.cfg.cmake_params }} -D CMAKE_SYSTEM_NAME=Darwin -D CMAKE_OSX_ARCHITECTURES=${{ matrix.cfg.arch }} -D CMAKE_PREFIX_PATH="ibex" -D CMAKE_INSTALL_PREFIX="../codac" -D WITH_PYTHON=ON .. cmake --build . --config Release --target install - cmake --build . --config Release --target pip_package ; cp `ls *.whl` ../`ls *.whl | sed "s/py3-none-any/cp${{ matrix.cfg.py_v_maj }}${{ matrix.cfg.py_v_min }}-cp${{ matrix.cfg.py_v_maj }}${{ matrix.cfg.py_v_min }}${{ matrix.cfg.cpcfg }}/"` ; pip install ../*.whl ; python -c "import sys; print(sys.version)" ; python ../examples/tuto/01_getting_started/01_getting_started.py + cmake --build . --config Release --target pip_package ; cp `ls *.whl` ../`ls *.whl | sed "s/py3-none-any/cp${{ matrix.cfg.py_v_maj }}${{ matrix.cfg.py_v_min }}-cp${{ matrix.cfg.py_v_maj }}${{ matrix.cfg.py_v_min }}${{ matrix.cfg.cpcfg }}/"` cd .. shell: bash + - run: pip install *.whl ; python -c "import sys; print(sys.version)" ; python examples/tuto/01_getting_started/01_getting_started.py + shell: bash + if: (runner.os=='macOS')&&(matrix.cfg.arch=='x86_64') - uses: xresloader/upload-to-github-release@v1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/unixmatrix.yml b/.github/workflows/unixmatrix.yml index 441e424cf..79848e57e 100644 --- a/.github/workflows/unixmatrix.yml +++ b/.github/workflows/unixmatrix.yml @@ -43,7 +43,6 @@ jobs: - { os: ubuntu-18.04, shell: bash, arch: amd64, runtime: bionic, cmake_flags: '-fPIC', desc: 'Ubuntu 18.04' } - { os: macos-12, shell: bash, arch: x64, runtime: monterey, cmake_flags: '-fPIC', desc: 'macOS Monterey' } - { os: macos-11, shell: bash, arch: x64, runtime: bigsur, cmake_flags: '-fPIC', desc: 'macOS Big Sur' } - - { os: macos-10.15, shell: bash, arch: x64, runtime: catalina, cmake_flags: '-fPIC', desc: 'macOS Catalina' } name: ${{ matrix.cfg.desc }} steps: - uses: actions/checkout@v2 diff --git a/.github/workflows/vcmatrix.yml b/.github/workflows/vcmatrix.yml index 91804df75..e3f00d1de 100644 --- a/.github/workflows/vcmatrix.yml +++ b/.github/workflows/vcmatrix.yml @@ -34,7 +34,7 @@ jobs: submodules: true fetch-depth: 0 clean: false - - uses: actions/setup-python@v2 + - uses: actions/setup-python@v4 with: python-version: ${{ matrix.cfg.py_v_maj }}.${{ matrix.cfg.py_v_min }} architecture: ${{ matrix.cfg.arch }} From af54c3a59d1fe4b688f89671d87aab31e804eeb8 Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Fri, 12 Aug 2022 14:56:09 +0200 Subject: [PATCH 031/256] Attempt to speed up workflows with git clone --depth 1 --- .github/workflows/dockercentos.yml | 2 +- .github/workflows/dockerpi.yml | 2 +- .github/workflows/macosmatrix.yml | 4 ++-- .github/workflows/tests.yml | 2 +- .github/workflows/unixmatrix.yml | 4 ++-- .github/workflows/vcmatrix.yml | 6 +++--- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/dockercentos.yml b/.github/workflows/dockercentos.yml index 28967dfda..0833cd8d8 100644 --- a/.github/workflows/dockercentos.yml +++ b/.github/workflows/dockercentos.yml @@ -10,7 +10,7 @@ jobs: name: CentOS Docker runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: submodules: true fetch-depth: 0 diff --git a/.github/workflows/dockerpi.yml b/.github/workflows/dockerpi.yml index 9f45370d2..bc536896a 100644 --- a/.github/workflows/dockerpi.yml +++ b/.github/workflows/dockerpi.yml @@ -10,7 +10,7 @@ jobs: name: Raspbian Buster pi Docker runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: submodules: true fetch-depth: 0 diff --git a/.github/workflows/macosmatrix.yml b/.github/workflows/macosmatrix.yml index 8eb5be8c8..460666500 100644 --- a/.github/workflows/macosmatrix.yml +++ b/.github/workflows/macosmatrix.yml @@ -28,7 +28,7 @@ jobs: - { os: macos-11, shell: bash, arch: x86_64, runtime: bigsur, cmake_flags: '-fPIC', ilib: 'gaol', trgt: '10.9', cpcfg: '-macosx_10_9_x86_64', py_v_maj: 3, py_v_min: 7, desc: 'macOS Big Sur Python 3.7' } name: ${{ matrix.cfg.desc }} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: submodules: true fetch-depth: 0 @@ -46,7 +46,7 @@ jobs: if: runner.os=='macOS' - run: brew install doxygen ; brew install graphviz ; python -m pip install --upgrade pip ; pip install --upgrade wheel setuptools sphinx breathe sphinx_rtd_theme sphinx-tabs sphinx-issues sphinx-reredirects if: runner.os=='macOS' - - run: git clone -b actions https://github.com/lebarsfa/ibex-lib.git ; cd ibex-lib ; mkdir build ; cd build ; cmake -E env CXXFLAGS="${{ matrix.cfg.cmake_flags }}" CFLAGS="${{ matrix.cfg.cmake_flags }}" cmake ${{ matrix.cfg.cmake_params }} -D CMAKE_SYSTEM_NAME=Darwin -D CMAKE_OSX_ARCHITECTURES=${{ matrix.cfg.arch }} -D CMAKE_INSTALL_PREFIX="../../ibex" -D INTERVAL_LIB=${{ matrix.cfg.ilib }} .. ; cmake --build . --config Release --target install ; cd ../.. + - run: git clone --depth 1 -b actions https://github.com/lebarsfa/ibex-lib.git ; cd ibex-lib ; mkdir build ; cd build ; cmake -E env CXXFLAGS="${{ matrix.cfg.cmake_flags }}" CFLAGS="${{ matrix.cfg.cmake_flags }}" cmake ${{ matrix.cfg.cmake_params }} -D CMAKE_SYSTEM_NAME=Darwin -D CMAKE_OSX_ARCHITECTURES=${{ matrix.cfg.arch }} -D CMAKE_INSTALL_PREFIX="../../ibex" -D INTERVAL_LIB=${{ matrix.cfg.ilib }} .. ; cmake --build . --config Release --target install ; cd ../.. shell: bash - run: | mkdir build ; cd build diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 92b8bbad8..76ea535d5 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -15,7 +15,7 @@ jobs: - { os: ubuntu-18.04, gcc_v: 8, py_v_maj: 3, py_v_min: 6, desc: 'Ubuntu 18.04 GCC 8 Python 3.6 tests' } name: ${{ matrix.cfg.desc }} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: submodules: true fetch-depth: 0 diff --git a/.github/workflows/unixmatrix.yml b/.github/workflows/unixmatrix.yml index 79848e57e..45ee26e73 100644 --- a/.github/workflows/unixmatrix.yml +++ b/.github/workflows/unixmatrix.yml @@ -45,7 +45,7 @@ jobs: - { os: macos-11, shell: bash, arch: x64, runtime: bigsur, cmake_flags: '-fPIC', desc: 'macOS Big Sur' } name: ${{ matrix.cfg.desc }} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: submodules: true fetch-depth: 0 @@ -106,7 +106,7 @@ jobs: if: runner.os=='Linux' - run: brew install eigen if: runner.os=='macOS' - - run: git clone -b actions https://github.com/lebarsfa/ibex-lib.git ; cd ibex-lib ; mkdir build ; cd build ; cmake -E env CXXFLAGS="${{ matrix.cfg.cmake_flags }}" CFLAGS="${{ matrix.cfg.cmake_flags }}" cmake ${{ matrix.cfg.cmake_params }} -D CMAKE_INSTALL_PREFIX="../../ibex" .. ; cmake --build . --config Release --target install ; cd ../.. + - run: git clone --depth 1 -b actions https://github.com/lebarsfa/ibex-lib.git ; cd ibex-lib ; mkdir build ; cd build ; cmake -E env CXXFLAGS="${{ matrix.cfg.cmake_flags }}" CFLAGS="${{ matrix.cfg.cmake_flags }}" cmake ${{ matrix.cfg.cmake_params }} -D CMAKE_INSTALL_PREFIX="../../ibex" .. ; cmake --build . --config Release --target install ; cd ../.. shell: bash if: ((runner.os=='Windows')&&(matrix.cfg.runtime!='mingw8'))||((runner.os!='Windows')&&(runner.os!='Linux')) - run: | diff --git a/.github/workflows/vcmatrix.yml b/.github/workflows/vcmatrix.yml index e3f00d1de..8570b9bd7 100644 --- a/.github/workflows/vcmatrix.yml +++ b/.github/workflows/vcmatrix.yml @@ -29,7 +29,7 @@ jobs: - { os: windows-2019, shell: cmd, arch: x64, runtime: vc15, cmake_params: '-G "Visual Studio 16" -T v141 -A x64', cpcfg: 'm-win_amd64', py_v_maj: 3, py_v_min: 6, desc: 'Windows Visual Studio 2017 x64 Python 3.6' } name: ${{ matrix.cfg.desc }} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: submodules: true fetch-depth: 0 @@ -53,9 +53,9 @@ jobs: - run: choco install -y -r --no-progress eigen --version=3.4.0 ${{ matrix.cfg.choco_flags }} shell: cmd if: runner.os=='Windows' - - run: choco install -y -r --no-progress doxygen.install graphviz & python -m pip install --upgrade pip & pip install --upgrade wheel setuptools & git clone -b v3.1.1 https://github.com/sphinx-doc/sphinx & cd sphinx & pip install . & pip install --upgrade breathe sphinx-issues sphinx-tabs sphinx_rtd_theme sphinx-reredirects + - run: choco install -y -r --no-progress doxygen.install graphviz & python -m pip install --upgrade pip & pip install --upgrade wheel setuptools & git clone --depth 1 -b v3.1.1 https://github.com/sphinx-doc/sphinx & cd sphinx & pip install . & pip install --upgrade breathe sphinx-issues sphinx-tabs sphinx_rtd_theme sphinx-reredirects if: runner.os=='Windows' - - run: git clone -b actions https://github.com/lebarsfa/ibex-lib.git ; cd ibex-lib ; mkdir build ; cd build ; cmake -E env CXXFLAGS=" /wd4267 /wd4244 /wd4305 /wd4996" CFLAGS=" /wd4267 /wd4244 /wd4305 /wd4996" cmake ${{ matrix.cfg.cmake_params }} -D CMAKE_INSTALL_PREFIX="../../ibex" .. ; cmake --build . --config Release --target install ; cd ../.. + - run: git clone --depth 1 -b actions https://github.com/lebarsfa/ibex-lib.git ; cd ibex-lib ; mkdir build ; cd build ; cmake -E env CXXFLAGS=" /wd4267 /wd4244 /wd4305 /wd4996" CFLAGS=" /wd4267 /wd4244 /wd4305 /wd4996" cmake ${{ matrix.cfg.cmake_params }} -D CMAKE_INSTALL_PREFIX="../../ibex" .. ; cmake --build . --config Release --target install ; cd ../.. shell: bash - run: | mkdir build ; cd build From bd2965997773d7d6b3fc05fa3e21f792a3ee874e Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Wed, 10 Aug 2022 23:06:21 +0200 Subject: [PATCH 032/256] Updated workflows --- .github/workflows/macosmatrix.yml | 13 ++++++++----- .github/workflows/unixmatrix.yml | 24 ++++++------------------ 2 files changed, 14 insertions(+), 23 deletions(-) diff --git a/.github/workflows/macosmatrix.yml b/.github/workflows/macosmatrix.yml index 460666500..5073450a8 100644 --- a/.github/workflows/macosmatrix.yml +++ b/.github/workflows/macosmatrix.yml @@ -22,10 +22,13 @@ jobs: - { os: macos-11, shell: bash, arch: arm64, runtime: bigsur, cmake_flags: '-fPIC', ilib: 'filib', trgt: '11.0', cpcfg: '-macosx_11_0_arm64', py_v_maj: 3, py_v_min: 10, desc: 'macOS Big Sur Python 3.10 arm64' } - { os: macos-11, shell: bash, arch: arm64, runtime: bigsur, cmake_flags: '-fPIC', ilib: 'filib', trgt: '11.0', cpcfg: '-macosx_11_0_arm64', py_v_maj: 3, py_v_min: 9, desc: 'macOS Big Sur Python 3.9 arm64' } - { os: macos-11, shell: bash, arch: arm64, runtime: bigsur, cmake_flags: '-fPIC', ilib: 'filib', trgt: '11.0', cpcfg: '-macosx_11_0_arm64', py_v_maj: 3, py_v_min: 8, desc: 'macOS Big Sur Python 3.8 arm64' } - - { os: macos-11, shell: bash, arch: x86_64, runtime: bigsur, cmake_flags: '-fPIC', ilib: 'gaol', trgt: '10.9', cpcfg: '-macosx_10_9_x86_64', py_v_maj: 3, py_v_min: 10, desc: 'macOS Big Sur Python 3.10' } - - { os: macos-11, shell: bash, arch: x86_64, runtime: bigsur, cmake_flags: '-fPIC', ilib: 'gaol', trgt: '10.9', cpcfg: '-macosx_10_9_x86_64', py_v_maj: 3, py_v_min: 9, desc: 'macOS Big Sur Python 3.9' } - - { os: macos-11, shell: bash, arch: x86_64, runtime: bigsur, cmake_flags: '-fPIC', ilib: 'gaol', trgt: '10.9', cpcfg: '-macosx_10_9_x86_64', py_v_maj: 3, py_v_min: 8, desc: 'macOS Big Sur Python 3.8' } - - { os: macos-11, shell: bash, arch: x86_64, runtime: bigsur, cmake_flags: '-fPIC', ilib: 'gaol', trgt: '10.9', cpcfg: '-macosx_10_9_x86_64', py_v_maj: 3, py_v_min: 7, desc: 'macOS Big Sur Python 3.7' } + - { os: macos-11, shell: bash, arch: arm64, runtime: bigsur, cmake_flags: '-fPIC', ilib: 'filib', trgt: '11.0', cpcfg: '-macosx_11_0_arm64', py_v_maj: 3, py_v_min: 7, desc: 'macOS Big Sur Python 3.7 arm64' } + - { os: macos-11, shell: bash, arch: arm64, runtime: bigsur, cmake_flags: '-fPIC', ilib: 'filib', trgt: '11.0', cpcfg: '-macosx_11_0_arm64', py_v_maj: 3, py_v_min: 6, desc: 'macOS Big Sur Python 3.6 arm64' } + - { os: macos-11, shell: bash, arch: x86_64, runtime: bigsur, cmake_flags: '-fPIC', ilib: 'gaol', trgt: '10.9', cpcfg: '-macosx_10_9_x86_64', py_v_maj: 3, py_v_min: 10, desc: 'macOS Big Sur Python 3.10 x86_64' } + - { os: macos-11, shell: bash, arch: x86_64, runtime: bigsur, cmake_flags: '-fPIC', ilib: 'gaol', trgt: '10.9', cpcfg: '-macosx_10_9_x86_64', py_v_maj: 3, py_v_min: 9, desc: 'macOS Big Sur Python 3.9 x86_64' } + - { os: macos-11, shell: bash, arch: x86_64, runtime: bigsur, cmake_flags: '-fPIC', ilib: 'gaol', trgt: '10.9', cpcfg: '-macosx_10_9_x86_64', py_v_maj: 3, py_v_min: 8, desc: 'macOS Big Sur Python 3.8 x86_64' } + - { os: macos-11, shell: bash, arch: x86_64, runtime: bigsur, cmake_flags: '-fPIC', ilib: 'gaol', trgt: '10.9', cpcfg: '-macosx_10_9_x86_64', py_v_maj: 3, py_v_min: 7, desc: 'macOS Big Sur Python 3.7 x86_64' } + - { os: macos-11, shell: bash, arch: x86_64, runtime: bigsur, cmake_flags: '-fPIC', ilib: 'gaol', trgt: '10.14', cpcfg: '-macosx_10_14_x86_64', py_v_maj: 3, py_v_min: 6, desc: 'macOS Big Sur Python 3.6 x86_64' } # 10.14 because of error $MACOSX_DEPLOYMENT_TARGET mismatch: now "10.9" but "10.14" during configure. name: ${{ matrix.cfg.desc }} steps: - uses: actions/checkout@v3 @@ -46,7 +49,7 @@ jobs: if: runner.os=='macOS' - run: brew install doxygen ; brew install graphviz ; python -m pip install --upgrade pip ; pip install --upgrade wheel setuptools sphinx breathe sphinx_rtd_theme sphinx-tabs sphinx-issues sphinx-reredirects if: runner.os=='macOS' - - run: git clone --depth 1 -b actions https://github.com/lebarsfa/ibex-lib.git ; cd ibex-lib ; mkdir build ; cd build ; cmake -E env CXXFLAGS="${{ matrix.cfg.cmake_flags }}" CFLAGS="${{ matrix.cfg.cmake_flags }}" cmake ${{ matrix.cfg.cmake_params }} -D CMAKE_SYSTEM_NAME=Darwin -D CMAKE_OSX_ARCHITECTURES=${{ matrix.cfg.arch }} -D CMAKE_INSTALL_PREFIX="../../ibex" -D INTERVAL_LIB=${{ matrix.cfg.ilib }} .. ; cmake --build . --config Release --target install ; cd ../.. + - run: git clone --depth 1 -b actions https://github.com/lebarsfa/ibex-lib.git ; cd ibex-lib ; mkdir build ; cd build ; cmake -E env CXXFLAGS="${{ matrix.cfg.cmake_flags }}" CFLAGS="${{ matrix.cfg.cmake_flags }}" cmake ${{ matrix.cfg.cmake_params }} -D CMAKE_SYSTEM_NAME=Darwin -D CMAKE_OSX_ARCHITECTURES=${{ matrix.cfg.arch }} -D INTERVAL_LIB=${{ matrix.cfg.ilib }} -D CMAKE_INSTALL_PREFIX="../../ibex" .. ; cmake --build . --config Release --target install ; cd ../.. shell: bash - run: | mkdir build ; cd build diff --git a/.github/workflows/unixmatrix.yml b/.github/workflows/unixmatrix.yml index 45ee26e73..73ae5ab63 100644 --- a/.github/workflows/unixmatrix.yml +++ b/.github/workflows/unixmatrix.yml @@ -35,14 +35,17 @@ jobs: - { os: windows-2019, shell: cmd, arch: x86, runtime: vc16, cmake_params: '-G "Visual Studio 16" -A Win32', choco_flags: '--x86', cmake_flags: ' /wd4267 /wd4244 /wd4305 /wd4996', test_config: 'Release/', desc: 'Windows Visual Studio 2019 x86' } - { os: windows-2019, shell: cmd, arch: x64, runtime: vc15, cmake_params: '-G "Visual Studio 16" -T v141 -A x64', cmake_flags: ' /wd4267 /wd4244 /wd4305 /wd4996', test_config: 'Release/', desc: 'Windows Visual Studio 2017 x64' } - { os: windows-2019, shell: cmd, arch: x86, runtime: vc15, cmake_params: '-G "Visual Studio 16" -T v141 -A Win32', choco_flags: '--x86', cmake_flags: ' /wd4267 /wd4244 /wd4305 /wd4996', test_config: 'Release/', desc: 'Windows Visual Studio 2017 x86' } +# - { os: windows-2022, shell: cmd, arch: x64, runtime: mingw11, cmake_params: '-G "MSYS Makefiles"', cmake_flags: '-fPIC', desc: 'Windows MinGW 11.2.0 x64' } +# - { os: windows-2022, shell: cmd, arch: x86, runtime: mingw11, cmake_params: '-G "MSYS Makefiles"', cmake_flags: '-fPIC', choco_flags: '--x86', desc: 'Windows MinGW 11.2.0 x86' } - { os: windows-2019, shell: cmd, arch: x64, runtime: mingw8, cmake_params: '-G "MSYS Makefiles"', cmake_flags: '-fPIC', desc: 'Windows MinGW 8.1.0 x64' } - { os: windows-2019, shell: cmd, arch: x86, runtime: mingw8, cmake_params: '-G "MSYS Makefiles"', cmake_flags: '-fPIC', choco_flags: '--x86', desc: 'Windows MinGW 8.1.0 x86' } - - { os: windows-2019, shell: cmd, arch: x86, runtime: mingw7, cmake_params: '-G "MSYS Makefiles"', cmake_flags: '-fPIC', choco_flags: '--x86', desc: 'Windows Qt 5.12.6 MinGW 7.3.0 x86' } +# - { os: windows-2019, shell: cmd, arch: x64, runtime: mingw7, cmake_params: '-G "MSYS Makefiles"', cmake_flags: '-fPIC', desc: 'Windows MinGW 7.3.0 x64' } + - { os: windows-2019, shell: cmd, arch: x86, runtime: mingw7, cmake_params: '-G "MSYS Makefiles"', cmake_flags: '-fPIC', choco_flags: '--x86', desc: 'Windows MinGW 7.3.0 x86' } - { os: ubuntu-22.04, shell: bash, arch: amd64, runtime: jammy, cmake_flags: '-fPIC', desc: 'Ubuntu 22.04' } - { os: ubuntu-20.04, shell: bash, arch: amd64, runtime: focal, cmake_flags: '-fPIC', desc: 'Ubuntu 20.04' } - { os: ubuntu-18.04, shell: bash, arch: amd64, runtime: bionic, cmake_flags: '-fPIC', desc: 'Ubuntu 18.04' } - - { os: macos-12, shell: bash, arch: x64, runtime: monterey, cmake_flags: '-fPIC', desc: 'macOS Monterey' } - - { os: macos-11, shell: bash, arch: x64, runtime: bigsur, cmake_flags: '-fPIC', desc: 'macOS Big Sur' } + - { os: macos-12, shell: bash, arch: x86_64, runtime: monterey, cmake_flags: '-fPIC', desc: 'macOS Monterey' } + - { os: macos-11, shell: bash, arch: x86_64, runtime: bigsur, cmake_flags: '-fPIC', desc: 'macOS Big Sur' } name: ${{ matrix.cfg.desc }} steps: - uses: actions/checkout@v3 @@ -67,21 +70,6 @@ jobs: - run: echo "PACKAGE_VERSION=$SOFTWARE_VERSION-${DEBIAN_PACKAGE_REV}${{ matrix.cfg.runtime }}$PACKAGE_REV" >> $GITHUB_ENV shell: bash if: runner.os=='Linux' -# - run: | -# choco install -y -r --no-progress wget -# wget http://download.qt.io/archive/qt/5.12/5.12.6/qt-opensource-windows-x86-5.12.6.exe --no-check-certificate -nv -# move /Y qt-opensource-windows-x86-5.12.6.exe %SystemDrive%\ -# wget http://www.ensta-bretagne.fr/lebars/Share/qt-installer-5.12.6-mingw73_32.qs --no-check-certificate -nv -# move /Y qt-installer-5.12.6-mingw73_32.qs %SystemDrive%\ -# netsh advfirewall set allprofiles state on -# netsh advfirewall firewall add rule name="Qt offline installer" dir=out action=block program="%SystemDrive%\qt-opensource-windows-x86-5.12.6.exe" enable=yes -# rem Take several min... -# %SystemDrive%\qt-opensource-windows-x86-5.12.6.exe --script %SystemDrive%\qt-installer-5.12.6-mingw73_32.qs -# netsh advfirewall firewall del rule name="Qt offline installer" -# netsh advfirewall set allprofiles state off -# del /f /q %SystemDrive%\qt-opensource-windows-x86-5.12.6.exe -# echo C:\Qt\Qt5.12.6\5.12.6\mingw73_32\bin;C:\Qt\Qt5.12.6\Tools\mingw730_32\bin>>%GITHUB_PATH% -# if: (runner.os=='Windows')&&(matrix.cfg.runtime=='mingw7') - run: | choco install -y -r --no-progress mingw --version=7.3.0 --force ${{ matrix.cfg.choco_flags }} echo C:\ProgramData\chocolatey\lib\mingw\tools\install\mingw32\bin>>%GITHUB_PATH% From bfcabc66bc46dfd457b20b724371e92eab42bc09 Mon Sep 17 00:00:00 2001 From: SimonRohou Date: Wed, 24 Aug 2022 15:49:18 +0200 Subject: [PATCH 033/256] [doc] updated CHANGELOG --- CHANGELOG | 93 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index 8f52ce771..034e579b1 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -37,6 +37,99 @@ Codac releases +----------------------------------------------------------------- + +Codac release 1.2.0 (released `Jun 6, 2022 `_) +======================================================================================================= + +`Commits since previous release 1.1.0 `_ + +Features added +-------------- + +* :commit:`54b5f45d50f73fa4f2c0eb4ee937f1d3968fadb0`: added ``SepBox`` +* :commit:`0a4f3ad7861d9dba3fbd418e75e3d9d71d761d6c`: ``SepProj``, ``SepFixPoint``, ``SepCtcPairProj``, ``QInterProjF`` now in core + +Documentation +------------- + +* :commit:`a5a483e84f5a8c3fb7eecda68cc1394adbd9c517`: string formatting for run-time ``Function`` definition +* :commit:`a5a483e84f5a8c3fb7eecda68cc1394adbd9c517`: string formatting for run-time ``Function`` definition + +Python binding +-------------- + +* :commit:`7cbb1eb06ef35d6f75df06cd1b82d5017bfe0edc`: SepFixPoint documentation +* :commit:`0aeb37f8fa6a93b60c588982c557dc1aa25471d8`: SepProj documentation +* :commit:`490dd0d5b872e1d4186c15c7b02a98b8edab086b`: SepCtcPairProj documentation +* :commit:`31727f5f889c121d4d9a7d1f48c99670ae034add`: QInterProjF documentation +* :commit:`9000cc63169dd4937ac3a20726f5290882a8aaed`: constructor for creating an ``IntervalMatrix`` from NumPy objects + + +----------------------------------------------------------------- + +Codac release 1.1.0 (released `May 17, 2022 `_) +======================================================================================================== + +`Commits since previous release 1.0.0 `_ + +Features added +-------------- + +* :commit:`b6f79a06aa0d3fe94072aba2168dfd523d1ef123`: added ``CtcCartProd`` +* :commit:`9382404ef967cdf77e20568a5c57e32fab9e9463`: added ``CtcBox`` +* :commit:`e014ae05c0f45f4bd357f3cd047e3fd475761428`: added ``SepFunction`` + +Changes +------- + +* :commit:`4357e3d50b192935a7da9a6f785e6ef7e11f9658`: improving SIVIA interface + compatibility with former pySIVIA + +Documentation +------------- + +* :commit:`a9cefd2b9a90503c09eb765ee916e7e4077aa89b`: Lie symmetries page +* :commit:`b534e48a71ac6657e0c6cdbb06738e2be547cbd0`: updated installation information +* :commit:`f04180d7517d6169ebdcff42159357a5ea171f15`: how to build ``Trajectory`` from ``npz`` file +* :commit:`cc350cb32e54b663c3a7e0ce71f76fcf194f2535`: codac-unsupported + +Python binding +-------------- + +* :commit:`eea406bb88e95f42c601d7643f1f81a08f900718`: now available for MacOS +* :commit:`ad2b7fac70d72832057d2a2af13dd09d8ea0f898`: corrected bug in ``__invert__`` of a ``Sep`` + + +----------------------------------------------------------------- + +Codac release 1.0.0 (released `Apr 21, 2022 `_) +======================================================================================================== + +`Commits since previous release 0.1.14 `_ + +Changes +------- + +* :commit:`ad73493012207bacc191a9261058f2af4b4a61d4`: C++17 + +Bugs fixed +---------- + +* :commit:`3c268eb9e0f93b21d1d81907bac23f64400d2cbc`: fix encoding problems on Windows with Python 3.10 +* :commit:`4e5d21637d17efe97175c7e026457cb8a62128e2`: updated pybind11 to v2.9.2 for Python 3.10 support +* :commit:`03889ad182cf24d41a6c001a90e20bed15a0b1f2`: ``const`` for ``TFnc`` parameter in ``CtcPicard`` constructor + +Documentation +------------- + +* :commit:`b1617459d2acc216fa5db854d7ba09affe279aff`: set-inversion and separators + +Python binding +-------------- + +* :commit:`5fcaebf959e4ab454818e6921d278fdf78ab38cc`: added ``__hash__`` for ``Interval``, ``IntervalVar``, ``IntervalVectorVar`` + + ----------------------------------------------------------------- Codac release 0.1.14 (released `Apr 14, 2022 `_) From 307c956aa3a7ad71bb9b2edd3fb6633293de4c2d Mon Sep 17 00:00:00 2001 From: SimonRohou Date: Thu, 25 Aug 2022 13:53:17 +0200 Subject: [PATCH 034/256] [fnc] solving ambiguity in TFunction --- src/core/functions/codac_TFunction.cpp | 5 +++++ src/core/functions/codac_TFunction.h | 5 +++-- tests/core/tests_ctc_picard.cpp | 2 +- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/core/functions/codac_TFunction.cpp b/src/core/functions/codac_TFunction.cpp index 2d4efe755..e7c8ee6a0 100644 --- a/src/core/functions/codac_TFunction.cpp +++ b/src/core/functions/codac_TFunction.cpp @@ -418,6 +418,11 @@ namespace codac return y; } + const IntervalVector TFunction::eval_vector(const IntervalVector& x1, const IntervalVector& x2) const + { + return eval_vector(cart_prod(x1,x2)); + } + const TrajectoryVector TFunction::traj_eval_vector(const TrajectoryVector& x) const { // Faster evaluation than the generic Fnc::eval method diff --git a/src/core/functions/codac_TFunction.h b/src/core/functions/codac_TFunction.h index 8e6455433..b768bef29 100644 --- a/src/core/functions/codac_TFunction.h +++ b/src/core/functions/codac_TFunction.h @@ -68,11 +68,12 @@ namespace codac const IntervalVector eval_vector(const IntervalVector& x) const; const IntervalVector eval_vector(int slice_id, const TubeVector& x) const; const IntervalVector eval_vector(const Interval& t, const TubeVector& x) const; + const IntervalVector eval_vector(const IntervalVector& x1, const IntervalVector& x2) const; template - const IntervalVector eval_vector(const IntervalVector& x, FirstArg& xi, Args&... xs) // recursive variadic function + const IntervalVector eval_vector(const IntervalVector& x1, const IntervalVector& x2, FirstArg& xi, Args&... xs) // recursive variadic function { - IntervalVector x_ = cart_prod(x,xi); + IntervalVector x_ = cart_prod(x1,x2,xi); if constexpr(sizeof...(xs) > 0) return eval_vector(x_, xs...); else diff --git a/tests/core/tests_ctc_picard.cpp b/tests/core/tests_ctc_picard.cpp index d7c77720a..84dc69100 100644 --- a/tests/core/tests_ctc_picard.cpp +++ b/tests/core/tests_ctc_picard.cpp @@ -32,7 +32,7 @@ TEST_CASE("CtcPicard") TFunction f2("x", "3."); CtcPicard ctc_picard_f2(f2, delta); Interval dtest = tube[0].slice_tdomain(0); - IntervalVector test = f2.eval_vector(dtest, tube); + IntervalVector test = f2.eval_vector((const Interval)dtest, (const TubeVector)tube); ctc_picard_f2.guess_kth_slices_envelope(tube, 0, TimePropag::FORWARD); CHECK(tube(0).is_superset(1.5 + Interval(0.,0.5) * 3.)); From 72aa60cb538423951b1386f29375d8f6147e70ec Mon Sep 17 00:00:00 2001 From: SimonRohou Date: Thu, 25 Aug 2022 13:53:54 +0200 Subject: [PATCH 035/256] [tube] TDomain now defined as shared pointer --- .../tube/codac2_AbstractSlicedTube.cpp | 6 +- .../domains/tube/codac2_AbstractSlicedTube.h | 6 +- src/core/2/domains/tube/codac2_Slice.h | 6 +- src/core/2/domains/tube/codac2_TDomain.cpp | 39 +++-- src/core/2/domains/tube/codac2_TDomain.h | 8 +- src/core/2/domains/tube/codac2_Tube.h | 54 +++---- src/core/arithmetic/codac_predef_values.h | 2 + tests/core/CMakeLists.txt | 54 +++---- tests/core/tests_codac2_tubes.cpp | 133 ++++++++++-------- 9 files changed, 173 insertions(+), 135 deletions(-) diff --git a/src/core/2/domains/tube/codac2_AbstractSlicedTube.cpp b/src/core/2/domains/tube/codac2_AbstractSlicedTube.cpp index 713864216..f259afcaf 100644 --- a/src/core/2/domains/tube/codac2_AbstractSlicedTube.cpp +++ b/src/core/2/domains/tube/codac2_AbstractSlicedTube.cpp @@ -15,19 +15,19 @@ using namespace std; namespace codac2 { - AbstractSlicedTube::AbstractSlicedTube(TDomain& tdomain) : + AbstractSlicedTube::AbstractSlicedTube(const shared_ptr& tdomain) : _tdomain(tdomain) { } - TDomain& AbstractSlicedTube::tdomain() const + const shared_ptr& AbstractSlicedTube::tdomain() const { return _tdomain; } Interval AbstractSlicedTube::t0_tf() const { - return _tdomain.t0_tf(); + return _tdomain->t0_tf(); } } // namespace codac \ No newline at end of file diff --git a/src/core/2/domains/tube/codac2_AbstractSlicedTube.h b/src/core/2/domains/tube/codac2_AbstractSlicedTube.h index 5726bf538..0cc3fdc54 100644 --- a/src/core/2/domains/tube/codac2_AbstractSlicedTube.h +++ b/src/core/2/domains/tube/codac2_AbstractSlicedTube.h @@ -22,18 +22,18 @@ namespace codac2 { public: - AbstractSlicedTube(TDomain& tdomain); + AbstractSlicedTube(const std::shared_ptr& tdomain); virtual const std::shared_ptr& first_abstract_slice() const = 0; virtual const std::shared_ptr& last_abstract_slice() const = 0; - TDomain& tdomain() const; + const std::shared_ptr& tdomain() const; Interval t0_tf() const; protected: - TDomain& _tdomain; + std::shared_ptr _tdomain; }; } // namespace codac diff --git a/src/core/2/domains/tube/codac2_Slice.h b/src/core/2/domains/tube/codac2_Slice.h index 39d121db2..6342ad4e9 100644 --- a/src/core/2/domains/tube/codac2_Slice.h +++ b/src/core/2/domains/tube/codac2_Slice.h @@ -76,7 +76,11 @@ namespace codac2 virtual size_t size() const { - return codomain().size(); + // todo: define size() method in Interval class + if constexpr(std::is_same::value) + return 1; + else + return codomain().size(); } bool is_gate() const diff --git a/src/core/2/domains/tube/codac2_TDomain.cpp b/src/core/2/domains/tube/codac2_TDomain.cpp index 2c275dccc..eca35fcfb 100644 --- a/src/core/2/domains/tube/codac2_TDomain.cpp +++ b/src/core/2/domains/tube/codac2_TDomain.cpp @@ -20,11 +20,6 @@ using namespace codac; namespace codac2 { - TDomain::TDomain() - { - _tslices.push_back(TSlice(Interval())); - } - TDomain::TDomain(const Interval& t0_tf, bool with_gates) : TDomain(t0_tf, t0_tf.diam(), with_gates) { @@ -36,19 +31,25 @@ namespace codac2 assert(!t0_tf.is_empty()); assert(dt > 0.); - double prev_t = -oo; - for(double t = t0_tf.lb() ; t < t0_tf.ub()+dt ; t+=dt) + if(isinf(dt)) + _tslices.push_back(TSlice(t0_tf)); + + else { - double t_ = min(t, t0_tf.ub()); + double prev_t = -oo; + for(double t = t0_tf.lb() ; t < t0_tf.ub()+dt ; t+=dt) + { + double t_ = min(t, t0_tf.ub()); - _tslices.push_back(TSlice(Interval(prev_t,t_))); - if(with_gates) - _tslices.push_back(TSlice(Interval(t_,t_))); + _tslices.push_back(TSlice(Interval(prev_t,t_))); + if(with_gates) + _tslices.push_back(TSlice(Interval(t_,t_))); - prev_t = t_; - } + prev_t = t_; + } - _tslices.push_back(TSlice(Interval(t0_tf.ub(),oo))); + _tslices.push_back(TSlice(Interval(t0_tf.ub(),oo))); + } } const Interval TDomain::t0_tf() const @@ -120,4 +121,14 @@ namespace codac2 << flush; return os; } + + shared_ptr create_tdomain(const Interval& t0_tf, bool with_gates) + { + return make_shared(t0_tf, with_gates); + } + + shared_ptr create_tdomain(const Interval& t0_tf, double dt, bool with_gates) + { + return make_shared(t0_tf, dt, with_gates); + } } // namespace codac \ No newline at end of file diff --git a/src/core/2/domains/tube/codac2_TDomain.h b/src/core/2/domains/tube/codac2_TDomain.h index 382532f50..fee3cba3c 100644 --- a/src/core/2/domains/tube/codac2_TDomain.h +++ b/src/core/2/domains/tube/codac2_TDomain.h @@ -18,6 +18,7 @@ #include #include "codac_Interval.h" +#include "codac_predef_values.h" namespace codac2 { @@ -28,8 +29,7 @@ namespace codac2 { public: - explicit TDomain(); - explicit TDomain(const Interval& t0_tf, bool with_gates = false); + explicit TDomain(const Interval& t0_tf = Interval(-oo,oo), bool with_gates = false); explicit TDomain(const Interval& t0_tf, double dt, bool with_gates = false); const Interval t0_tf() const; // todo: keep this method? std::list::iterator iterator_tslice(double t); @@ -47,6 +47,10 @@ namespace codac2 template friend class Tube; }; + + std::shared_ptr create_tdomain(const Interval& t0_tf = Interval(-oo,oo), bool with_gates = false); + std::shared_ptr create_tdomain(const Interval& t0_tf, double dt, bool with_gates = false); + } // namespace codac #endif \ No newline at end of file diff --git a/src/core/2/domains/tube/codac2_Tube.h b/src/core/2/domains/tube/codac2_Tube.h index 7e9e66167..d0c3ddb43 100644 --- a/src/core/2/domains/tube/codac2_Tube.h +++ b/src/core/2/domains/tube/codac2_Tube.h @@ -38,11 +38,11 @@ namespace codac2 { public: - explicit Tube(size_t n, TDomain& tdomain) : + explicit Tube(size_t n, const std::shared_ptr& tdomain) : AbstractSlicedTube(tdomain) { - for(std::list::iterator it = _tdomain._tslices.begin(); - it != _tdomain._tslices.end(); ++it) + for(std::list::iterator it = _tdomain->_tslices.begin(); + it != _tdomain->_tslices.end(); ++it) { it->_slices.insert( std::pair>>(this, @@ -50,7 +50,7 @@ namespace codac2 } } - explicit Tube(size_t n, TDomain& tdomain, const TFnc& f) : + explicit Tube(size_t n, const std::shared_ptr& tdomain, const TFnc& f) : Tube(n, tdomain) { assert(f.nb_var() == 0 && "function's inputs must be limited to system variable"); @@ -63,8 +63,8 @@ namespace codac2 explicit Tube(const Tube& x) : AbstractSlicedTube(x.tdomain()) { - for(std::list::iterator it = _tdomain._tslices.begin(); - it != _tdomain._tslices.end(); ++it) + for(std::list::iterator it = _tdomain->_tslices.begin(); + it != _tdomain->_tslices.end(); ++it) { it->_slices.insert( std::pair>>(this, @@ -74,7 +74,7 @@ namespace codac2 ~Tube() { - for(auto& s : _tdomain._tslices) + for(auto& s : _tdomain->_tslices) s._slices.erase(this); } @@ -85,22 +85,26 @@ namespace codac2 size_t size() const { - return first_slice().size(); + // todo: define size() method in Interval class + if constexpr(std::is_same::value) + return 1; + else + return first_slice().size(); } size_t nb_slices() const { - return _tdomain.nb_tslices(); + return _tdomain->nb_tslices(); } virtual const std::shared_ptr& first_abstract_slice() const { - return _tdomain.tslices().front().slices().at(this); + return _tdomain->tslices().front().slices().at(this); } virtual const std::shared_ptr& last_abstract_slice() const { - return _tdomain.tslices().back().slices().at(this); + return _tdomain->tslices().back().slices().at(this); } const std::shared_ptr> first_slice_ptr() const @@ -156,9 +160,9 @@ namespace codac2 return true; } - IntervalVector codomain() const + T codomain() const { - IntervalVector codomain(size()); + T codomain = first_slice().codomain(); codomain.set_empty(); for(const auto& s : *this) codomain |= s.codomain(); @@ -177,15 +181,15 @@ namespace codac2 T eval(double t) const { - return std::static_pointer_cast>(_tdomain.iterator_tslice(t)->_slices.at(this))->codomain(); + return std::static_pointer_cast>(_tdomain->iterator_tslice(t)->_slices.at(this))->codomain(); } T eval(const Interval& t) const { - std::list::iterator it = _tdomain.iterator_tslice(t.lb()); + std::list::iterator it = _tdomain->iterator_tslice(t.lb()); T codomain = std::static_pointer_cast>(it->_slices.at(this))->codomain(); - while(it != _tdomain.iterator_tslice(t.ub())) + while(it != _tdomain->iterator_tslice(t.ub())) { codomain |= std::static_pointer_cast>(it->_slices.at(this))->codomain(); it++; @@ -258,8 +262,8 @@ namespace codac2 const Tube& _tube_vector; }; - iterator begin() { return iterator(*this, _tdomain._tslices.begin()); } - iterator end() { return iterator(*this, _tdomain._tslices.end()); } + iterator begin() { return iterator(*this, _tdomain->_tslices.begin()); } + iterator end() { return iterator(*this, _tdomain->_tslices.end()); } struct const_iterator : public base_container::const_iterator @@ -286,8 +290,8 @@ namespace codac2 const Tube& _tube_vector; }; - const_iterator begin() const { return const_iterator(*this, _tdomain._tslices.cbegin()); } - const_iterator end() const { return const_iterator(*this, _tdomain._tslices.cend()); } + const_iterator begin() const { return const_iterator(*this, _tdomain->_tslices.cbegin()); } + const_iterator end() const { return const_iterator(*this, _tdomain->_tslices.cend()); } }; @@ -300,8 +304,8 @@ namespace codac2 { // Sampling the tube only if affectation is performed // (i.e. this is not done in the constructor) - std::list::iterator it_lb = _tubevector->_tdomain.sample(_t.lb(), false); - std::list::iterator it_ub = _tubevector->_tdomain.sample(_t.ub(), _t.is_degenerated()); + std::list::iterator it_lb = _tubevector->_tdomain->sample(_t.lb(), false); + std::list::iterator it_ub = _tubevector->_tdomain->sample(_t.ub(), _t.is_degenerated()); do { @@ -367,7 +371,7 @@ namespace codac2 return 1; } - const TDomain& tdomain() const + const std::shared_ptr& tdomain() const { return _tubevector.tdomain(); } @@ -399,7 +403,7 @@ namespace codac2 const TubeComponent& operator=(const TubeComponent& x) { - assert(&x.tdomain() == &tdomain()); + assert(x.tdomain() == tdomain()); for(auto& s : _tubevector) s.set_component(_i, std::static_pointer_cast>(s._it_tslice->_slices.at(&x._tubevector))->codomain()[x._i]); return *this; @@ -407,7 +411,7 @@ namespace codac2 const TubeComponent& operator=(std::pair,const TubeComponent> rel) { - assert(&rel.second.tdomain() == &tdomain()); + assert(rel.second.tdomain() == tdomain()); for(auto& s : _tubevector) s.set_component(_i, rel.first(std::static_pointer_cast>(s._it_tslice->_slices.at(&rel.second._tubevector))->codomain()[rel.second._i])); return *this; diff --git a/src/core/arithmetic/codac_predef_values.h b/src/core/arithmetic/codac_predef_values.h index e5c294dce..41de0c035 100644 --- a/src/core/arithmetic/codac_predef_values.h +++ b/src/core/arithmetic/codac_predef_values.h @@ -19,4 +19,6 @@ namespace codac const double oo = POS_INFINITY; } +using codac::oo; + #endif \ No newline at end of file diff --git a/tests/core/CMakeLists.txt b/tests/core/CMakeLists.txt index 868ad7da5..9bf1484bf 100644 --- a/tests/core/CMakeLists.txt +++ b/tests/core/CMakeLists.txt @@ -8,33 +8,33 @@ list(APPEND SRC_TESTS ${CMAKE_CURRENT_SOURCE_DIR}/main.cpp ${CMAKE_CURRENT_SOURCE_DIR}/tests_codac2_tubes.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_predefined_tubes.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_predefined_tubes.h - ${CMAKE_CURRENT_SOURCE_DIR}/tests_arithmetic.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_cn.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_ctc_box.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_ctc_cart_prod.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_ctc_delay.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_ctc_deriv.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_ctc_chain.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_ctc_eval.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_ctc_picard.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_ctc_lohner.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_ctc_static.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_definition.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_functions.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_integration.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_operators.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_geometry.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_polygons.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_serialization.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_slices_structure.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_trajectory.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_values.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_sep_polygon.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_sep_qinterprojf.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_sep_fixpoint_proj.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_sep_polar.cpp + #${CMAKE_CURRENT_SOURCE_DIR}/tests_predefined_tubes.cpp + #${CMAKE_CURRENT_SOURCE_DIR}/tests_predefined_tubes.h + #${CMAKE_CURRENT_SOURCE_DIR}/tests_arithmetic.cpp + #${CMAKE_CURRENT_SOURCE_DIR}/tests_cn.cpp + #${CMAKE_CURRENT_SOURCE_DIR}/tests_ctc_box.cpp + #${CMAKE_CURRENT_SOURCE_DIR}/tests_ctc_cart_prod.cpp + #${CMAKE_CURRENT_SOURCE_DIR}/tests_ctc_delay.cpp + #${CMAKE_CURRENT_SOURCE_DIR}/tests_ctc_deriv.cpp + #${CMAKE_CURRENT_SOURCE_DIR}/tests_ctc_chain.cpp + #${CMAKE_CURRENT_SOURCE_DIR}/tests_ctc_eval.cpp + #${CMAKE_CURRENT_SOURCE_DIR}/tests_ctc_picard.cpp + #${CMAKE_CURRENT_SOURCE_DIR}/tests_ctc_lohner.cpp + #${CMAKE_CURRENT_SOURCE_DIR}/tests_ctc_static.cpp + #${CMAKE_CURRENT_SOURCE_DIR}/tests_definition.cpp + #${CMAKE_CURRENT_SOURCE_DIR}/tests_functions.cpp + #${CMAKE_CURRENT_SOURCE_DIR}/tests_integration.cpp + #${CMAKE_CURRENT_SOURCE_DIR}/tests_operators.cpp + #${CMAKE_CURRENT_SOURCE_DIR}/tests_geometry.cpp + #${CMAKE_CURRENT_SOURCE_DIR}/tests_polygons.cpp + #${CMAKE_CURRENT_SOURCE_DIR}/tests_serialization.cpp + #${CMAKE_CURRENT_SOURCE_DIR}/tests_slices_structure.cpp + #${CMAKE_CURRENT_SOURCE_DIR}/tests_trajectory.cpp + #${CMAKE_CURRENT_SOURCE_DIR}/tests_values.cpp + #${CMAKE_CURRENT_SOURCE_DIR}/tests_sep_polygon.cpp + #${CMAKE_CURRENT_SOURCE_DIR}/tests_sep_qinterprojf.cpp + #${CMAKE_CURRENT_SOURCE_DIR}/tests_sep_fixpoint_proj.cpp + #${CMAKE_CURRENT_SOURCE_DIR}/tests_sep_polar.cpp ) add_executable(${TESTS_NAME} ${SRC_TESTS}) diff --git a/tests/core/tests_codac2_tubes.cpp b/tests/core/tests_codac2_tubes.cpp index 4746b869d..94e657f9b 100644 --- a/tests/core/tests_codac2_tubes.cpp +++ b/tests/core/tests_codac2_tubes.cpp @@ -21,11 +21,11 @@ TEST_CASE("Test codac2::tubes") { SECTION("Test TDomain") { - TDomain tdomain(Interval(0,1), 0.5); - CHECK(tdomain.nb_tslices() == 4); - CHECK(tdomain.t0_tf() == Interval(0,1)); + auto tdomain = create_tdomain(Interval(0,1), 0.5); + CHECK(tdomain->nb_tslices() == 4); + CHECK(tdomain->t0_tf() == Interval(0,1)); - const list& tslices = tdomain.tslices(); + const list& tslices = tdomain->tslices(); vector vector_tslices{ make_move_iterator(tslices.begin()), make_move_iterator(tslices.end()) }; @@ -36,31 +36,31 @@ TEST_CASE("Test codac2::tubes") CHECK(vector_tslices[2].t0_tf() == Interval(0.5,1)); CHECK(vector_tslices[3].t0_tf() == Interval(1,oo)); - CHECK(tdomain.iterator_tslice(-10.)->t0_tf() == Interval(-oo,0)); - CHECK(tdomain.iterator_tslice(-120.)->t0_tf() == Interval(-oo,0)); - CHECK(tdomain.iterator_tslice(0.2)->t0_tf() == Interval(0,0.5)); - CHECK(tdomain.iterator_tslice(5540.2)->t0_tf() == Interval(1,oo)); + CHECK(tdomain->iterator_tslice(-10.)->t0_tf() == Interval(-oo,0)); + CHECK(tdomain->iterator_tslice(-120.)->t0_tf() == Interval(-oo,0)); + CHECK(tdomain->iterator_tslice(0.2)->t0_tf() == Interval(0,0.5)); + CHECK(tdomain->iterator_tslice(5540.2)->t0_tf() == Interval(1,oo)); - CHECK(tdomain.nb_tubes() == 0); + CHECK(tdomain->nb_tubes() == 0); Tube x(2, tdomain); // adding a tubevector to the tdomain - CHECK(tdomain.nb_tubes() == 1); + CHECK(tdomain->nb_tubes() == 1); { // new scope Tube v(3, tdomain); - CHECK(tdomain.nb_tubes() == 2); + CHECK(tdomain->nb_tubes() == 2); } // end of scope, auto removing the tube - CHECK(tdomain.nb_tubes() == 1); + CHECK(tdomain->nb_tubes() == 1); } SECTION("Test degenerated TDomain") { - TDomain tdomain(Interval(1), 0.5); - CHECK(tdomain.nb_tslices() == 2); - CHECK(tdomain.t0_tf() == Interval(1)); - CHECK(tdomain.nb_tubes() == 0); + auto tdomain = create_tdomain(Interval(1), 0.5); + CHECK(tdomain->nb_tslices() == 2); + CHECK(tdomain->t0_tf() == Interval(1)); + CHECK(tdomain->nb_tubes() == 0); - const list& tslices = tdomain.tslices(); + const list& tslices = tdomain->tslices(); vector vector_tslices{ make_move_iterator(tslices.begin()), make_move_iterator(tslices.end()) }; @@ -72,12 +72,12 @@ TEST_CASE("Test codac2::tubes") SECTION("Test TDomain with gates") { - TDomain tdomain(Interval(0,1), 0.5, true); - CHECK(tdomain.nb_tslices() == 7); - CHECK(tdomain.t0_tf() == Interval(0,1)); - CHECK(tdomain.nb_tubes() == 0); + auto tdomain = create_tdomain(Interval(0,1), 0.5, true); + CHECK(tdomain->nb_tslices() == 7); + CHECK(tdomain->t0_tf() == Interval(0,1)); + CHECK(tdomain->nb_tubes() == 0); - const list& tslices = tdomain.tslices(); + const list& tslices = tdomain->tslices(); vector vector_tslices{ make_move_iterator(tslices.begin()), make_move_iterator(tslices.end()) }; @@ -91,26 +91,26 @@ TEST_CASE("Test codac2::tubes") CHECK(vector_tslices[5].t0_tf() == Interval(1,1)); CHECK(vector_tslices[6].t0_tf() == Interval(1,oo)); - CHECK(tdomain.iterator_tslice(-oo)->t0_tf() == Interval(-oo,0)); - CHECK(tdomain.iterator_tslice(-10.)->t0_tf() == Interval(-oo,0)); - CHECK(tdomain.iterator_tslice(0.)->t0_tf() == Interval(0)); - CHECK(tdomain.iterator_tslice(0.6)->t0_tf() == Interval(0.5,1)); - CHECK(tdomain.iterator_tslice(5540.2)->t0_tf() == Interval(1,oo)); - CHECK(tdomain.iterator_tslice(oo)->t0_tf() == Interval(1,oo)); + CHECK(tdomain->iterator_tslice(-oo)->t0_tf() == Interval(-oo,0)); + CHECK(tdomain->iterator_tslice(-10.)->t0_tf() == Interval(-oo,0)); + CHECK(tdomain->iterator_tslice(0.)->t0_tf() == Interval(0)); + CHECK(tdomain->iterator_tslice(0.6)->t0_tf() == Interval(0.5,1)); + CHECK(tdomain->iterator_tslice(5540.2)->t0_tf() == Interval(1,oo)); + CHECK(tdomain->iterator_tslice(oo)->t0_tf() == Interval(1,oo)); } SECTION("Test TDomain with sampling") { - TDomain tdomain(Interval(1), 0.5); - CHECK(tdomain.nb_tslices() == 2); - tdomain.sample(10.); - CHECK(tdomain.nb_tslices() == 3); - tdomain.sample(10.); // second sampling which creates a gate - CHECK(tdomain.nb_tslices() == 4); - tdomain.sample(10.); // no more action - CHECK(tdomain.nb_tslices() == 4); + auto tdomain = create_tdomain(Interval(1), 0.5); + CHECK(tdomain->nb_tslices() == 2); + tdomain->sample(10.); + CHECK(tdomain->nb_tslices() == 3); + tdomain->sample(10.); // second sampling which creates a gate + CHECK(tdomain->nb_tslices() == 4); + tdomain->sample(10.); // no more action + CHECK(tdomain->nb_tslices() == 4); - const list& tslices = tdomain.tslices(); + const list& tslices = tdomain->tslices(); vector vector_tslices{ make_move_iterator(tslices.begin()), make_move_iterator(tslices.end()) }; @@ -124,20 +124,20 @@ TEST_CASE("Test codac2::tubes") SECTION("Test unbounded TDomain") { - TDomain tdomain; - const list& tslices = tdomain.tslices(); + auto tdomain = create_tdomain(); + const list& tslices = tdomain->tslices(); vector vector_tslices{ make_move_iterator(tslices.begin()), make_move_iterator(tslices.end()) }; - CHECK(tdomain.nb_tslices() == 1); + CHECK(tdomain->nb_tslices() == 1); CHECK(vector_tslices.size() == 1); CHECK(vector_tslices[0].t0_tf() == Interval(-oo,oo)); } SECTION("Test TDomain with sampling and values") { - TDomain tdomain; - CHECK(tdomain.nb_tslices() == 1); + auto tdomain = create_tdomain(); + CHECK(tdomain->nb_tslices() == 1); Tube x(1, tdomain); x.set(Interval(4)); x(Interval(0,1)) = IntervalVector({{1,2}}); @@ -164,9 +164,9 @@ TEST_CASE("Test codac2::tubes") CHECK(s4->t0_tf() == Interval(3,oo)); CHECK(s4->codomain() == IntervalVector({{4}})); - CHECK(tdomain.nb_tslices() == 5); - tdomain.sample(1.3); - CHECK(tdomain.nb_tslices() == 6); + CHECK(tdomain->nb_tslices() == 5); + tdomain->sample(1.3); + CHECK(tdomain->nb_tslices() == 6); CHECK(s2->t0_tf() == Interval(1,1.3)); CHECK(s2->codomain() == IntervalVector({{5,6}})); const std::shared_ptr> s2bis = s2->next_slice(); @@ -178,13 +178,13 @@ TEST_CASE("Test codac2::tubes") SECTION("Test basic Tube") { - TDomain tdomain(Interval(0,1), 0.1, false); + auto tdomain = create_tdomain(Interval(0,1), 0.1, false); Tube x(3, tdomain); CHECK(x.size() == 3); - CHECK(&x.tdomain() == &tdomain); + CHECK(x.tdomain() == tdomain); CHECK(x.t0_tf() == Interval(0,1)); - CHECK(x.nb_slices() == tdomain.nb_tslices()); + CHECK(x.nb_slices() == tdomain->nb_tslices()); CHECK(x.nb_slices() == 12); CHECK(x.first_slice_ptr()->t0_tf() == Interval(-oo,0)); CHECK(x.last_slice_ptr()->t0_tf() == Interval(1,oo)); @@ -195,7 +195,7 @@ TEST_CASE("Test codac2::tubes") // TubeComponent CHECK(x[0].codomain() == Interval(-10,10)); CHECK(x[0].t0_tf() == Interval(0,1)); - CHECK(&x[0].tdomain() == &tdomain); + CHECK(x[0].tdomain() == tdomain); x[0].set(Interval(-20,20)); CHECK(x[0].codomain() == Interval(-20,20)); CHECK(x.codomain() == IntervalVector({Interval(-20,20),Interval(-10,10),Interval(-10,10)})); @@ -204,23 +204,23 @@ TEST_CASE("Test codac2::tubes") CHECK(x.codomain() == IntervalVector({Interval(-20,20),Interval(-20,20),Interval(-10,10)})); // Eval - CHECK(tdomain.nb_tubes() == 1); + CHECK(tdomain->nb_tubes() == 1); CHECK(static_cast(x(Interval(-oo,oo))) == x.codomain()); CHECK(static_cast(x(Interval(-1,1))) == x.codomain()); CHECK(static_cast(x(-42.)) == x.codomain()); // Eval: affectation at scalar t - CHECK(tdomain.nb_tslices() == 12); + CHECK(tdomain->nb_tslices() == 12); x(-42.) = IntervalVector(3,Interval(2.,3.)); - CHECK(tdomain.nb_tslices() == 14); + CHECK(tdomain->nb_tslices() == 14); CHECK(static_cast(x(-42.)) == IntervalVector(3,Interval(2.,3.))); CHECK(static_cast(x(ibex::previous_float(-42.))) == IntervalVector({Interval(-20,20),Interval(-20,20),Interval(-10,10)})); CHECK(static_cast(x(ibex::next_float(-42.))) == IntervalVector({Interval(-20,20),Interval(-20,20),Interval(-10,10)})); // Eval: affectation at interval t - CHECK(tdomain.nb_tslices() == 14); + CHECK(tdomain->nb_tslices() == 14); x(Interval(44,55)) = IntervalVector(3,Interval(9.,10.)); - CHECK(tdomain.nb_tslices() == 16); + CHECK(tdomain->nb_tslices() == 16); CHECK(static_cast(x(Interval(44,55))) == IntervalVector(3,Interval(9.,10.))); CHECK(static_cast(x(ibex::previous_float(44.))) == IntervalVector({Interval(-20,20),Interval(-20,20),Interval(-10,10)})); CHECK(static_cast(x(ibex::next_float(55.))) == IntervalVector({Interval(-20,20),Interval(-20,20),Interval(-10,10)})); @@ -249,11 +249,11 @@ TEST_CASE("Test codac2::tubes") SECTION("Test SliceVector") { - TDomain tdomain(Interval(0,1), 0.1); + auto tdomain = create_tdomain(Interval(0,1), 0.1); Tube x(2, tdomain); CHECK(x.nb_slices() == 12); - CHECK(x.first_slice_ptr() == tdomain.iterator_tslice(-oo)->_slices.at(&x)); - CHECK(x.last_slice_ptr() == tdomain.iterator_tslice(oo)->_slices.at(&x)); + CHECK(x.first_slice_ptr() == tdomain->iterator_tslice(-oo)->_slices.at(&x)); + CHECK(x.last_slice_ptr() == tdomain->iterator_tslice(oo)->_slices.at(&x)); for(auto& s : x) s.set(IntervalVector(2,s.t0_tf())); @@ -282,7 +282,7 @@ TEST_CASE("Test codac2::tubes") SECTION("Test again 1") { - TDomain tdomain(Interval(0,10), 0.01, true); // last argument creates "gates" (degenerated slices at scalar timesteps) + auto tdomain = create_tdomain(Interval(0,10), 0.01, true); // last argument creates "gates" (degenerated slices at scalar timesteps) Tube x(2, tdomain, codac::TFunction("(sin(sqrt(t)+((t-5)^2)*[-0.01,0.01]) ; cos(t)+sin(t/0.2)*[-0.1,0.1])")); Tube u(2, tdomain); @@ -307,7 +307,7 @@ TEST_CASE("Test codac2::tubes") SECTION("Test again 2") { /*{ - TDomain tdomain(Interval(0,10), 1., false); + auto tdomain = create_tdomain(Interval(0,10), 1., false); Tube x(2, tdomain); for(auto& sx : x) @@ -315,7 +315,7 @@ TEST_CASE("Test codac2::tubes") if(sx.t0_tf().contains(5.2)) { cout << "sample" << endl; - tdomain.sample(5.2); + tdomain->sample(5.2); } cout << sx << endl; } @@ -330,4 +330,17 @@ TEST_CASE("Test codac2::tubes") CHECK(f.eval_vector(t,x,u) == ApproxIntvVector(IntervalVector({{7, 8.100000000000002},{10, 11.10000000000001}}))); } } + + SECTION("Local TDomain, tube still valid after") + { + Tube *x; + + { + auto tdomain = create_tdomain(Interval(0,1), 0.1); + x = new Tube(1, tdomain); + CHECK(x->tdomain() == tdomain); + } + + CHECK(x->tdomain()->t0_tf() == Interval(0,1)); // should not segfault + } } \ No newline at end of file From beb1c4d24aa8a6fa96fbe55ebd61ca5e1a5667e7 Mon Sep 17 00:00:00 2001 From: SimonRohou Date: Fri, 26 Aug 2022 15:15:27 +0200 Subject: [PATCH 036/256] [sep] removing warning: explicit link request could not be resolved --- src/core/geometry/codac_SepPolygon.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/core/geometry/codac_SepPolygon.h b/src/core/geometry/codac_SepPolygon.h index 8b466e254..a1453428c 100644 --- a/src/core/geometry/codac_SepPolygon.h +++ b/src/core/geometry/codac_SepPolygon.h @@ -74,7 +74,7 @@ class SepPolygon : public ibex::SepBoundaryCtc { * The polygon boundary contractor is composed of a union of * contractor on segments (CtcSegment). * This contractor is minimal as an union of minimal contractors. - * See #ibex::SepBoundaryCtc. + * See ibex::SepBoundaryCtc. * * \param points list of segments representing the edges of the polygon in the format of ( ((a1_x, a1_y), (b1_x, b1_x)), ((a2_x, a2_y), (b2_x, b2_x)), ...) */ @@ -89,7 +89,7 @@ class SepPolygon : public ibex::SepBoundaryCtc { * The polygon boundary contractor is composed of a union of * contractor on segments (CtcSegment). * This contractor is minimal as an union of minimal contractors. - * See #ibex::SepBoundaryCtc. + * See ibex::SepBoundaryCtc. * * \param vertices list of vertices on the form (ax, ay), (bx, by), (cx, cy), ... */ @@ -104,7 +104,7 @@ class SepPolygon : public ibex::SepBoundaryCtc { * The polygon boundary contractor is composed of a union of * contractor on segments (CtcSegment). * This contractor is minimal as an union of minimal contractors. - * See #ibex::SepBoundaryCtc. + * See ibex::SepBoundaryCtc. * * \param ax list of x coordinate of the first point of each segment * \param ay list of y coordinate of the first point of each segment From 2bdc68e50e2550b2dbaf1d46b2d793d82497ecaa Mon Sep 17 00:00:00 2001 From: SimonRohou Date: Fri, 26 Aug 2022 15:16:01 +0200 Subject: [PATCH 037/256] [unsupp] removing conflict between classes with same names --- .../src/unsupported/image/codac_CtcRaster.h | 6 +- python/src/unsupported/image/codac_GeoImage.h | 195 +++++++++--------- python/src/unsupported/image/codac_images.cpp | 12 +- 3 files changed, 108 insertions(+), 105 deletions(-) diff --git a/python/src/unsupported/image/codac_CtcRaster.h b/python/src/unsupported/image/codac_CtcRaster.h index a955cac0e..fbf5baaf9 100644 --- a/python/src/unsupported/image/codac_CtcRaster.h +++ b/python/src/unsupported/image/codac_CtcRaster.h @@ -27,13 +27,13 @@ using codac::IntervalVector; using codac::Interval; -class CtcRaster : public codac::Ctc, public GeoImage { +class CtcRaster : public codac::Ctc, public from_pyibex::GeoImage { // Georeferenced image with integral image inclusion test public: CtcRaster(py::array_t data, double x0, double y0, double dx, double dy, bool inner) : Ctc(2), - GeoImage(data, x0, y0, dx, dy), inner(inner) + from_pyibex::GeoImage(data, x0, y0, dx, dy), inner(inner) { // constructor } @@ -51,7 +51,7 @@ class CtcRaster : public codac::Ctc, public GeoImage { //############################################################################## inline int CtcRaster::enclosed_pixels(PixelCoords& pixel_coords){ - return GeoImage::enclosed_pixels(pixel_coords[0],pixel_coords[1],pixel_coords[2],pixel_coords[3]); + return from_pyibex::GeoImage::enclosed_pixels(pixel_coords[0],pixel_coords[1],pixel_coords[2],pixel_coords[3]); } diff --git a/python/src/unsupported/image/codac_GeoImage.h b/python/src/unsupported/image/codac_GeoImage.h index 54d31100e..d3050d9ce 100644 --- a/python/src/unsupported/image/codac_GeoImage.h +++ b/python/src/unsupported/image/codac_GeoImage.h @@ -24,115 +24,118 @@ using codac::Interval; #define DATA_TYPE unsigned long -class GeoImage { - - // Georeferenced image with integral image inclusion test -public: - GeoImage(py::array_t data, double x0, double y0, double dx, double dy) : - // II(data), - info_i(data.request()), - boundingBox(2), - coordMapper(x0, y0, data.request().shape[0], data.request().shape[1], dx, dy) - { - if( info_i.ndim != 2 ) - throw std::runtime_error("Number of dimensions must be one"); - boundingBox = getBoundingBox(); - } +namespace from_pyibex +{ + class GeoImage { + + // Georeferenced image with integral image inclusion test + public: + GeoImage(py::array_t data, double x0, double y0, double dx, double dy) : + // II(data), + info_i(data.request()), + boundingBox(2), + coordMapper(x0, y0, data.request().shape[0], data.request().shape[1], dx, dy) + { + if( info_i.ndim != 2 ) + throw std::runtime_error("Number of dimensions must be one"); + boundingBox = getBoundingBox(); + } - DATA_TYPE pixelAt( int x, int y ); - DATA_TYPE enclosed_pixels( int xmin,int xmax,int ymin,int ymax); - IntervalVector getBoundingBox(); + DATA_TYPE pixelAt( int x, int y ); + DATA_TYPE enclosed_pixels( int xmin,int xmax,int ymin,int ymax); + IntervalVector getBoundingBox(); - PixelCoords world_to_grid(IntervalVector box) { return coordMapper.world_to_grid(box); } - IntervalVector grid_to_world(const PixelCoords& pixel_coords) {return coordMapper.grid_to_world(pixel_coords); } + PixelCoords world_to_grid(IntervalVector box) { return coordMapper.world_to_grid(box); } + IntervalVector grid_to_world(const PixelCoords& pixel_coords) {return coordMapper.grid_to_world(pixel_coords); } - bool checkBorder(PixelCoords& c, const std::vector& limit, const bool & b); + bool checkBorder(PixelCoords& c, const std::vector& limit, const bool & b); - IntervalVector boundingBox; + IntervalVector boundingBox; -protected: - // py::array_t II; - GeoMapper coordMapper; - py::buffer_info info_i; - // DATA_TYPE * img_ptr; -}; + protected: + // py::array_t II; + GeoMapper coordMapper; + py::buffer_info info_i; + // DATA_TYPE * img_ptr; + }; -//############################################################################## -//################# INLINE IMPLEMENTATION ################################## -//############################################################################## + //############################################################################## + //################# INLINE IMPLEMENTATION ################################## + //############################################################################## -inline DATA_TYPE GeoImage::pixelAt( int x, int y){ - // std::cerr << x << " " << y << "\n"; - if (x < 0 || y < 0) - return 0; - if ( !(x < info_i.shape[0]) ){ - std::cout << "x " << x << " " << info_i.shape[0] << "\n"; - assert(x < info_i.shape[0]); + inline DATA_TYPE GeoImage::pixelAt( int x, int y){ + // std::cerr << x << " " << y << "\n"; + if (x < 0 || y < 0) + return 0; + if ( !(x < info_i.shape[0]) ){ + std::cout << "x " << x << " " << info_i.shape[0] << "\n"; + assert(x < info_i.shape[0]); + } + if ( !(y < info_i.shape[1]) ) { + std::cout << "y " << y << " " << info_i.shape[1] << "\n"; + assert(y < info_i.shape[1]); + } + DATA_TYPE* ptr = (DATA_TYPE*) info_i.ptr; + long idx = (x*info_i.strides[0] + y*info_i.strides[1] ) / info_i.itemsize; //sizeof(DATA_TYPE); + // std::cout << idx << " "; + return ((DATA_TYPE*)info_i.ptr)[idx]; } - if ( !(y < info_i.shape[1]) ) { - std::cout << "y " << y << " " << info_i.shape[1] << "\n"; - assert(y < info_i.shape[1]); + // + inline DATA_TYPE GeoImage::enclosed_pixels( int xmin,int xmax,int ymin,int ymax) { + // std::cerr << "enclosed_pixels" << ymin << " " << ymin -1 << "\n"; + DATA_TYPE b1 = pixelAt(xmax,ymax); + DATA_TYPE b2 = pixelAt(xmax,ymin-1); + DATA_TYPE b3 = pixelAt(xmin-1,ymax); + DATA_TYPE b4 = pixelAt(xmin-1,ymin-1); + return b1 - b2 - b3 + b4; } - DATA_TYPE* ptr = (DATA_TYPE*) info_i.ptr; - long idx = (x*info_i.strides[0] + y*info_i.strides[1] ) / info_i.itemsize; //sizeof(DATA_TYPE); - // std::cout << idx << " "; - return ((DATA_TYPE*)info_i.ptr)[idx]; -} -// -inline DATA_TYPE GeoImage::enclosed_pixels( int xmin,int xmax,int ymin,int ymax) { - // std::cerr << "enclosed_pixels" << ymin << " " << ymin -1 << "\n"; - DATA_TYPE b1 = pixelAt(xmax,ymax); - DATA_TYPE b2 = pixelAt(xmax,ymin-1); - DATA_TYPE b3 = pixelAt(xmin-1,ymax); - DATA_TYPE b4 = pixelAt(xmin-1,ymin-1); - return b1 - b2 - b3 + b4; -} - -inline int boxSize(int xmin, int xmax, int ymin, int ymax){ - return (xmax+1 - xmin) * (ymax+1 - ymin); -} - -inline bool GeoImage::checkBorder(PixelCoords& c, const std::vector& limit, const bool & b){ - // b initialy a ThickBoolean IN == true - // Check the border to see if it is a pixel limit - int val, bound, nPix; - for(int i = 0; i < limit.size(); i++){ - if (limit[i] == false) continue; - switch (i) { - case 0: - bound = std::max(0,c[0]-1); - val = ( b == true ) ? boxSize(bound, bound, c[2], c[3]) : 0; - nPix = enclosed_pixels(bound, bound, c[2], c[3]); - break; - case 1: - bound = std::min(info_i.shape[0]-1,c[1]+1); - val = ( b == true ) ? boxSize(bound, bound, c[2], c[3]) : 0; - nPix = enclosed_pixels(bound, bound, c[2], c[3]); - break; - case 2: - bound = std::max(0,c[2]-1); - val = ( b == true ) ? boxSize(c[0], c[1], bound, bound) : 0; - nPix = enclosed_pixels(c[0], c[1], bound, bound); - break; - case 3: - bound = std::min(info_i.shape[1]-1,c[3]+1); - val = ( b == true ) ? boxSize(c[0], c[1], bound, bound) : 0; - nPix = enclosed_pixels(c[0], c[1], bound, bound); - break; - } - if (nPix != val) return false; + inline int boxSize(int xmin, int xmax, int ymin, int ymax){ + return (xmax+1 - xmin) * (ymax+1 - ymin); } - return true; -} -inline IntervalVector GeoImage::getBoundingBox(){ - int xmax = info_i.shape[0]-1; - int ymax = info_i.shape[1]-1; - return grid_to_world({ 0, xmax, 0, ymax}); -} + inline bool GeoImage::checkBorder(PixelCoords& c, const std::vector& limit, const bool & b){ + // b initialy a ThickBoolean IN == true + // Check the border to see if it is a pixel limit + int val, bound, nPix; + for(int i = 0; i < limit.size(); i++){ + if (limit[i] == false) continue; + switch (i) { + case 0: + bound = std::max(0,c[0]-1); + val = ( b == true ) ? boxSize(bound, bound, c[2], c[3]) : 0; + nPix = enclosed_pixels(bound, bound, c[2], c[3]); + break; + case 1: + bound = std::min(info_i.shape[0]-1,c[1]+1); + val = ( b == true ) ? boxSize(bound, bound, c[2], c[3]) : 0; + nPix = enclosed_pixels(bound, bound, c[2], c[3]); + break; + case 2: + bound = std::max(0,c[2]-1); + val = ( b == true ) ? boxSize(c[0], c[1], bound, bound) : 0; + nPix = enclosed_pixels(c[0], c[1], bound, bound); + break; + case 3: + bound = std::min(info_i.shape[1]-1,c[3]+1); + val = ( b == true ) ? boxSize(c[0], c[1], bound, bound) : 0; + nPix = enclosed_pixels(c[0], c[1], bound, bound); + break; + } + + if (nPix != val) return false; + } + return true; + } + + inline IntervalVector GeoImage::getBoundingBox(){ + int xmax = info_i.shape[0]-1; + int ymax = info_i.shape[1]-1; + return grid_to_world({ 0, xmax, 0, ymax}); + } +} // end of namespace from_pyibex -#endif //__PYIBEX_GEOIMAGE_H__ +#endif //__PYIBEX_GEOIMAGE_H__ \ No newline at end of file diff --git a/python/src/unsupported/image/codac_images.cpp b/python/src/unsupported/image/codac_images.cpp index 8d7728697..b1ef08e54 100644 --- a/python/src/unsupported/image/codac_images.cpp +++ b/python/src/unsupported/image/codac_images.cpp @@ -43,18 +43,18 @@ using namespace pybind11::literals; void export_GeoImage(py::module& m) { - class_ geo_image(m, "GeoImage"); + class_ geo_image(m, "GeoImage"); geo_image .def(init , double, double , double , double >(), __DOC_GEOIMAGE_CONSTRUCTOR, py::keep_alive<1,2>(), "img"_a, "x0"_a, "y0"_a, "dx"_a, "dy"_a) - .def("world_to_grid", &GeoImage::world_to_grid, __DOC_GEOIMAGE_WORLD_TO_GRID, "box"_a) - .def("grid_to_world", &GeoImage::grid_to_world, __DOC_GEOIMAGE_GRID_TO_WORLD, "pixel_coords"_a) - .def("pixelAt", &GeoImage::pixelAt, __DOC_GEOIMAGE_PIXELAT, "x"_a, "y"_a) - .def("enclosed_pixels", &GeoImage::enclosed_pixels, __DOC_GEOIMAGE_ENCLOSED_PIXELS, + .def("world_to_grid", &from_pyibex::GeoImage::world_to_grid, __DOC_GEOIMAGE_WORLD_TO_GRID, "box"_a) + .def("grid_to_world", &from_pyibex::GeoImage::grid_to_world, __DOC_GEOIMAGE_GRID_TO_WORLD, "pixel_coords"_a) + .def("pixelAt", &from_pyibex::GeoImage::pixelAt, __DOC_GEOIMAGE_PIXELAT, "x"_a, "y"_a) + .def("enclosed_pixels", &from_pyibex::GeoImage::enclosed_pixels, __DOC_GEOIMAGE_ENCLOSED_PIXELS, "xmin"_a, "xmax"_a, "ymin"_a, "ymax"_a ) // .def("test", &GeoImage::test, __DOC_GEOIMAGE_TEST, "box"_a) - .def_property_readonly("boundingBox", [](GeoImage& self){return self.boundingBox;}) + .def_property_readonly("boundingBox", [](from_pyibex::GeoImage& self){return self.boundingBox;}) ; } From 9ea487979712d8b7f3829682fa92784814d02ee2 Mon Sep 17 00:00:00 2001 From: SimonRohou Date: Fri, 26 Aug 2022 16:16:56 +0200 Subject: [PATCH 038/256] [tube] changing arguments order + tests (return value) --- .../2/domains/tube/codac2_AbstractSlice.cpp | 8 +-- .../2/domains/tube/codac2_AbstractSlice.h | 4 +- .../domains/tube/codac2_AbstractSlicedTube.h | 4 +- src/core/2/domains/tube/codac2_Slice.h | 32 ++++++------ src/core/2/domains/tube/codac2_Tube.h | 30 ++++++++---- tests/core/tests_codac2_tubes.cpp | 49 +++++++++++++------ 6 files changed, 79 insertions(+), 48 deletions(-) diff --git a/src/core/2/domains/tube/codac2_AbstractSlice.cpp b/src/core/2/domains/tube/codac2_AbstractSlice.cpp index cb6d58324..b5c71cb2f 100644 --- a/src/core/2/domains/tube/codac2_AbstractSlice.cpp +++ b/src/core/2/domains/tube/codac2_AbstractSlice.cpp @@ -33,16 +33,16 @@ namespace codac2 return *_it_tslice; } - const std::shared_ptr AbstractSlice::prev_abstract_slice() const + const std::shared_ptr AbstractSlice::prev_abstract_slice_ptr() const { - if(&(*_tubevector.first_abstract_slice()) == this) + if(&(*_tubevector.first_abstract_slice_ptr()) == this) return nullptr; return prev(_it_tslice)->slices().at(&_tubevector); } - const std::shared_ptr AbstractSlice::next_abstract_slice() const + const std::shared_ptr AbstractSlice::next_abstract_slice_ptr() const { - if(&(*_tubevector.last_abstract_slice()) == this) + if(&(*_tubevector.last_abstract_slice_ptr()) == this) return nullptr; return next(_it_tslice)->slices().at(&_tubevector); } diff --git a/src/core/2/domains/tube/codac2_AbstractSlice.h b/src/core/2/domains/tube/codac2_AbstractSlice.h index 0a0164875..0f856f352 100644 --- a/src/core/2/domains/tube/codac2_AbstractSlice.h +++ b/src/core/2/domains/tube/codac2_AbstractSlice.h @@ -40,8 +40,8 @@ namespace codac2 const Interval& t0_tf() const; const TSlice& tslice() const; - const std::shared_ptr prev_abstract_slice() const; - const std::shared_ptr next_abstract_slice() const; + const std::shared_ptr prev_abstract_slice_ptr() const; + const std::shared_ptr next_abstract_slice_ptr() const; protected: diff --git a/src/core/2/domains/tube/codac2_AbstractSlicedTube.h b/src/core/2/domains/tube/codac2_AbstractSlicedTube.h index 0cc3fdc54..528f6b015 100644 --- a/src/core/2/domains/tube/codac2_AbstractSlicedTube.h +++ b/src/core/2/domains/tube/codac2_AbstractSlicedTube.h @@ -24,8 +24,8 @@ namespace codac2 AbstractSlicedTube(const std::shared_ptr& tdomain); - virtual const std::shared_ptr& first_abstract_slice() const = 0; - virtual const std::shared_ptr& last_abstract_slice() const = 0; + virtual const std::shared_ptr& first_abstract_slice_ptr() const = 0; + virtual const std::shared_ptr& last_abstract_slice_ptr() const = 0; const std::shared_ptr& tdomain() const; Interval t0_tf() const; diff --git a/src/core/2/domains/tube/codac2_Slice.h b/src/core/2/domains/tube/codac2_Slice.h index 6342ad4e9..f1da9ac27 100644 --- a/src/core/2/domains/tube/codac2_Slice.h +++ b/src/core/2/domains/tube/codac2_Slice.h @@ -41,8 +41,8 @@ namespace codac2 } - explicit Slice(const T& box, const AbstractSlicedTube& tube_vector, const std::list::iterator& it_tslice) : - AbstractSlice(tube_vector,it_tslice), _codomain(box) + explicit Slice(const T& codomain, const AbstractSlicedTube& tube_vector, const std::list::iterator& it_tslice) : + AbstractSlice(tube_vector,it_tslice), _codomain(codomain) { } @@ -103,26 +103,26 @@ namespace codac2 return true; // todo } - const std::shared_ptr> prev_slice() const + const std::shared_ptr> prev_slice_ptr() const { - return std::static_pointer_cast>(prev_abstract_slice()); + return std::static_pointer_cast>(prev_abstract_slice_ptr()); } - std::shared_ptr> prev_slice() + std::shared_ptr> prev_slice_ptr() { return std::const_pointer_cast>( - static_cast(*this).prev_slice()); + static_cast(*this).prev_slice_ptr()); } - const std::shared_ptr> next_slice() const + const std::shared_ptr> next_slice_ptr() const { - return std::static_pointer_cast>(next_abstract_slice()); + return std::static_pointer_cast>(next_abstract_slice_ptr()); } - std::shared_ptr> next_slice() + std::shared_ptr> next_slice_ptr() { return std::const_pointer_cast>( - static_cast(*this).next_slice()); + static_cast(*this).next_slice_ptr()); } const T& codomain() const @@ -133,16 +133,16 @@ namespace codac2 T input_gate() const { T gate = codomain(); - if(prev_slice()) - gate &= prev_slice()->codomain(); + if(prev_slice_ptr()) + gate &= prev_slice_ptr()->codomain(); return gate; } T output_gate() const { T gate = codomain(); - if(next_slice()) - gate &= next_slice()->codomain(); + if(next_slice_ptr()) + gate &= next_slice_ptr()->codomain(); return gate; } @@ -151,7 +151,7 @@ namespace codac2 assert((size_t)codomain().size() == size()); _codomain = x; if(is_gate()) - _codomain &= prev_slice()->codomain() & next_slice()->codomain(); + _codomain &= prev_slice_ptr()->codomain() & next_slice_ptr()->codomain(); } void set_component(size_t i, const Interval& xi) @@ -159,7 +159,7 @@ namespace codac2 assert((size_t)codomain().size() == size()); _codomain[i] = xi; if(is_gate()) - _codomain[i] &= prev_slice()->codomain()[i] & next_slice()->codomain()[i]; + _codomain[i] &= prev_slice_ptr()->codomain()[i] & next_slice_ptr()->codomain()[i]; } friend std::ostream& operator<<(std::ostream& os, const Slice& x) diff --git a/src/core/2/domains/tube/codac2_Tube.h b/src/core/2/domains/tube/codac2_Tube.h index d0c3ddb43..f38c0c356 100644 --- a/src/core/2/domains/tube/codac2_Tube.h +++ b/src/core/2/domains/tube/codac2_Tube.h @@ -38,9 +38,10 @@ namespace codac2 { public: - explicit Tube(size_t n, const std::shared_ptr& tdomain) : - AbstractSlicedTube(tdomain) + explicit Tube(const std::shared_ptr& tdomain, size_t n = 1) : + Tube(tdomain, T(n)) { + assert(n > 0); for(std::list::iterator it = _tdomain->_tslices.begin(); it != _tdomain->_tslices.end(); ++it) { @@ -50,16 +51,27 @@ namespace codac2 } } - explicit Tube(size_t n, const std::shared_ptr& tdomain, const TFnc& f) : - Tube(n, tdomain) + explicit Tube(const std::shared_ptr& tdomain, const TFnc& f) : + Tube(tdomain, (size_t)f.image_dim()) { assert(f.nb_var() == 0 && "function's inputs must be limited to system variable"); - assert((size_t)f.image_dim() == n); for(auto& s : *this) s.set(f.eval_vector(s.t0_tf())); } + explicit Tube(const std::shared_ptr& tdomain, const T& default_value) : + AbstractSlicedTube(tdomain) + { + for(std::list::iterator it = _tdomain->_tslices.begin(); + it != _tdomain->_tslices.end(); ++it) + { + it->_slices.insert( + std::pair>>(this, + std::make_shared>(default_value, *this, it))); + } + } + explicit Tube(const Tube& x) : AbstractSlicedTube(x.tdomain()) { @@ -97,19 +109,19 @@ namespace codac2 return _tdomain->nb_tslices(); } - virtual const std::shared_ptr& first_abstract_slice() const + virtual const std::shared_ptr& first_abstract_slice_ptr() const { return _tdomain->tslices().front().slices().at(this); } - virtual const std::shared_ptr& last_abstract_slice() const + virtual const std::shared_ptr& last_abstract_slice_ptr() const { return _tdomain->tslices().back().slices().at(this); } const std::shared_ptr> first_slice_ptr() const { - return std::static_pointer_cast>(first_abstract_slice()); + return std::static_pointer_cast>(first_abstract_slice_ptr()); } const Slice& first_slice() const @@ -125,7 +137,7 @@ namespace codac2 const std::shared_ptr> last_slice_ptr() const { - return std::static_pointer_cast>(last_abstract_slice()); + return std::static_pointer_cast>(last_abstract_slice_ptr()); } const Slice& last_slice() const diff --git a/tests/core/tests_codac2_tubes.cpp b/tests/core/tests_codac2_tubes.cpp index 94e657f9b..5f4b1c3e4 100644 --- a/tests/core/tests_codac2_tubes.cpp +++ b/tests/core/tests_codac2_tubes.cpp @@ -17,6 +17,13 @@ using namespace ibex; using namespace codac2; using codac::oo; +Tube return_a_tube() +{ + return Tube( + create_tdomain(Interval(0,2),0.5), + IntervalVector(3,Interval(-1.5,1))); +} + TEST_CASE("Test codac2::tubes") { SECTION("Test TDomain") @@ -42,11 +49,11 @@ TEST_CASE("Test codac2::tubes") CHECK(tdomain->iterator_tslice(5540.2)->t0_tf() == Interval(1,oo)); CHECK(tdomain->nb_tubes() == 0); - Tube x(2, tdomain); // adding a tubevector to the tdomain + Tube x(tdomain, 2); // adding a tubevector to the tdomain CHECK(tdomain->nb_tubes() == 1); { // new scope - Tube v(3, tdomain); + Tube v(tdomain, 3); CHECK(tdomain->nb_tubes() == 2); } // end of scope, auto removing the tube @@ -138,7 +145,7 @@ TEST_CASE("Test codac2::tubes") { auto tdomain = create_tdomain(); CHECK(tdomain->nb_tslices() == 1); - Tube x(1, tdomain); + Tube x(tdomain, 1); x.set(Interval(4)); x(Interval(0,1)) = IntervalVector({{1,2}}); x(Interval(1,2)) = IntervalVector({{5,6}}); @@ -151,16 +158,16 @@ TEST_CASE("Test codac2::tubes") const std::shared_ptr> s0 = x.first_slice_ptr(); CHECK(s0->t0_tf() == Interval(-oo,0)); CHECK(s0->codomain() == IntervalVector({{4}})); - const std::shared_ptr> s1 = s0->next_slice(); + const std::shared_ptr> s1 = s0->next_slice_ptr(); CHECK(s1->t0_tf() == Interval(0,1)); CHECK(s1->codomain() == IntervalVector({{1,2}})); - const std::shared_ptr> s2 = s1->next_slice(); + const std::shared_ptr> s2 = s1->next_slice_ptr(); CHECK(s2->t0_tf() == Interval(1,2)); CHECK(s2->codomain() == IntervalVector({{5,6}})); - const std::shared_ptr> s3 = s2->next_slice(); + const std::shared_ptr> s3 = s2->next_slice_ptr(); CHECK(s3->t0_tf() == Interval(2,3)); CHECK(s3->codomain() == IntervalVector({{8,9}})); - const std::shared_ptr> s4 = s3->next_slice(); + const std::shared_ptr> s4 = s3->next_slice_ptr(); CHECK(s4->t0_tf() == Interval(3,oo)); CHECK(s4->codomain() == IntervalVector({{4}})); @@ -169,7 +176,7 @@ TEST_CASE("Test codac2::tubes") CHECK(tdomain->nb_tslices() == 6); CHECK(s2->t0_tf() == Interval(1,1.3)); CHECK(s2->codomain() == IntervalVector({{5,6}})); - const std::shared_ptr> s2bis = s2->next_slice(); + const std::shared_ptr> s2bis = s2->next_slice_ptr(); CHECK(s2bis->t0_tf() == Interval(1.3,2)); CHECK(s2bis->codomain() == IntervalVector({{5,6}})); CHECK(s3->t0_tf() == Interval(2,3)); @@ -179,7 +186,7 @@ TEST_CASE("Test codac2::tubes") SECTION("Test basic Tube") { auto tdomain = create_tdomain(Interval(0,1), 0.1, false); - Tube x(3, tdomain); + Tube x(tdomain, 3); CHECK(x.size() == 3); CHECK(x.tdomain() == tdomain); @@ -231,7 +238,7 @@ TEST_CASE("Test codac2::tubes") for(auto& s : x) { CHECK(&s == &(*s_)); - s_ = s_->next_slice(); + s_ = s_->next_slice_ptr(); } } @@ -242,7 +249,7 @@ TEST_CASE("Test codac2::tubes") for(const auto& s : x) { CHECK(&s == &(*s_)); - s_ = s_->next_slice(); + s_ = s_->next_slice_ptr(); } } } @@ -250,7 +257,7 @@ TEST_CASE("Test codac2::tubes") SECTION("Test SliceVector") { auto tdomain = create_tdomain(Interval(0,1), 0.1); - Tube x(2, tdomain); + Tube x(tdomain, 2); CHECK(x.nb_slices() == 12); CHECK(x.first_slice_ptr() == tdomain->iterator_tslice(-oo)->_slices.at(&x)); CHECK(x.last_slice_ptr() == tdomain->iterator_tslice(oo)->_slices.at(&x)); @@ -283,9 +290,12 @@ TEST_CASE("Test codac2::tubes") SECTION("Test again 1") { auto tdomain = create_tdomain(Interval(0,10), 0.01, true); // last argument creates "gates" (degenerated slices at scalar timesteps) - Tube x(2, tdomain, + Tube x(tdomain, codac::TFunction("(sin(sqrt(t)+((t-5)^2)*[-0.01,0.01]) ; cos(t)+sin(t/0.2)*[-0.1,0.1])")); - Tube u(2, tdomain); + Tube u(tdomain, 2); + + CHECK(x.size() == 2); + CHECK(u.size() == 2); codac::TFunction tf("x[2]", "u[2]", "(sin(x[1]) ; -sin(x[0]))"); CtcDiffInclusion ctc_diffincl(tf); @@ -337,10 +347,19 @@ TEST_CASE("Test codac2::tubes") { auto tdomain = create_tdomain(Interval(0,1), 0.1); - x = new Tube(1, tdomain); + x = new Tube(tdomain); CHECK(x->tdomain() == tdomain); } CHECK(x->tdomain()->t0_tf() == Interval(0,1)); // should not segfault + delete x; + } + + SECTION("Function returning a tube") + { + Tube x = return_a_tube(); + CHECK(x.tdomain()->t0_tf() == Interval(0,2)); + CHECK(x.size() == 3); + CHECK(x.codomain()[1] == Interval(-1.5,1)); } } \ No newline at end of file From d64d6db159a1b943f8ad27b545a462303a62a1b2 Mon Sep 17 00:00:00 2001 From: SimonRohou Date: Fri, 26 Aug 2022 16:25:04 +0200 Subject: [PATCH 039/256] [tube] TubeComponent, TubeEvaluation --- src/core/2/domains/tube/codac2_Tube.h | 159 +----------------- .../2/domains/tube/codac2_TubeComponent.h | 121 +++++++++++++ .../2/domains/tube/codac2_TubeEvaluation.h | 66 ++++++++ 3 files changed, 189 insertions(+), 157 deletions(-) create mode 100644 src/core/2/domains/tube/codac2_TubeComponent.h create mode 100644 src/core/2/domains/tube/codac2_TubeEvaluation.h diff --git a/src/core/2/domains/tube/codac2_Tube.h b/src/core/2/domains/tube/codac2_Tube.h index f38c0c356..1f2d3b516 100644 --- a/src/core/2/domains/tube/codac2_Tube.h +++ b/src/core/2/domains/tube/codac2_Tube.h @@ -307,163 +307,8 @@ namespace codac2 }; - template - class TubeEvaluation - { - public: - - const TubeEvaluation& operator=(const T& x) - { - // Sampling the tube only if affectation is performed - // (i.e. this is not done in the constructor) - std::list::iterator it_lb = _tubevector->_tdomain->sample(_t.lb(), false); - std::list::iterator it_ub = _tubevector->_tdomain->sample(_t.ub(), _t.is_degenerated()); - - do - { - std::static_pointer_cast>(it_lb->_slices.at(_tubevector))->set(x); - it_lb++; - } while(it_lb != it_ub); - - return *this; - } - - explicit operator T() - { - return _tubevector->eval(_t); - } - - friend std::ostream& operator<<(std::ostream& os, const TubeEvaluation& x) - { - os << x._tubevector->eval(x._t) << std::flush; - return os; - } - - - protected: - - explicit TubeEvaluation(const Tube* tubevector, double t) : - _t(Interval(t)), _tubevector(tubevector) - { - - } - - explicit TubeEvaluation(const Tube* tubevector, const Interval& t) : - _t(t), _tubevector(tubevector) - { - - } - - const Interval _t; - const Tube* _tubevector; - }; - - - template - class TubeComponent //: public AbstractConstTube> - { - protected: - - TubeComponent(Tube& tubevector, size_t i) : - _i(i), _tubevector(tubevector) - { - assert(i >= 0 && i < tubevector.size()); - } - - public: - - TubeComponent(const TubeComponent& tubevector_i) : - _i(tubevector_i._i), _tubevector(tubevector_i._tubevector) - { - - } - - size_t size() const - { - return 1; - } - - const std::shared_ptr& tdomain() const - { - return _tubevector.tdomain(); - } - - Interval t0_tf() const - { - return _tubevector.t0_tf(); - } - - Interval codomain() const - { - Interval codomain(Interval::EMPTY_SET); - for(const auto& s : _tubevector) - codomain |= s.codomain()[_i]; - return codomain; - } - - bool contains(const Trajectory& value) const - { - assert(false); - return true; - } - - void set(const Interval& codomain) - { - for(auto& s : _tubevector) - s.set_component(_i, codomain); - } - - const TubeComponent& operator=(const TubeComponent& x) - { - assert(x.tdomain() == tdomain()); - for(auto& s : _tubevector) - s.set_component(_i, std::static_pointer_cast>(s._it_tslice->_slices.at(&x._tubevector))->codomain()[x._i]); - return *this; - } - - const TubeComponent& operator=(std::pair,const TubeComponent> rel) - { - assert(rel.second.tdomain() == tdomain()); - for(auto& s : _tubevector) - s.set_component(_i, rel.first(std::static_pointer_cast>(s._it_tslice->_slices.at(&rel.second._tubevector))->codomain()[rel.second._i])); - return *this; - } - - friend std::ostream& operator<<(std::ostream& os, const TubeComponent& x) - { - os << "Component " << x._i << " of: " << x._tubevector << std::flush; - return os; - } - - std::pair,const TubeComponent> cos(const TubeComponent& x) - { - return std::make_pair(static_cast(ibex::cos), x); - } - - codac::Tube to_codac1() const - { - codac::Tube x(t0_tf()); - for(const auto& s : _tubevector) - if(!s.t0_tf().is_unbounded()) - x.set(s.codomain()[_i], s.t0_tf()); - for(const auto& s : _tubevector) // setting gate (were overwritten) - if(s.t0_tf().is_degenerated()) - x.set(s.codomain()[_i], s.t0_tf()); - return x; - } - - - protected: - - size_t _i; - Tube& _tubevector; - - template - friend class Tube; - }; - - template - std::pair,const TubeComponent> cos(const TubeComponent& x); + #include "codac2_TubeEvaluation.h" + #include "codac2_TubeComponent.h" } // namespace codac diff --git a/src/core/2/domains/tube/codac2_TubeComponent.h b/src/core/2/domains/tube/codac2_TubeComponent.h new file mode 100644 index 000000000..895d892b5 --- /dev/null +++ b/src/core/2/domains/tube/codac2_TubeComponent.h @@ -0,0 +1,121 @@ +/** + * \file + * + * ---------------------------------------------------------------------------- + * \date 2022 + * \author Simon Rohou + * \copyright Copyright 2022 Codac Team + * \license This program is distributed under the terms of + * the GNU Lesser General Public License (LGPL). + */ + +#ifndef __CODAC2_TUBECOMPONENT_H__ +#define __CODAC2_TUBECOMPONENT_H__ + +template +class TubeComponent //: public AbstractConstTube> +{ + protected: + + TubeComponent(Tube& tubevector, size_t i) : + _i(i), _tubevector(tubevector) + { + assert(i >= 0 && i < tubevector.size()); + } + + public: + + TubeComponent(const TubeComponent& tubevector_i) : + _i(tubevector_i._i), _tubevector(tubevector_i._tubevector) + { + + } + + size_t size() const + { + return 1; + } + + const std::shared_ptr& tdomain() const + { + return _tubevector.tdomain(); + } + + Interval t0_tf() const + { + return _tubevector.t0_tf(); + } + + Interval codomain() const + { + Interval codomain(Interval::EMPTY_SET); + for(const auto& s : _tubevector) + codomain |= s.codomain()[_i]; + return codomain; + } + + bool contains(const Trajectory& value) const + { + assert(false); + return true; + } + + void set(const Interval& codomain) + { + for(auto& s : _tubevector) + s.set_component(_i, codomain); + } + + const TubeComponent& operator=(const TubeComponent& x) + { + assert(x.tdomain() == tdomain()); + for(auto& s : _tubevector) + s.set_component(_i, std::static_pointer_cast>(s._it_tslice->_slices.at(&x._tubevector))->codomain()[x._i]); + return *this; + } + + const TubeComponent& operator=(std::pair,const TubeComponent> rel) + { + assert(rel.second.tdomain() == tdomain()); + for(auto& s : _tubevector) + s.set_component(_i, rel.first(std::static_pointer_cast>(s._it_tslice->_slices.at(&rel.second._tubevector))->codomain()[rel.second._i])); + return *this; + } + + friend std::ostream& operator<<(std::ostream& os, const TubeComponent& x) + { + os << "Component " << x._i << " of: " << x._tubevector << std::flush; + return os; + } + + std::pair,const TubeComponent> cos(const TubeComponent& x) + { + return std::make_pair(static_cast(ibex::cos), x); + } + + codac::Tube to_codac1() const + { + codac::Tube x(t0_tf()); + for(const auto& s : _tubevector) + if(!s.t0_tf().is_unbounded()) + x.set(s.codomain()[_i], s.t0_tf()); + for(const auto& s : _tubevector) // setting gate (were overwritten) + if(s.t0_tf().is_degenerated()) + x.set(s.codomain()[_i], s.t0_tf()); + return x; + } + + + protected: + + size_t _i; + Tube& _tubevector; + + template + friend class Tube; +}; + +template +std::pair,const TubeComponent> cos(const TubeComponent& x); + +#endif \ No newline at end of file diff --git a/src/core/2/domains/tube/codac2_TubeEvaluation.h b/src/core/2/domains/tube/codac2_TubeEvaluation.h new file mode 100644 index 000000000..2a90698f2 --- /dev/null +++ b/src/core/2/domains/tube/codac2_TubeEvaluation.h @@ -0,0 +1,66 @@ +/** + * \file + * + * ---------------------------------------------------------------------------- + * \date 2022 + * \author Simon Rohou + * \copyright Copyright 2022 Codac Team + * \license This program is distributed under the terms of + * the GNU Lesser General Public License (LGPL). + */ + +#ifndef __CODAC2_TUBEEVALUATION_H__ +#define __CODAC2_TUBEEVALUATION_H__ + +template +class TubeEvaluation +{ + public: + + const TubeEvaluation& operator=(const T& x) + { + // Sampling the tube only if affectation is performed + // (i.e. this is not done in the constructor) + std::list::iterator it_lb = _tubevector->_tdomain->sample(_t.lb(), false); + std::list::iterator it_ub = _tubevector->_tdomain->sample(_t.ub(), _t.is_degenerated()); + + do + { + std::static_pointer_cast>(it_lb->_slices.at(_tubevector))->set(x); + it_lb++; + } while(it_lb != it_ub); + + return *this; + } + + explicit operator T() + { + return _tubevector->eval(_t); + } + + friend std::ostream& operator<<(std::ostream& os, const TubeEvaluation& x) + { + os << x._tubevector->eval(x._t) << std::flush; + return os; + } + + + protected: + + explicit TubeEvaluation(const Tube* tubevector, double t) : + _t(Interval(t)), _tubevector(tubevector) + { + + } + + explicit TubeEvaluation(const Tube* tubevector, const Interval& t) : + _t(t), _tubevector(tubevector) + { + + } + + const Interval _t; + const Tube* _tubevector; +}; + +#endif \ No newline at end of file From a399e3f665157982062316603548c7eb6187e59b Mon Sep 17 00:00:00 2001 From: SimonRohou Date: Wed, 31 Aug 2022 13:34:07 +0200 Subject: [PATCH 040/256] [ctc] CtcDiffInclusion minor update --- src/core/2/contractors/codac2_CtcDiffInclusion.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/2/contractors/codac2_CtcDiffInclusion.cpp b/src/core/2/contractors/codac2_CtcDiffInclusion.cpp index d68dfdc3f..a08127a91 100644 --- a/src/core/2/contractors/codac2_CtcDiffInclusion.cpp +++ b/src/core/2/contractors/codac2_CtcDiffInclusion.cpp @@ -29,7 +29,7 @@ namespace codac2 void CtcDiffInclusion::contract(Tube& x, const Tube& u, TimePropag t_propa) { // Verifying that x and u share exactly the same tdomain and slicing: - assert(&x.tdomain() == &u.tdomain()); + assert(x.tdomain() == u.tdomain()); // Verifying that the provided tubes are consistent with the function assert((size_t)_f.nb_var() == 2); assert((size_t)_f.image_dim() == x.size()); From c27207a4c251f765deca2a5d5f46d6a1442ca288 Mon Sep 17 00:00:00 2001 From: SimonRohou Date: Wed, 31 Aug 2022 17:14:52 +0200 Subject: [PATCH 041/256] [tube] forgotten files in CMakeLists --- src/core/CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 1f4c5a2eb..b2762b779 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -186,6 +186,8 @@ ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/tube/codac2_TSlice.cpp ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/tube/codac2_TSlice.h ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/tube/codac2_Tube.h + ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/tube/codac2_TubeComponent.h + ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/tube/codac2_TubeEvaluation.h ${CMAKE_CURRENT_SOURCE_DIR}/2/contractors/codac2_CtcDiffInclusion.cpp ${CMAKE_CURRENT_SOURCE_DIR}/2/contractors/codac2_CtcDiffInclusion.h ${CMAKE_CURRENT_SOURCE_DIR}/2/integration/codac2_AbstractDomain.cpp From 017e38d7f93dde307a3b849e58939bc7b341c40e Mon Sep 17 00:00:00 2001 From: SimonRohou Date: Wed, 31 Aug 2022 17:15:16 +0200 Subject: [PATCH 042/256] [paving] adding get_boxes() method --- src/core/paving/codac_Paving.cpp | 29 +++++++++++++++++++++++++++++ src/core/paving/codac_Paving.h | 11 +++++++++++ src/core/paving/codac_Set.h | 2 ++ 3 files changed, 42 insertions(+) diff --git a/src/core/paving/codac_Paving.cpp b/src/core/paving/codac_Paving.cpp index 7a0f1c72c..f7fb4817c 100644 --- a/src/core/paving/codac_Paving.cpp +++ b/src/core/paving/codac_Paving.cpp @@ -129,9 +129,38 @@ namespace codac } // Extract methods + + void Paving::get_boxes(list& l_subpavings, SetValue val, SetValue neg_val) const + { + assert(!(val & neg_val) && "val and neg_val intersect"); + + if(neg_val != SetValue::DEFAULT && (m_value & neg_val) && !(m_value & val)) + { + // The current node and its leaves do not contain solutions + // So we stop here + } + + else + { + if(is_leaf()) + { + if(m_value & val) + l_subpavings.push_back(box()); + } + + else + { + m_first_subpaving->get_boxes(l_subpavings, val, neg_val); + m_second_subpaving->get_boxes(l_subpavings, val, neg_val); + } + } + } void Paving::get_pavings_intersecting(SetValue val, const IntervalVector& box_to_intersect, vector& v_subpavings, bool no_degenerated_intersection) const { + // todo: use the negation of val for a faster reading of the tree? + // See Paving::get_boxes + assert(box_to_intersect.size() == 2); IntervalVector inter = box_to_intersect & m_box; diff --git a/src/core/paving/codac_Paving.h b/src/core/paving/codac_Paving.h index d1163eabb..84704e0c3 100644 --- a/src/core/paving/codac_Paving.h +++ b/src/core/paving/codac_Paving.h @@ -12,6 +12,8 @@ #ifndef __CODAC_PAVING_H__ #define __CODAC_PAVING_H__ +#include +#include #include "codac_Set.h" #include "codac_ConnectedSubset.h" @@ -149,6 +151,15 @@ namespace codac /// \name Extract methods /// @{ + /** + * \brief Returns a set of boxes leaves of some value + * + * \param l_subpavings the set of returned objects + * \param val the value of the leaves (boxes of the paving) we are looking for + * \param neg_val the value for which we reject the leaves, optional argument used for faster execution along the tree + */ + void get_boxes(std::list& l_subpavings, SetValue val, SetValue neg_val = SetValue::DEFAULT) const; + /** * \brief Returns a set of Paving leaves of some value and intersecting a given box * diff --git a/src/core/paving/codac_Set.h b/src/core/paving/codac_Set.h index 46bd575f6..df9094d7c 100644 --- a/src/core/paving/codac_Set.h +++ b/src/core/paving/codac_Set.h @@ -25,6 +25,8 @@ namespace codac */ enum class SetValue { + DEFAULT = 0x00, ///< does not have a meaning, only used for default values of arguments + UNKNOWN = 0x01, ///< unable to conclude OUT = 0x02, ///< outside the solution set IN = 0x04, ///< inside the solution set From 1bd1628a87feefd7d3ad436017190317a36ff1e6 Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Thu, 18 Aug 2022 23:28:39 +0200 Subject: [PATCH 043/256] Updated workflows --- .github/workflows/macosmatrix.yml | 8 +- .github/workflows/unixmatrix.yml | 73 ++++++++------- .github/workflows/vcmatrix.yml | 9 +- packages/choco/codac/codac.nuspec | 10 ++ .../codac/tools/chocolateybeforemodify.ps1 | 13 +++ .../choco/codac/tools/chocolateyinstall.ps1 | 92 +++++++++++++++++-- packages/choco/codac/tools/sharedVars.ps1 | 1 + 7 files changed, 158 insertions(+), 48 deletions(-) create mode 100644 packages/choco/codac/tools/chocolateybeforemodify.ps1 diff --git a/.github/workflows/macosmatrix.yml b/.github/workflows/macosmatrix.yml index 5073450a8..0d176f562 100644 --- a/.github/workflows/macosmatrix.yml +++ b/.github/workflows/macosmatrix.yml @@ -49,11 +49,15 @@ jobs: if: runner.os=='macOS' - run: brew install doxygen ; brew install graphviz ; python -m pip install --upgrade pip ; pip install --upgrade wheel setuptools sphinx breathe sphinx_rtd_theme sphinx-tabs sphinx-issues sphinx-reredirects if: runner.os=='macOS' - - run: git clone --depth 1 -b actions https://github.com/lebarsfa/ibex-lib.git ; cd ibex-lib ; mkdir build ; cd build ; cmake -E env CXXFLAGS="${{ matrix.cfg.cmake_flags }}" CFLAGS="${{ matrix.cfg.cmake_flags }}" cmake ${{ matrix.cfg.cmake_params }} -D CMAKE_SYSTEM_NAME=Darwin -D CMAKE_OSX_ARCHITECTURES=${{ matrix.cfg.arch }} -D INTERVAL_LIB=${{ matrix.cfg.ilib }} -D CMAKE_INSTALL_PREFIX="../../ibex" .. ; cmake --build . --config Release --target install ; cd ../.. + - run: | + wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20220812/ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip --no-check-certificate -nv + unzip -q ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip + rm -Rf ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip + cp -Rf ibex/* /usr/local/ shell: bash - run: | mkdir build ; cd build - cmake -E env CXXFLAGS="${{ matrix.cfg.cmake_flags }}" CFLAGS="${{ matrix.cfg.cmake_flags }}" cmake ${{ matrix.cfg.cmake_params }} -D CMAKE_SYSTEM_NAME=Darwin -D CMAKE_OSX_ARCHITECTURES=${{ matrix.cfg.arch }} -D CMAKE_PREFIX_PATH="ibex" -D CMAKE_INSTALL_PREFIX="../codac" -D WITH_PYTHON=ON .. + cmake -E env CXXFLAGS="${{ matrix.cfg.cmake_flags }}" CFLAGS="${{ matrix.cfg.cmake_flags }}" cmake ${{ matrix.cfg.cmake_params }} -D CMAKE_SYSTEM_NAME=Darwin -D CMAKE_OSX_ARCHITECTURES=${{ matrix.cfg.arch }} -D CMAKE_INSTALL_PREFIX="../codac" -D WITH_PYTHON=ON .. cmake --build . --config Release --target install cmake --build . --config Release --target pip_package ; cp `ls *.whl` ../`ls *.whl | sed "s/py3-none-any/cp${{ matrix.cfg.py_v_maj }}${{ matrix.cfg.py_v_min }}-cp${{ matrix.cfg.py_v_maj }}${{ matrix.cfg.py_v_min }}${{ matrix.cfg.cpcfg }}/"` cd .. diff --git a/.github/workflows/unixmatrix.yml b/.github/workflows/unixmatrix.yml index 73ae5ab63..c984c9a9e 100644 --- a/.github/workflows/unixmatrix.yml +++ b/.github/workflows/unixmatrix.yml @@ -35,17 +35,19 @@ jobs: - { os: windows-2019, shell: cmd, arch: x86, runtime: vc16, cmake_params: '-G "Visual Studio 16" -A Win32', choco_flags: '--x86', cmake_flags: ' /wd4267 /wd4244 /wd4305 /wd4996', test_config: 'Release/', desc: 'Windows Visual Studio 2019 x86' } - { os: windows-2019, shell: cmd, arch: x64, runtime: vc15, cmake_params: '-G "Visual Studio 16" -T v141 -A x64', cmake_flags: ' /wd4267 /wd4244 /wd4305 /wd4996', test_config: 'Release/', desc: 'Windows Visual Studio 2017 x64' } - { os: windows-2019, shell: cmd, arch: x86, runtime: vc15, cmake_params: '-G "Visual Studio 16" -T v141 -A Win32', choco_flags: '--x86', cmake_flags: ' /wd4267 /wd4244 /wd4305 /wd4996', test_config: 'Release/', desc: 'Windows Visual Studio 2017 x86' } -# - { os: windows-2022, shell: cmd, arch: x64, runtime: mingw11, cmake_params: '-G "MSYS Makefiles"', cmake_flags: '-fPIC', desc: 'Windows MinGW 11.2.0 x64' } -# - { os: windows-2022, shell: cmd, arch: x86, runtime: mingw11, cmake_params: '-G "MSYS Makefiles"', cmake_flags: '-fPIC', choco_flags: '--x86', desc: 'Windows MinGW 11.2.0 x86' } + - { os: windows-2022, shell: cmd, arch: x64, runtime: mingw11, cmake_params: '-G "MSYS Makefiles"', cmake_flags: '-fPIC', desc: 'Windows MinGW 11.2.0 x64' } + - { os: windows-2022, shell: cmd, arch: x86, runtime: mingw11, cmake_params: '-G "MSYS Makefiles"', cmake_flags: '-fPIC', choco_flags: '--x86', desc: 'Windows MinGW 11.2.0 x86' } - { os: windows-2019, shell: cmd, arch: x64, runtime: mingw8, cmake_params: '-G "MSYS Makefiles"', cmake_flags: '-fPIC', desc: 'Windows MinGW 8.1.0 x64' } - { os: windows-2019, shell: cmd, arch: x86, runtime: mingw8, cmake_params: '-G "MSYS Makefiles"', cmake_flags: '-fPIC', choco_flags: '--x86', desc: 'Windows MinGW 8.1.0 x86' } -# - { os: windows-2019, shell: cmd, arch: x64, runtime: mingw7, cmake_params: '-G "MSYS Makefiles"', cmake_flags: '-fPIC', desc: 'Windows MinGW 7.3.0 x64' } + - { os: windows-2019, shell: cmd, arch: x64, runtime: mingw7, cmake_params: '-G "MSYS Makefiles"', cmake_flags: '-fPIC', desc: 'Windows MinGW 7.3.0 x64' } - { os: windows-2019, shell: cmd, arch: x86, runtime: mingw7, cmake_params: '-G "MSYS Makefiles"', cmake_flags: '-fPIC', choco_flags: '--x86', desc: 'Windows MinGW 7.3.0 x86' } - { os: ubuntu-22.04, shell: bash, arch: amd64, runtime: jammy, cmake_flags: '-fPIC', desc: 'Ubuntu 22.04' } - { os: ubuntu-20.04, shell: bash, arch: amd64, runtime: focal, cmake_flags: '-fPIC', desc: 'Ubuntu 20.04' } - { os: ubuntu-18.04, shell: bash, arch: amd64, runtime: bionic, cmake_flags: '-fPIC', desc: 'Ubuntu 18.04' } - - { os: macos-12, shell: bash, arch: x86_64, runtime: monterey, cmake_flags: '-fPIC', desc: 'macOS Monterey' } - - { os: macos-11, shell: bash, arch: x86_64, runtime: bigsur, cmake_flags: '-fPIC', desc: 'macOS Big Sur' } + - { os: macos-12, shell: bash, arch: arm64, runtime: monterey, cmake_params: '-D CMAKE_SYSTEM_NAME=Darwin -D CMAKE_OSX_ARCHITECTURES=arm64', cmake_flags: '-fPIC', desc: 'macOS Monterey arm64' } + - { os: macos-12, shell: bash, arch: x86_64, runtime: monterey, cmake_params: '-D CMAKE_SYSTEM_NAME=Darwin -D CMAKE_OSX_ARCHITECTURES=x86_64', cmake_flags: '-fPIC', desc: 'macOS Monterey x86_64' } + - { os: macos-11, shell: bash, arch: arm64, runtime: bigsur, cmake_params: '-D CMAKE_SYSTEM_NAME=Darwin -D CMAKE_OSX_ARCHITECTURES=arm64', cmake_flags: '-fPIC', desc: 'macOS Big Sur arm64' } + - { os: macos-11, shell: bash, arch: x86_64, runtime: bigsur, cmake_params: '-D CMAKE_SYSTEM_NAME=Darwin -D CMAKE_OSX_ARCHITECTURES=x86_64', cmake_flags: '-fPIC', desc: 'macOS Big Sur x86_64' } name: ${{ matrix.cfg.desc }} steps: - uses: actions/checkout@v3 @@ -73,64 +75,62 @@ jobs: - run: | choco install -y -r --no-progress mingw --version=7.3.0 --force ${{ matrix.cfg.choco_flags }} echo C:\ProgramData\chocolatey\lib\mingw\tools\install\mingw32\bin>>%GITHUB_PATH% - choco install -y -r --no-progress ibex --version=2.8.9.20220413 --ignore-dependencies ${{ matrix.cfg.choco_flags }} - if: (runner.os=='Windows')&&(matrix.cfg.runtime=='mingw7')&&(matrix.cfg.arch=='x86') + if: (runner.os=='Windows')&&(matrix.cfg.runtime=='mingw7') - run: | choco install -y -r --no-progress mingw --version=8.1.0 --force ${{ matrix.cfg.choco_flags }} echo C:\ProgramData\chocolatey\lib\mingw\tools\install\mingw32\bin>>%GITHUB_PATH% if: (runner.os=='Windows')&&(matrix.cfg.runtime=='mingw8')&&(matrix.cfg.arch=='x86') + - run: | + choco install -y -r --no-progress mingw --version=11.2.0 --force ${{ matrix.cfg.choco_flags }} + echo C:\ProgramData\chocolatey\lib\mingw\tools\install\mingw32\bin>>%GITHUB_PATH% + if: (runner.os=='Windows')&&(matrix.cfg.runtime=='mingw11')&&(matrix.cfg.arch=='x86') - run: | choco install -y -r --no-progress checksum wget winflexbison make patch zip ${{ matrix.cfg.choco_flags }} choco install -y -r --no-progress eigen --version=3.4.0 ${{ matrix.cfg.choco_flags }} - choco install -y -r --no-progress ibex --version=2.8.9.20220413 ${{ matrix.cfg.choco_flags }} + wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20220812/ibex.2.8.9.20220812.nupkg --no-check-certificate -nv + choco install -y -r --no-progress --ignore-dependencies -s . ibex --version=2.8.9.20220812 ${{ matrix.cfg.choco_flags }} --params "'/url:https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20220812/ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip'" + del /f /q ibex.2.8.9.20220812.nupkg if: runner.os=='Windows' -# - run: sudo apt-get -q update ; sudo apt-get -y install flex bison libeigen3-dev || true -# shell: bash -# if: runner.os=='Linux' - run: | sudo sh -c 'echo "deb [trusted=yes] https://www.ensta-bretagne.fr/packages/`lsb_release --id -s | tr [:upper:] [:lower:]`/`lsb_release -cs` ./" > /etc/apt/sources.list.d/ensta-bretagne.list' sudo apt-get -q update ; sudo apt-get -y install libibex-dev libeigen3-dev dpkg-dev || true shell: bash if: runner.os=='Linux' - - run: brew install eigen + - run: | + brew install eigen + wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20220812/ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip --no-check-certificate -nv + unzip -q ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip + rm -Rf ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip + cp -Rf ibex/* /usr/local/ if: runner.os=='macOS' - - run: git clone --depth 1 -b actions https://github.com/lebarsfa/ibex-lib.git ; cd ibex-lib ; mkdir build ; cd build ; cmake -E env CXXFLAGS="${{ matrix.cfg.cmake_flags }}" CFLAGS="${{ matrix.cfg.cmake_flags }}" cmake ${{ matrix.cfg.cmake_params }} -D CMAKE_INSTALL_PREFIX="../../ibex" .. ; cmake --build . --config Release --target install ; cd ../.. - shell: bash - if: ((runner.os=='Windows')&&(matrix.cfg.runtime!='mingw8'))||((runner.os!='Windows')&&(runner.os!='Linux')) +# - run: git clone --depth 1 -b actions https://github.com/lebarsfa/ibex-lib.git ; cd ibex-lib ; mkdir build ; cd build ; cmake -E env CXXFLAGS="${{ matrix.cfg.cmake_flags }}" CFLAGS="${{ matrix.cfg.cmake_flags }}" cmake ${{ matrix.cfg.cmake_params }} -D CMAKE_INSTALL_PREFIX="../../ibex" .. ; cmake --build . --config Release --target install ; cd ../.. +# shell: bash - run: | mkdir build ; cd build - cmake -E env CXXFLAGS="${{ matrix.cfg.cmake_flags }}" CFLAGS="${{ matrix.cfg.cmake_flags }}" cmake ${{ matrix.cfg.cmake_params }} -D CMAKE_PREFIX_PATH="ibex" -D CMAKE_INSTALL_PREFIX="../codac" .. + cmake -E env CXXFLAGS="${{ matrix.cfg.cmake_flags }}" CFLAGS="${{ matrix.cfg.cmake_flags }}" cmake ${{ matrix.cfg.cmake_params }} -D CMAKE_INSTALL_PREFIX="../codac" .. cmake --build . --config Debug --target install cd .. + sed_param=s/PATH_SUFFIXES\ /PATHS\ \$\{CMAKE_CURRENT_LIST_FILE\}\\/..\\/..\\/..\\/..\\/\ PATH_SUFFIXES\ / + if [ ${{ runner.os }} = Windows ]; then sed -i "$sed_param" codac/share/codac/cmake/*.cmake ; fi + zip -q -r codac_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip codac shell: bash - - run: | - mkdir -p codac_standalone/example ; cd codac_standalone - wget https://community.chocolatey.org/api/v2/package/eigen/3.4.0 --no-check-certificate -nv ; unzip -q 3.4.0 -d eigen ; rm -Rf 3.4.0 eigen/*.xml eigen/*.nuspec eigen/_* eigen/package eigen/tools - cp -Rf ../ibex . ; cp -Rf ../codac . ; cp -Rf ../tests/test_codac/* ./example/ ; cd .. ; zip -q -r codac_standalone_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip codac_standalone ; cd codac_standalone/example - cmake ${{ matrix.cfg.cmake_params }} . - cmake --build . --config Release - ./${{ matrix.cfg.test_config }}my_project - cd ../.. - shell: bash - if: ((runner.os=='Windows')&&(matrix.cfg.runtime!='mingw8'))||((runner.os!='Windows')&&(runner.os!='Linux')) - run: | cd packages/choco - cp -Rf ../../codac/* codac sed_param=s/\1\<\\/version\>/\${PACKAGE_VERSION}\<\\/version\>/ sed -i "$sed_param" codac/codac.nuspec - sed_param=s/PATH_SUFFIXES\ /PATHS\ \$\{CMAKE_CURRENT_LIST_FILE\}\\/..\\/..\\/..\\/..\\/\ PATH_SUFFIXES\ / - sed -i "$sed_param" codac/share/codac/cmake/*.cmake + sed_param=s/codac-1/codac-${PACKAGE_VERSION}/ + sed -i "$sed_param" codac/tools/chocolateyinstall.ps1 mv -f codac codac.$PACKAGE_VERSION cd codac.$PACKAGE_VERSION choco pack mv -f codac.$PACKAGE_VERSION.nupkg ../../../ cd ../../.. checksum -f=codac.$PACKAGE_VERSION.nupkg -t=sha256 - choco source add -n=current-directory -s . --priority=100 - choco install -y -r --no-progress codac --version=$PACKAGE_VERSION ${{ matrix.cfg.choco_flags }} - mv -f codac.$PACKAGE_VERSION.nupkg codac.$PACKAGE_VERSION${{ matrix.cfg.choco_flags }}.nupkg + choco install -y -r --no-progress --ignore-dependencies -s . codac --params "'/url:./codac_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip'" + if [ ${{ matrix.cfg.runtime }} != mingw8 ] || [ ${{ matrix.cfg.arch }} != x64 ]; then rm -Rf codac.$PACKAGE_VERSION.nupkg ; fi # To avoid upload conflicts of the same file... + checksum -f=codac_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip -t=sha256 shell: bash - if: (runner.os=='Windows')&&(matrix.cfg.runtime=='mingw8') + if: runner.os=='Windows' - run: | cd packages chmod +x ./genlibcodac-dev.sh @@ -139,14 +139,19 @@ jobs: sudo dpkg -i libcodac-dev-$PACKAGE_VERSION\_${{ matrix.cfg.arch }}.deb shell: bash if: runner.os=='Linux' + - run: | + cp -Rf codac/* /usr/local/ + shell: bash + if: (runner.os=='macOS')&&(matrix.cfg.arch=='x86_64') - run: | rm -Rf codac cd tests/test_codac cmake ${{ matrix.cfg.cmake_params }} . cmake --build . --config Release + file ./${{ matrix.cfg.test_config }}my_project ./${{ matrix.cfg.test_config }}my_project shell: bash - if: ((runner.os=='Windows')&&(matrix.cfg.runtime=='mingw8'))||(runner.os=='Linux') + if: (runner.os!='macOS')||(matrix.cfg.arch=='x86_64') - uses: xresloader/upload-to-github-release@v1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/vcmatrix.yml b/.github/workflows/vcmatrix.yml index 8570b9bd7..255eba5ef 100644 --- a/.github/workflows/vcmatrix.yml +++ b/.github/workflows/vcmatrix.yml @@ -51,15 +51,16 @@ jobs: - run: choco install -y -r --no-progress winflexbison patch if: startsWith(matrix.cfg.runtime, 'vc') - run: choco install -y -r --no-progress eigen --version=3.4.0 ${{ matrix.cfg.choco_flags }} - shell: cmd if: runner.os=='Windows' - run: choco install -y -r --no-progress doxygen.install graphviz & python -m pip install --upgrade pip & pip install --upgrade wheel setuptools & git clone --depth 1 -b v3.1.1 https://github.com/sphinx-doc/sphinx & cd sphinx & pip install . & pip install --upgrade breathe sphinx-issues sphinx-tabs sphinx_rtd_theme sphinx-reredirects if: runner.os=='Windows' - - run: git clone --depth 1 -b actions https://github.com/lebarsfa/ibex-lib.git ; cd ibex-lib ; mkdir build ; cd build ; cmake -E env CXXFLAGS=" /wd4267 /wd4244 /wd4305 /wd4996" CFLAGS=" /wd4267 /wd4244 /wd4305 /wd4996" cmake ${{ matrix.cfg.cmake_params }} -D CMAKE_INSTALL_PREFIX="../../ibex" .. ; cmake --build . --config Release --target install ; cd ../.. - shell: bash + - run: | + wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20220812/ibex.2.8.9.20220812.nupkg --no-check-certificate -nv + choco install -y -r --no-progress --ignore-dependencies -s . ibex --version=2.8.9.20220812 ${{ matrix.cfg.choco_flags }} --params "'/url:https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20220812/ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip'" + del /f /q ibex.2.8.9.20220812.nupkg - run: | mkdir build ; cd build - cmake -E env CXXFLAGS=" /wd4267 /wd4244 /wd4305 /wd4996" CFLAGS=" /wd4267 /wd4244 /wd4305 /wd4996" cmake ${{ matrix.cfg.cmake_params }} -D CMAKE_PREFIX_PATH="ibex" -D CMAKE_INSTALL_PREFIX="../codac" -D WITH_PYTHON=ON .. + cmake -E env CXXFLAGS=" /wd4267 /wd4244 /wd4305 /wd4996" CFLAGS=" /wd4267 /wd4244 /wd4305 /wd4996" cmake ${{ matrix.cfg.cmake_params }} -D CMAKE_INSTALL_PREFIX="../codac" -D WITH_PYTHON=ON .. cmake --build . --config Release --target install cmake --build . --config Release --target pip_package ; cp `ls *.whl` ../`ls *.whl | sed "s/py3-none-any/cp${{ matrix.cfg.py_v_maj }}${{ matrix.cfg.py_v_min }}-cp${{ matrix.cfg.py_v_maj }}${{ matrix.cfg.py_v_min }}${{ matrix.cfg.cpcfg }}/"` ; pip install ../*.whl ; python -c "import sys; print(sys.version)" ; python ../examples/tuto/01_getting_started/01_getting_started.py cd .. diff --git a/packages/choco/codac/codac.nuspec b/packages/choco/codac/codac.nuspec index c6620641f..27826d88b 100644 --- a/packages/choco/codac/codac.nuspec +++ b/packages/choco/codac/codac.nuspec @@ -24,6 +24,16 @@ Codac is a library providing tools for constraint programming over reals, trajec - Check that all the packages and their dependencies were installed, if one failed (e.g. due to network-related errors) try to reinstall it using `--force` or try a previous version... - 32 bit versions of Qt Creator do not seem available any more, see https://github.com/AdmiringWorm/chocolatey-packages/issues/362. - If multiple compilers are already installed, Qt Creator might show multiple possibilities in the Configure Project panel, ensure you choose one compatible with https://chocolatey.org/packages/codac#dependencies. + +## Package parameters +The following package parameters can be set: +- `/url:URL` - Will install the specified binary package, see versions from https://github.com/codac-team/codac/releases (the Windows `PATH` might need to be updated manually, etc.). By default, only the MinGW libraries compatible with the corresponding MinGW Chocolatey package dependency are installed. Use the standard parameter `choco install --ignore-dependencies ...` to avoid installing MinGW Chocolatey package dependency if needed. +- `/NoPath` - Will not try to update Windows PATH. +- `/NoRegistry` - Will not try to update Windows registry. +To pass package parameters, use `--params "''"` (e.g. `choco install codac --params "'/NoPath /NoRegistry'"`), e.g. +``` +choco install -y -f --ignore-dependencies codac --params "'/url:https://github.com/codac-team/codac/releases/download/codac-2.8.9.20220812/codac_x86_vc15.zip'" +``` https://github.com/codac-team/codac/releases diff --git a/packages/choco/codac/tools/chocolateybeforemodify.ps1 b/packages/choco/codac/tools/chocolateybeforemodify.ps1 new file mode 100644 index 000000000..37cecf4ea --- /dev/null +++ b/packages/choco/codac/tools/chocolateybeforemodify.ps1 @@ -0,0 +1,13 @@ +$ErrorActionPreference = 'Stop'; # Stop on all errors. + +# Source variables which are shared between install and uninstall. +. $PSScriptRoot\sharedVars.ps1 + +#Uninstall-BinFile -Name libcodac-rob.a +#Uninstall-BinFile -Name libcodac.a + +if (Test-Path $CMakeRegistryPath) { + if (Test-Path $CMakeSystemRepositoryPath\$CMakePackageName) { + Remove-Item "$CMakeSystemRepositoryPath\$CMakePackageName" + } +} diff --git a/packages/choco/codac/tools/chocolateyinstall.ps1 b/packages/choco/codac/tools/chocolateyinstall.ps1 index 7b030baa2..81a8a975b 100644 --- a/packages/choco/codac/tools/chocolateyinstall.ps1 +++ b/packages/choco/codac/tools/chocolateyinstall.ps1 @@ -3,17 +3,93 @@ # Source variables which are shared between install and uninstall. . $PSScriptRoot\sharedVars.ps1 -New-Item "$CMakeSystemRepositoryPath\$CMakePackageName" -ItemType directory -Force -New-ItemProperty -Name "CMakePackageDir" -PropertyType String -Value "$env:ChocolateyPackageFolder\share\$CMakePackageName\cmake" -Path "$CMakeSystemRepositoryPath\$CMakePackageName" -Force +$pp = Get-PackageParameters -# Get 32 bit binaries if needed and delete unnecessary files... -if (Test-Path "$env:ChocolateyPackageFolder\x86") { - if ((Get-ProcessorBits 32) -or $env:ChocolateyForceX86 -eq $true) { - Copy-Item -Recurse -Force -Path "$env:ChocolateyPackageFolder\x86\*" -Destination "$env:ChocolateyPackageFolder" +$root = "$env:ChocolateyPackageFolder\.." + +if (!$pp['url']) { + $url = 'https://github.com/lebarsfa/codac/releases/download/codac-1/codac_x86_mingw8.zip' + $checksum = '7855B0393DF389855E126E712C51DAB8649F19BE0101579C753FBEDF108B0132' + $url64 = 'https://github.com/lebarsfa/codac/releases/download/codac-1/codac_x64_mingw8.zip' + $checksum64 = '89C6BB0DE09BD86524947827B3ED88CBBDCAB63141AEE5F7FF273CFB416C0D22' + $packageArgs = @{ + packageName = $env:ChocolateyPackageName + unzipLocation = "$root" + url = $url + url64bit = $url64 + checksum = $checksum + checksumType = 'sha256' + checksum64 = $checksum64 + checksumType64= 'sha256' + } + Install-ChocolateyZipPackage @packageArgs + + $runtime = "mingw" +} +else +{ + $url = $pp['url'] + #$checksum = $pp['sha256'] + $packageArgs = @{ + packageName = $env:ChocolateyPackageName + unzipLocation = "$root" + url = $url + #checksum = $checksum + #checksumType = 'sha256' + } + Install-ChocolateyZipPackage @packageArgs + + # Analyze url to guess what to add to Windows PATH... + if ($url -match "x86") { + $arch = "x86" + } + else { + $arch = "x64" + } + if ($url -match "vc8") { + $runtime = "vc8" + } + if ($url -match "vc9") { + $runtime = "vc9" + } + elseif ($url -match "vc10") { + $runtime = "vc10" } - Remove-Item -Recurse -Force "$env:ChocolateyPackageFolder\x86" + elseif ($url -match "vc11") { + $runtime = "vc11" + } + elseif ($url -match "vc12") { + $runtime = "vc12" + } + elseif ($url -match "vc14") { + $runtime = "vc14" + } + elseif ($url -match "vc15") { + $runtime = "vc15" + } + elseif ($url -match "vc16") { + $runtime = "vc16" + } + elseif ($url -match "vc17") { + $runtime = "vc17" + } + elseif ($url -match "vc18") { + $runtime = "vc18" + } + else { + $runtime = "mingw" + } +} + +if (!$pp['NoRegistry']) { + New-Item "$CMakeSystemRepositoryPath\$CMakePackageName" -ItemType directory -Force + New-ItemProperty -Name "CMakePackageDir" -PropertyType String -Value "$env:ChocolateyPackageFolder\share\$CMakePackageName\cmake" -Path "$CMakeSystemRepositoryPath\$CMakePackageName" -Force } +#$pathtoadd = "$env:ChocolateyPackageFolder\bin" +#if (!($pp['NoPath']) -and !([environment]::GetEnvironmentVariable("Path","Machine") -match [regex]::escape($pathtoadd))) { +# $newpath = [environment]::GetEnvironmentVariable("Path","Machine") + ";$pathtoadd" +# [environment]::SetEnvironmentVariable("Path",$newpath,"Machine") +#} #Install-BinFile -Name libcodac.a -Path "$env:ChocolateyPackageFolder\lib" #Install-BinFile -Name libcodac-rob.a -Path "$env:ChocolateyPackageFolder\lib" -#Install-BinFile -Name libcodac-pybex.a -Path "$env:ChocolateyPackageFolder\lib" diff --git a/packages/choco/codac/tools/sharedVars.ps1 b/packages/choco/codac/tools/sharedVars.ps1 index 1a1a238d4..4fc220aea 100644 --- a/packages/choco/codac/tools/sharedVars.ps1 +++ b/packages/choco/codac/tools/sharedVars.ps1 @@ -1,3 +1,4 @@ +if ((Get-ProcessorBits 32) -or $env:ChocolateyForceX86 -eq $true) { $arch = "x86" } else { $arch = "x64" } $CMakeRegistryPath = "HKCU:\SOFTWARE\Kitware\CMake" $CMakeSystemRepositoryPath = "HKLM:\SOFTWARE\Kitware\CMake\Packages" $CMakePackageName = "Codac" From 53b4bbf49764c2983cbbc5c2f58a935e5b889ed9 Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Sat, 3 Sep 2022 17:24:01 +0200 Subject: [PATCH 044/256] Temporary fix due to server firewall blocking apt --- .github/workflows/unixmatrix.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/unixmatrix.yml b/.github/workflows/unixmatrix.yml index c984c9a9e..dd82151b6 100644 --- a/.github/workflows/unixmatrix.yml +++ b/.github/workflows/unixmatrix.yml @@ -92,7 +92,10 @@ jobs: del /f /q ibex.2.8.9.20220812.nupkg if: runner.os=='Windows' - run: | - sudo sh -c 'echo "deb [trusted=yes] https://www.ensta-bretagne.fr/packages/`lsb_release --id -s | tr [:upper:] [:lower:]`/`lsb_release -cs` ./" > /etc/apt/sources.list.d/ensta-bretagne.list' + wget https://www.ensta-bretagne.fr/packages/`lsb_release --id -s | tr [:upper:] [:lower:]`/`lsb_release -cs`/libibex-dev-2.8.9.20220809-0`lsb_release -cs`0_amd64.deb --no-check-certificate -nv + sudo apt-get -q update ; sudo apt-get -y install ./libibex-dev-2.8.9.20220809-0`lsb_release -cs`0_amd64.deb || true + rm -Rf ./libibex-dev-2.8.9.20220809-0`lsb_release -cs`0_amd64.deb + #sudo sh -c 'echo "deb [trusted=yes] https://www.ensta-bretagne.fr/packages/`lsb_release --id -s | tr [:upper:] [:lower:]`/`lsb_release -cs` ./" > /etc/apt/sources.list.d/ensta-bretagne.list' sudo apt-get -q update ; sudo apt-get -y install libibex-dev libeigen3-dev dpkg-dev || true shell: bash if: runner.os=='Linux' From cf385c8c2dc6541665c643e111898dbec9e65bd5 Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Sat, 3 Sep 2022 23:46:03 +0200 Subject: [PATCH 045/256] Added back codac_standalone --- .github/workflows/unixmatrix.yml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/.github/workflows/unixmatrix.yml b/.github/workflows/unixmatrix.yml index dd82151b6..fb8b23804 100644 --- a/.github/workflows/unixmatrix.yml +++ b/.github/workflows/unixmatrix.yml @@ -117,6 +117,24 @@ jobs: if [ ${{ runner.os }} = Windows ]; then sed -i "$sed_param" codac/share/codac/cmake/*.cmake ; fi zip -q -r codac_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip codac shell: bash + - run: | + mkdir -p codac_standalone/example ; cd codac_standalone + wget https://community.chocolatey.org/api/v2/package/eigen/3.4.0 --no-check-certificate -nv ; unzip -q 3.4.0 -d eigen ; rm -Rf 3.4.0 eigen/*.xml eigen/*.nuspec eigen/_* eigen/package eigen/tools + if [ ${{ runner.os }} = Windows ]; then cp -Rf /C/ProgramData/chocolatey/lib/ibex . ; rm -Rf ibex/tools ibex/ibex.* + elif [ ${{ runner.os }} = Linux ]; then mkdir -p ibex/include ; mkdir -p ibex/lib ; mkdir -p ibex/share ; mkdir -p ibex/bin ; cp -Rf /usr/include/ibex* ibex/include/ ; cp -Rf /usr/lib/*ibex* ibex/lib/ ; cp -Rf /usr/share/*ibex* ibex/share/ ; cp -Rf /usr/share/pkgconfig ibex/share/ ; cp -Rf /usr/bin/ibex* ibex/bin/ + elif [ ${{ runner.os }} = macOS ]; then cp -Rf ../ibex . + fi + cp -Rf ../codac . ; cp -Rf ../tests/test_codac/* ./example/ ; cd .. ; zip -q -r codac_standalone_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip codac_standalone + shell: bash + - run: | + cd codac_standalone/example + cmake ${{ matrix.cfg.cmake_params }} . + cmake --build . --config Release + file ./${{ matrix.cfg.test_config }}my_project + ./${{ matrix.cfg.test_config }}my_project + cd ../.. + shell: bash + if: (runner.os!='macOS')||(matrix.cfg.arch=='x86_64') - run: | cd packages/choco sed_param=s/\1\<\\/version\>/\${PACKAGE_VERSION}\<\\/version\>/ @@ -153,6 +171,7 @@ jobs: cmake --build . --config Release file ./${{ matrix.cfg.test_config }}my_project ./${{ matrix.cfg.test_config }}my_project + cd ../.. shell: bash if: (runner.os!='macOS')||(matrix.cfg.arch=='x86_64') - uses: xresloader/upload-to-github-release@v1 From 53e17b77c5aa38630828aaea19a0f3fd3932c658 Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Sat, 3 Sep 2022 23:00:01 +0200 Subject: [PATCH 046/256] Documentation updates --- .../install/01-installation-full-linux.rst | 2 + .../install/01-installation-full-macos.rst | 8 ++-- .../install/01-installation-full-windows.rst | 2 +- doc/doc/install/01-installation-python.rst | 41 +++++++++++-------- doc/doc/install/01-installation.rst | 30 +++++++------- 5 files changed, 45 insertions(+), 38 deletions(-) diff --git a/doc/doc/install/01-installation-full-linux.rst b/doc/doc/install/01-installation-full-linux.rst index f2fc15723..4f65ad29d 100644 --- a/doc/doc/install/01-installation-full-linux.rst +++ b/doc/doc/install/01-installation-full-linux.rst @@ -30,6 +30,8 @@ Then, check your installation `with the instructions of this page <03-start-cpp- and check that "My first tube:Tube [0, 10]" appears. + Similar archives exist also for all the supported configurations. + Install from sources (latest development) ----------------------------------------- diff --git a/doc/doc/install/01-installation-full-macos.rst b/doc/doc/install/01-installation-full-macos.rst index f8cfe3b3a..bb7ed00f3 100644 --- a/doc/doc/install/01-installation-full-macos.rst +++ b/doc/doc/install/01-installation-full-macos.rst @@ -14,11 +14,11 @@ Install Homebrew package manager and build tools: .. code-block:: bash - /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" + /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" brew install wget autoconf automake libtool brew install --cask cmake -Download and extract e.g. ``codac_standalone_x64_bigsur.zip`` (for macOS 11 Big Sur) from ``_, then in ``example`` folder run: +Download and extract *e.g.* ``codac_standalone_x86_64_bigsur.zip`` (for macOS 11 Big Sur (x86_64)) from ``_, then in ``example`` folder run: .. code-block:: bash @@ -26,13 +26,13 @@ Download and extract e.g. ``codac_standalone_x64_bigsur.zip`` (for macOS 11 Big and check that "My first tube:Tube [0, 10]" appears. -Optionally, download and run ``_ before running the project, and check that a tube appears in VIBes window. +Optionally, download and run ``_ (see also https://support.apple.com/HT211861) before running the project, and check that a tube appears in VIBes window. Building from sources --------------------- -You will probably need to install those prerequisites (assuming you installed `Homebrew package manager `_): +You will probably need to install those prerequisites (assuming you installed `Homebrew package manager `_): .. code-block:: bash diff --git a/doc/doc/install/01-installation-full-windows.rst b/doc/doc/install/01-installation-full-windows.rst index 5d271b2cb..674ba81c3 100644 --- a/doc/doc/install/01-installation-full-windows.rst +++ b/doc/doc/install/01-installation-full-windows.rst @@ -41,7 +41,7 @@ Optionally, for Python (*e.g.* ``choco install python --version=3.8.2``) and doc choco install doxygen.install graphviz python -m pip install --upgrade pip pip install --upgrade wheel setuptools - git clone -b v3.1.1 https://github.com/sphinx-doc/sphinx + git clone --depth 1 -b v3.1.1 https://github.com/sphinx-doc/sphinx cd sphinx pip install . pip install --upgrade breathe sphinx-issues sphinx-tabs sphinx_rtd_theme sphinx-reredirects diff --git a/doc/doc/install/01-installation-python.rst b/doc/doc/install/01-installation-python.rst index 803d24e5f..36d83bc67 100644 --- a/doc/doc/install/01-installation-python.rst +++ b/doc/doc/install/01-installation-python.rst @@ -35,36 +35,41 @@ In case you want to use Codac only with Python, then the installation procedure The :gbg:`✓` configurations are officially supported at the moment: -+---------------+----------------+-----------------+-----------------+----------------+----------------+ -|Language |Linux (amd64) |Windows (x64) |Windows (x86) |macOS (x86_64) |Online | -+===============+================+=================+=================+================+================+ -|Python 3.6 |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` ||online-py|_ | -+---------------+----------------+-----------------+-----------------+----------------+ + -|Python 3.7 |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` | | -+---------------+----------------+-----------------+-----------------+----------------+ + -|Python 3.8 |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` | | -+---------------+----------------+-----------------+-----------------+----------------+ + -|Python 3.9 |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` | | -+---------------+----------------+-----------------+-----------------+----------------+ + -|Python 3.10 |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` | | -+---------------+----------------+-----------------+-----------------+----------------+----------------+ ++---------------+----------------+-----------------+-----------------+----------------+----------------+----------------+ +|Language |Linux (amd64) |Windows (x64) |Windows (x86) |macOS (arm64) |macOS (x86_64) |Online | ++===============+================+=================+=================+================+================+================+ +|Python 3.6 |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` ||online-py|_ | ++---------------+----------------+-----------------+-----------------+----------------+----------------+ + +|Python 3.7 |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` | | ++---------------+----------------+-----------------+-----------------+----------------+----------------+ + +|Python 3.8 |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` | | ++---------------+----------------+-----------------+-----------------+----------------+----------------+ + +|Python 3.9 |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` | | ++---------------+----------------+-----------------+-----------------+----------------+----------------+ + +|Python 3.10 |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` | | ++---------------+----------------+-----------------+-----------------+----------------+----------------+----------------+ If a configuration in this table does not work, please `contact us `_. .. warning:: - | **macOS Big Sur and Monterey (amd64):** - | `Python wheels provided for Catalina may still be installed `_, try something similar to: + | **macOS Big Sur and later (arm64):** + | `Rosetta `_ might be necessary to run `VIBes `_ (not tested). + +.. warning:: + + | **macOS Big Sur and later (x86_64):** + | `Python wheels provided for Catalina or earlier may need to be installed with something similar to `: .. code-block:: bash - sudo pip3 install --platform macosx_10_15_x86_64 --only-binary=:all: --target=/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.8/lib/python3.8/site-packages codac + sudo pip3 install --platform macosx_10_9_x86_64 --only-binary=:all: --target=/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.8/lib/python3.8/site-packages codac - Depending on the way Python was installed, the path to specify after ``--target`` may differ, *e.g.* if Python was installed from https://www.python.org/ftp/python/3.10.4/python-3.10.4-macos11.pkg, it may be ``/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages``. Otherwise, run ``python3 -m site`` to check the ``site-packages`` full path in ``sys.path list``. + Depending on the way Python was installed, the path to specify after ``--target`` may differ, *e.g.* if Python was installed from https://www.python.org/ftp/python/3.10.4/python-3.10.4-macos11.pkg, it may be ``/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages``. Otherwise, run ``python3 -m site`` to check the ``site-packages`` full path in ``sys.path list``. .. note:: - ARM-based computers can still probably follow `Installing local Python binding <../dev/info_dev.html>`_ (not tested). + Unsupported ARM-based computers can still probably follow `Installing local Python binding <../dev/info_dev.html>`_ (not tested). Update your Codac Python package diff --git a/doc/doc/install/01-installation.rst b/doc/doc/install/01-installation.rst index e350fd974..1065838ca 100644 --- a/doc/doc/install/01-installation.rst +++ b/doc/doc/install/01-installation.rst @@ -37,21 +37,21 @@ Codac is available in both C++17 and Python3. Note that you can also :ref:`use C The :gbg:`✓` configurations are officially supported at the moment: -+---------------+----------------+-----------------+-----------------+----------------+----------------+ -|Language |Linux (amd64) |Windows (x64) |Windows (x86) |macOS (x86_64) |Online | -+===============+================+=================+=================+================+================+ -|C++17 ||linux-cpp|_ ||win-cpp|_ ||win-cpp|_ ||macos-cpp|_ | | -+---------------+----------------+-----------------+-----------------+----------------+----------------+ -|Python 3.6 ||linux-py|_ ||win-py|_ ||win-py|_ ||macos-py|_ ||online-py|_ | -+---------------+----------------+-----------------+-----------------+----------------+ + -|Python 3.7 ||linux-py|_ ||win-py|_ ||win-py|_ ||macos-py|_ | | -+---------------+----------------+-----------------+-----------------+----------------+ + -|Python 3.8 ||linux-py|_ ||win-py|_ ||win-py|_ ||macos-py|_ | | -+---------------+----------------+-----------------+-----------------+----------------+ + -|Python 3.9 ||linux-py|_ ||win-py|_ ||win-py|_ ||macos-py|_ | | -+---------------+----------------+-----------------+-----------------+----------------+ + -|Python 3.10 ||linux-py|_ ||win-py|_ ||win-py|_ ||macos-py|_ | | -+---------------+----------------+-----------------+-----------------+----------------+----------------+ ++---------------+----------------+-----------------+-----------------+----------------+----------------+----------------+ +|Language |Linux (amd64) |Windows (x64) |Windows (x86) |macOS (arm64) |macOS (x86_64) |Online | ++===============+================+=================+=================+================+================+================+ +|C++17 ||linux-cpp|_ ||win-cpp|_ ||win-cpp|_ ||macos-cpp|_ ||macos-cpp|_ | | ++---------------+----------------+-----------------+-----------------+----------------+----------------+----------------+ +|Python 3.6 ||linux-py|_ ||win-py|_ ||win-py|_ ||macos-py|_ ||macos-py|_ ||online-py|_ | ++---------------+----------------+-----------------+-----------------+----------------+----------------+ + +|Python 3.7 ||linux-py|_ ||win-py|_ ||win-py|_ ||macos-py|_ ||macos-py|_ | | ++---------------+----------------+-----------------+-----------------+----------------+----------------+ + +|Python 3.8 ||linux-py|_ ||win-py|_ ||win-py|_ ||macos-py|_ ||macos-py|_ | | ++---------------+----------------+-----------------+-----------------+----------------+----------------+ + +|Python 3.9 ||linux-py|_ ||win-py|_ ||win-py|_ ||macos-py|_ ||macos-py|_ | | ++---------------+----------------+-----------------+-----------------+----------------+----------------+ + +|Python 3.10 ||linux-py|_ ||win-py|_ ||win-py|_ ||macos-py|_ ||macos-py|_ | | ++---------------+----------------+-----------------+-----------------+----------------+----------------+----------------+ | **Click on the links in the table to access the related installation procedures.** | If a configuration in this table does not work, please `contact us `_. From 21c04b197af93075e3ac92018316c1aaf5b76a91 Mon Sep 17 00:00:00 2001 From: SimonRohou Date: Wed, 7 Sep 2022 14:04:33 +0200 Subject: [PATCH 047/256] [tplane] changing access of generic method --- src/robotics/loops/codac_TPlane.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/robotics/loops/codac_TPlane.h b/src/robotics/loops/codac_TPlane.h index fb1833b6a..d46b8f788 100644 --- a/src/robotics/loops/codac_TPlane.h +++ b/src/robotics/loops/codac_TPlane.h @@ -169,8 +169,6 @@ namespace codac */ static void verbose(bool verbose = true); - protected: - /** * \brief Tries to prove the existence of loops in each detection set * @@ -180,6 +178,8 @@ namespace codac */ void compute_proofs(const std::function& f); + protected: + /** * \brief Recursive computation of the tplane, from the tube of positions \f$[\mathbf{p}](\cdot)\f$ * and the tube of velocities \f$[\mathbf{v}](\cdot)\f$. From ee941a3dbdd6cd09189fd83a5a13875922617597 Mon Sep 17 00:00:00 2001 From: SimonRohou Date: Wed, 7 Sep 2022 14:47:11 +0200 Subject: [PATCH 048/256] [src] corrected warnings --- src/core/2/domains/tube/codac2_AbstractConstTube.h | 5 +++++ src/core/2/domains/tube/codac2_TSlice.cpp | 2 +- src/robotics/contractors/codac_CtcConstell.cpp | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/core/2/domains/tube/codac2_AbstractConstTube.h b/src/core/2/domains/tube/codac2_AbstractConstTube.h index 443799198..e35352c59 100644 --- a/src/core/2/domains/tube/codac2_AbstractConstTube.h +++ b/src/core/2/domains/tube/codac2_AbstractConstTube.h @@ -38,6 +38,11 @@ namespace codac2 explicit AbstractConstTube(const T& x); + virtual ~AbstractConstTube() + { + + } + virtual size_t size() const = 0; virtual bool contains(const TrajectoryVector& value) const = 0; virtual Interval t0_tf() const = 0; diff --git a/src/core/2/domains/tube/codac2_TSlice.cpp b/src/core/2/domains/tube/codac2_TSlice.cpp index 2e4e5e88c..80aefcfe5 100644 --- a/src/core/2/domains/tube/codac2_TSlice.cpp +++ b/src/core/2/domains/tube/codac2_TSlice.cpp @@ -26,7 +26,7 @@ namespace codac2 TSlice::TSlice(const TSlice& tslice, const Interval& tdomain) : TSlice(tdomain) { - for(const auto[k,s] : tslice._slices) + for(const auto&[k,s] : tslice._slices) _slices.insert(pair>( k, s->duplicate())); } diff --git a/src/robotics/contractors/codac_CtcConstell.cpp b/src/robotics/contractors/codac_CtcConstell.cpp index feb2f0f26..407a27718 100644 --- a/src/robotics/contractors/codac_CtcConstell.cpp +++ b/src/robotics/contractors/codac_CtcConstell.cpp @@ -40,7 +40,7 @@ namespace codac assert(a.size() == 2); IntervalVector union_result(2, Interval::EMPTY_SET); - for(const auto mj : m_map) + for(const auto& mj : m_map) union_result |= a & mj.subvector(0,1); a = union_result; } From 85b6244e6c9c1ec73c9f1ebdd7e635713c48287a Mon Sep 17 00:00:00 2001 From: SimonRohou Date: Wed, 7 Sep 2022 15:10:55 +0200 Subject: [PATCH 049/256] [tube] updated to_codac1() --- src/core/2/domains/tube/codac2_Tube.cpp | 42 +++++++++++++++++++++++++ src/core/2/domains/tube/codac2_Tube.h | 17 +++------- src/core/CMakeLists.txt | 1 + tests/core/tests_codac2_tubes.cpp | 4 +-- 4 files changed, 49 insertions(+), 15 deletions(-) create mode 100644 src/core/2/domains/tube/codac2_Tube.cpp diff --git a/src/core/2/domains/tube/codac2_Tube.cpp b/src/core/2/domains/tube/codac2_Tube.cpp new file mode 100644 index 000000000..13e51eb48 --- /dev/null +++ b/src/core/2/domains/tube/codac2_Tube.cpp @@ -0,0 +1,42 @@ +/** + * \file + * + * ---------------------------------------------------------------------------- + * \date 2022 + * \author Simon Rohou + * \copyright Copyright 2022 Codac Team + * \license This program is distributed under the terms of + * the GNU Lesser General Public License (LGPL). + */ + +#include "codac2_Tube.h" + +using namespace std; +using namespace codac; + +namespace codac2 +{ + codac::Tube to_codac1(const Tube& x) + { + codac::Tube x_(x.t0_tf()); + for(const auto& s : x) + if(!s.t0_tf().is_unbounded()) + x_.set(s.codomain(), s.t0_tf()); + for(const auto& s : x) // setting gate (were overwritten) + if(s.t0_tf().is_degenerated()) + x_.set(s.codomain(), s.t0_tf()); + return x_; + } + + codac::TubeVector to_codac1(const Tube& x) + { + codac::TubeVector x_(x.t0_tf(), x.size()); + for(const auto& s : x) + if(!s.t0_tf().is_unbounded()) + x_.set(s.codomain(), s.t0_tf()); + for(const auto& s : x) // setting gate (were overwritten) + if(s.t0_tf().is_degenerated()) + x_.set(s.codomain(), s.t0_tf()); + return x_; + } +} // namespace codac \ No newline at end of file diff --git a/src/core/2/domains/tube/codac2_Tube.h b/src/core/2/domains/tube/codac2_Tube.h index 1f2d3b516..ff978b370 100644 --- a/src/core/2/domains/tube/codac2_Tube.h +++ b/src/core/2/domains/tube/codac2_Tube.h @@ -222,18 +222,6 @@ namespace codac2 assert(i >= 0 && i < size()); return TubeComponent(*this, i); } - - codac::TubeVector to_codac1() const - { - codac::TubeVector x(t0_tf(), size()); - for(const auto& s : *this) - if(!s.t0_tf().is_unbounded()) - x.set(s.codomain(), s.t0_tf()); - for(const auto& s : *this) // setting gate (were overwritten) - if(s.t0_tf().is_degenerated()) - x.set(s.codomain(), s.t0_tf()); - return x; - } friend std::ostream& operator<<(std::ostream& os, const Tube& x) { @@ -306,10 +294,13 @@ namespace codac2 const_iterator end() const { return const_iterator(*this, _tdomain->_tslices.cend()); } }; + codac::Tube to_codac1(const Tube& x); + codac::TubeVector to_codac1(const Tube& x); + #include "codac2_TubeEvaluation.h" #include "codac2_TubeComponent.h" - + } // namespace codac #endif \ No newline at end of file diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index b2762b779..fc34736f6 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -185,6 +185,7 @@ ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/tube/codac2_TDomain.h ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/tube/codac2_TSlice.cpp ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/tube/codac2_TSlice.h + ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/tube/codac2_Tube.cpp ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/tube/codac2_Tube.h ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/tube/codac2_TubeComponent.h ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/tube/codac2_TubeEvaluation.h diff --git a/tests/core/tests_codac2_tubes.cpp b/tests/core/tests_codac2_tubes.cpp index 5f4b1c3e4..e6812a109 100644 --- a/tests/core/tests_codac2_tubes.cpp +++ b/tests/core/tests_codac2_tubes.cpp @@ -303,8 +303,8 @@ TEST_CASE("Test codac2::tubes") //vibes::beginDrawing(); - codac::TubeVector x_codac1 = x.to_codac1(); // may take time - codac::Tube xi_codac1 = x[1].to_codac1(); // may take time + codac::TubeVector x_codac1 = to_codac1(x); // may take time + codac::Tube xi_codac1 = to_codac1(x)[1]; // may take time //codac::VIBesFigTube fig("Tube"); //fig.set_properties(100, 100, 600, 300); From dea93e5e8b5c371b7b024d56ad9472746c4d5507 Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Thu, 8 Sep 2022 18:30:09 +0200 Subject: [PATCH 050/256] Revert "Temporary fix due to server firewall blocking apt" This reverts commit 53b4bbf49764c2983cbbc5c2f58a935e5b889ed9. --- .github/workflows/unixmatrix.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/unixmatrix.yml b/.github/workflows/unixmatrix.yml index fb8b23804..4458f26d9 100644 --- a/.github/workflows/unixmatrix.yml +++ b/.github/workflows/unixmatrix.yml @@ -92,10 +92,7 @@ jobs: del /f /q ibex.2.8.9.20220812.nupkg if: runner.os=='Windows' - run: | - wget https://www.ensta-bretagne.fr/packages/`lsb_release --id -s | tr [:upper:] [:lower:]`/`lsb_release -cs`/libibex-dev-2.8.9.20220809-0`lsb_release -cs`0_amd64.deb --no-check-certificate -nv - sudo apt-get -q update ; sudo apt-get -y install ./libibex-dev-2.8.9.20220809-0`lsb_release -cs`0_amd64.deb || true - rm -Rf ./libibex-dev-2.8.9.20220809-0`lsb_release -cs`0_amd64.deb - #sudo sh -c 'echo "deb [trusted=yes] https://www.ensta-bretagne.fr/packages/`lsb_release --id -s | tr [:upper:] [:lower:]`/`lsb_release -cs` ./" > /etc/apt/sources.list.d/ensta-bretagne.list' + sudo sh -c 'echo "deb [trusted=yes] https://www.ensta-bretagne.fr/packages/`lsb_release --id -s | tr [:upper:] [:lower:]`/`lsb_release -cs` ./" > /etc/apt/sources.list.d/ensta-bretagne.list' sudo apt-get -q update ; sudo apt-get -y install libibex-dev libeigen3-dev dpkg-dev || true shell: bash if: runner.os=='Linux' From 5f36d8a307aa5da1b7fde48312bc3dc8d295eb2b Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Thu, 8 Sep 2022 19:18:19 +0200 Subject: [PATCH 051/256] URL change from https://www.ensta-bretagne.fr/packages/ to https://packages.ensta-bretagne.fr/ --- .github/workflows/unixmatrix.yml | 2 +- doc/doc/install/01-installation-full-linux.rst | 7 ++++++- doc/doc/install/01-installation-python.rst | 2 +- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/.github/workflows/unixmatrix.yml b/.github/workflows/unixmatrix.yml index 4458f26d9..0db85075e 100644 --- a/.github/workflows/unixmatrix.yml +++ b/.github/workflows/unixmatrix.yml @@ -92,7 +92,7 @@ jobs: del /f /q ibex.2.8.9.20220812.nupkg if: runner.os=='Windows' - run: | - sudo sh -c 'echo "deb [trusted=yes] https://www.ensta-bretagne.fr/packages/`lsb_release --id -s | tr [:upper:] [:lower:]`/`lsb_release -cs` ./" > /etc/apt/sources.list.d/ensta-bretagne.list' + sudo sh -c 'echo "deb [trusted=yes] https://packages.ensta-bretagne.fr/`lsb_release --id -s | tr [:upper:] [:lower:]`/`lsb_release -cs` ./" > /etc/apt/sources.list.d/ensta-bretagne.list' sudo apt-get -q update ; sudo apt-get -y install libibex-dev libeigen3-dev dpkg-dev || true shell: bash if: runner.os=='Linux' diff --git a/doc/doc/install/01-installation-full-linux.rst b/doc/doc/install/01-installation-full-linux.rst index 4f65ad29d..c496e91a7 100644 --- a/doc/doc/install/01-installation-full-linux.rst +++ b/doc/doc/install/01-installation-full-linux.rst @@ -14,12 +14,17 @@ A Debian package is available for the last release |version| of the library: .. code-block:: bash - sudo sh -c 'echo "deb [trusted=yes] https://www.ensta-bretagne.fr/packages/`lsb_release --id -s | tr [:upper:] [:lower:]`/`lsb_release -cs` ./" > /etc/apt/sources.list.d/ensta-bretagne.list' + sudo sh -c 'echo "deb [trusted=yes] https://packages.ensta-bretagne.fr/`lsb_release --id -s | tr [:upper:] [:lower:]`/`lsb_release -cs` ./" > /etc/apt/sources.list.d/ensta-bretagne.list' sudo apt update sudo apt install libcodac-dev Then, check your installation `with the instructions of this page <03-start-cpp-project.html>`_. +.. warning:: + + | **URL changed** + | Please update :code:`/etc/apt/sources.list.d/ensta-bretagne.list` as above. + .. note:: For a Raspberry Pi running Raspbian Buster, download and extract ``codac_standalone_armv6hf_buster.zip`` from ``_, then in the ``example`` folder run: diff --git a/doc/doc/install/01-installation-python.rst b/doc/doc/install/01-installation-python.rst index 36d83bc67..08e1442db 100644 --- a/doc/doc/install/01-installation-python.rst +++ b/doc/doc/install/01-installation-python.rst @@ -59,7 +59,7 @@ If a configuration in this table does not work, please `contact us `: + | `Python wheels provided for Catalina or earlier may need to be installed with something similar to `_: .. code-block:: bash From f2ea9de2156891ca8c5f2f9cd1d00ec310627cc4 Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Thu, 8 Sep 2022 19:36:00 +0200 Subject: [PATCH 052/256] Documentation updates --- doc/doc/install/01-installation-full-linux.rst | 2 +- doc/doc/install/01-installation-python.rst | 12 ++++++------ doc/doc/install/01-installation.rst | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/doc/doc/install/01-installation-full-linux.rst b/doc/doc/install/01-installation-full-linux.rst index c496e91a7..e030d73cd 100644 --- a/doc/doc/install/01-installation-full-linux.rst +++ b/doc/doc/install/01-installation-full-linux.rst @@ -22,7 +22,7 @@ Then, check your installation `with the instructions of this page <03-start-cpp- .. warning:: - | **URL changed** + | **URL changed**: | Please update :code:`/etc/apt/sources.list.d/ensta-bretagne.list` as above. .. note:: diff --git a/doc/doc/install/01-installation-python.rst b/doc/doc/install/01-installation-python.rst index 08e1442db..355fb0565 100644 --- a/doc/doc/install/01-installation-python.rst +++ b/doc/doc/install/01-installation-python.rst @@ -36,17 +36,17 @@ In case you want to use Codac only with Python, then the installation procedure The :gbg:`✓` configurations are officially supported at the moment: +---------------+----------------+-----------------+-----------------+----------------+----------------+----------------+ -|Language |Linux (amd64) |Windows (x64) |Windows (x86) |macOS (arm64) |macOS (x86_64) |Online | +|Language |Linux (amd64) |Windows (x64) |Windows (x86) |macOS (arm64) |macOS (x86_64) |Online | +===============+================+=================+=================+================+================+================+ -|Python 3.6 |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` ||online-py|_ | +|Python 3.6 |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` ||online-py|_ | +---------------+----------------+-----------------+-----------------+----------------+----------------+ + -|Python 3.7 |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` | | +|Python 3.7 |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` | | +---------------+----------------+-----------------+-----------------+----------------+----------------+ + -|Python 3.8 |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` | | +|Python 3.8 |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` | | +---------------+----------------+-----------------+-----------------+----------------+----------------+ + -|Python 3.9 |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` | | +|Python 3.9 |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` | | +---------------+----------------+-----------------+-----------------+----------------+----------------+ + -|Python 3.10 |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` | | +|Python 3.10 |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` | | +---------------+----------------+-----------------+-----------------+----------------+----------------+----------------+ If a configuration in this table does not work, please `contact us `_. diff --git a/doc/doc/install/01-installation.rst b/doc/doc/install/01-installation.rst index 1065838ca..8bd7db009 100644 --- a/doc/doc/install/01-installation.rst +++ b/doc/doc/install/01-installation.rst @@ -38,7 +38,7 @@ Codac is available in both C++17 and Python3. Note that you can also :ref:`use C The :gbg:`✓` configurations are officially supported at the moment: +---------------+----------------+-----------------+-----------------+----------------+----------------+----------------+ -|Language |Linux (amd64) |Windows (x64) |Windows (x86) |macOS (arm64) |macOS (x86_64) |Online | +|Language |Linux (amd64) |Windows (x64) |Windows (x86) |macOS (arm64) |macOS (x86_64) |Online | +===============+================+=================+=================+================+================+================+ |C++17 ||linux-cpp|_ ||win-cpp|_ ||win-cpp|_ ||macos-cpp|_ ||macos-cpp|_ | | +---------------+----------------+-----------------+-----------------+----------------+----------------+----------------+ From 859c5710000b2e837e2d7a6368fcacd22d9eb19e Mon Sep 17 00:00:00 2001 From: SimonRohou Date: Tue, 13 Sep 2022 15:54:06 +0200 Subject: [PATCH 053/256] [paving] added reset method --- src/core/paving/codac_Paving.cpp | 10 ++++++++++ src/core/paving/codac_Paving.h | 9 ++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/core/paving/codac_Paving.cpp b/src/core/paving/codac_Paving.cpp index f7fb4817c..583a1b393 100644 --- a/src/core/paving/codac_Paving.cpp +++ b/src/core/paving/codac_Paving.cpp @@ -128,6 +128,16 @@ namespace codac if(m_second_subpaving) m_second_subpaving->reset_flags(); } + void Paving::reset_paving(SetValue value) + { + m_value = value; + if(m_first_subpaving) + { + delete m_first_subpaving; m_first_subpaving = nullptr; + delete m_second_subpaving; m_second_subpaving = nullptr; + } + } + // Extract methods void Paving::get_boxes(list& l_subpavings, SetValue val, SetValue neg_val) const diff --git a/src/core/paving/codac_Paving.h b/src/core/paving/codac_Paving.h index 84704e0c3..533a076db 100644 --- a/src/core/paving/codac_Paving.h +++ b/src/core/paving/codac_Paving.h @@ -147,6 +147,13 @@ namespace codac */ void reset_flags() const; + /** + * \brief Same as building a new Paving object + * + * \param value integer of the set, `SetValue::UNKNOWN` by default + */ + void reset_paving(SetValue value = SetValue::UNKNOWN); + /// @} /// \name Extract methods /// @{ @@ -203,7 +210,7 @@ namespace codac protected: - mutable bool m_flag; //!< optional flag, can be used by search algorithms + mutable bool m_flag = false; //!< optional flag, can be used by search algorithms Paving *m_root = nullptr; //!< pointer to the root Paving *m_first_subpaving = nullptr, *m_second_subpaving = nullptr; //!< tree structure }; From a229dc852bafad02c9d4d8499754b496ce582a55 Mon Sep 17 00:00:00 2001 From: SimonRohou Date: Wed, 14 Sep 2022 09:39:24 +0200 Subject: [PATCH 054/256] [doc] added link to future Brunovsky page --- doc/doc/conf.py.in | 1 + doc/doc/install/01-installation-python.rst | 12 ++++++------ doc/doc/install/01-installation.rst | 2 +- doc/doc/toctree.rst | 1 + doc/doc/use-cases/brunovsky/index.rst | 18 ++++++++++++++++++ 5 files changed, 27 insertions(+), 7 deletions(-) create mode 100644 doc/doc/use-cases/brunovsky/index.rst diff --git a/doc/doc/conf.py.in b/doc/doc/conf.py.in index b3626b97c..450062d89 100644 --- a/doc/doc/conf.py.in +++ b/doc/doc/conf.py.in @@ -49,6 +49,7 @@ redirects = { "lie-symmetries": "use-cases/lie-symmetries/index.html", "sivia": "manual/11-separators/index.html", "installation": "install/01-installation.html", + "brunovsky": "use-cases/brunovsky/index.html", } breathe_projects = { diff --git a/doc/doc/install/01-installation-python.rst b/doc/doc/install/01-installation-python.rst index 36d83bc67..a0c976f86 100644 --- a/doc/doc/install/01-installation-python.rst +++ b/doc/doc/install/01-installation-python.rst @@ -36,17 +36,17 @@ In case you want to use Codac only with Python, then the installation procedure The :gbg:`✓` configurations are officially supported at the moment: +---------------+----------------+-----------------+-----------------+----------------+----------------+----------------+ -|Language |Linux (amd64) |Windows (x64) |Windows (x86) |macOS (arm64) |macOS (x86_64) |Online | +|Language |Linux (amd64) |Windows (x64) |Windows (x86) |macOS (arm64) |macOS (x86_64) |Online | +===============+================+=================+=================+================+================+================+ -|Python 3.6 |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` ||online-py|_ | +|Python 3.6 |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` ||online-py|_ | +---------------+----------------+-----------------+-----------------+----------------+----------------+ + -|Python 3.7 |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` | | +|Python 3.7 |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` | | +---------------+----------------+-----------------+-----------------+----------------+----------------+ + -|Python 3.8 |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` | | +|Python 3.8 |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` | | +---------------+----------------+-----------------+-----------------+----------------+----------------+ + -|Python 3.9 |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` | | +|Python 3.9 |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` | | +---------------+----------------+-----------------+-----------------+----------------+----------------+ + -|Python 3.10 |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` | | +|Python 3.10 |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` | | +---------------+----------------+-----------------+-----------------+----------------+----------------+----------------+ If a configuration in this table does not work, please `contact us `_. diff --git a/doc/doc/install/01-installation.rst b/doc/doc/install/01-installation.rst index 1065838ca..8bd7db009 100644 --- a/doc/doc/install/01-installation.rst +++ b/doc/doc/install/01-installation.rst @@ -38,7 +38,7 @@ Codac is available in both C++17 and Python3. Note that you can also :ref:`use C The :gbg:`✓` configurations are officially supported at the moment: +---------------+----------------+-----------------+-----------------+----------------+----------------+----------------+ -|Language |Linux (amd64) |Windows (x64) |Windows (x86) |macOS (arm64) |macOS (x86_64) |Online | +|Language |Linux (amd64) |Windows (x64) |Windows (x86) |macOS (arm64) |macOS (x86_64) |Online | +===============+================+=================+=================+================+================+================+ |C++17 ||linux-cpp|_ ||win-cpp|_ ||win-cpp|_ ||macos-cpp|_ ||macos-cpp|_ | | +---------------+----------------+-----------------+-----------------+----------------+----------------+----------------+ diff --git a/doc/doc/toctree.rst b/doc/doc/toctree.rst index 771b77de8..0cbc450cc 100644 --- a/doc/doc/toctree.rst +++ b/doc/doc/toctree.rst @@ -41,6 +41,7 @@ Codac: constraint-programming for robotics :titlesonly: Lie symmetries for guaranteed integ. + Pose estimation with range-only obs. .. /use-cases/set-inversion/index .. /use-cases/loops/index diff --git a/doc/doc/use-cases/brunovsky/index.rst b/doc/doc/use-cases/brunovsky/index.rst new file mode 100644 index 000000000..167b6a64d --- /dev/null +++ b/doc/doc/use-cases/brunovsky/index.rst @@ -0,0 +1,18 @@ +.. _sec-usecases-brunovsky: + +.. warning:: + + This page is related to a paper that is not published yet. + More content will appear soon. + +############################################ +Pose estimation with range-only observations +############################################ + +In the paper *Brunovsky decomposition for dynamic interval localization*, a new set-membership method has been proposed for estimating the trajectories of dynamical systems, when the states are completely unknown and only non-linear observations are available. + +The first part of the proposed method is symbolic and follows the decomposition of Brunovsky, *i.e.*, it decomposes the set of differential equations describing the dynamical system into two blocks of constraints: one block gathers non-linear analytical equations that do not involve differential operators, while the other block is composed of linear chains of integrators. The second part of the method, that relies on the symbolic decomposition, is numerical and based on a contractor approach. It involves a specific optimal operator for narrowing the sets of feasible solutions. + +This approach is shown to be efficient on a difficult problem of dynamical localization of a mobile robot, without any prior knowledge about its states. + +*More content coming soon.* \ No newline at end of file From 4c7bf0512a80ccbecc630e5d8b722a13a5a88158 Mon Sep 17 00:00:00 2001 From: SimonRohou Date: Wed, 14 Sep 2022 17:40:26 +0200 Subject: [PATCH 055/256] [linobs] added CtcLinobs in Codac2, with ConvexPolygon tubes --- .../2/contractors/codac2_CtcDiffInclusion.cpp | 2 +- src/core/2/contractors/codac2_CtcLinobs.cpp | 309 ++++++++++++++++++ src/core/2/contractors/codac2_CtcLinobs.h | 76 +++++ .../2/domains/tube/codac2_AbstractConstTube.h | 4 +- src/core/2/domains/tube/codac2_Slice.h | 76 ++++- src/core/2/domains/tube/codac2_TDomain.cpp | 13 + src/core/2/domains/tube/codac2_TDomain.h | 1 + src/core/2/domains/tube/codac2_Tube.cpp | 10 + src/core/2/domains/tube/codac2_Tube.h | 102 ++++-- src/core/CMakeLists.txt | 2 + 10 files changed, 566 insertions(+), 29 deletions(-) create mode 100644 src/core/2/contractors/codac2_CtcLinobs.cpp create mode 100644 src/core/2/contractors/codac2_CtcLinobs.h diff --git a/src/core/2/contractors/codac2_CtcDiffInclusion.cpp b/src/core/2/contractors/codac2_CtcDiffInclusion.cpp index a08127a91..04e189c69 100644 --- a/src/core/2/contractors/codac2_CtcDiffInclusion.cpp +++ b/src/core/2/contractors/codac2_CtcDiffInclusion.cpp @@ -36,7 +36,7 @@ namespace codac2 for(auto& sx : x) // sx is a SliceVector of the TubeVector x { - if(sx.is_gate()) // the slace may be on a degenerated temporal domain, i.e. a gate + if(sx.is_gate()) // the slice may be on a degenerated temporal domain, i.e. a gate continue; // su is a SliceVector of the TubeVector u: diff --git a/src/core/2/contractors/codac2_CtcLinobs.cpp b/src/core/2/contractors/codac2_CtcLinobs.cpp new file mode 100644 index 000000000..60c44353f --- /dev/null +++ b/src/core/2/contractors/codac2_CtcLinobs.cpp @@ -0,0 +1,309 @@ +/** + * CtcLinobs class + * ---------------------------------------------------------------------------- + * \date 2020 + * \author Simon Rohou + * \copyright Copyright 2021 Codac Team + * \license This program is distributed under the terms of + * the GNU Lesser General Public License (LGPL). + */ + +#include "codac2_CtcLinobs.h" +#include "codac_Domain.h" +#include "codac_polygon_arithmetic.h" +#include "codac_DomainsTypeException.h" +#include // for computing e^At + +using namespace std; +using namespace ibex; +using namespace codac; + +namespace codac2 +{ + IntervalMatrix exp_At(const Matrix& A, const Interval& t) // computes e^At + { + assert(A.nb_rows() == 2 && A.nb_cols() == 2); + IntervalMatrix A_exp(2, 2, Interval::EMPTY_SET); + vector v_t({t.lb(), t.ub()}); + + for(const auto& t : v_t) + { + Eigen::MatrixXd eigen_A(2,2); + eigen_A << A[0][0], A[0][1], + A[1][0], A[1][1]; + eigen_A = eigen_A * t; + Eigen::MatrixXd eigen_A_exp = eigen_A.exp(); + + A_exp[0][0] |= eigen_A_exp(0,0); A_exp[0][1] |= eigen_A_exp(0,1); + A_exp[1][0] |= eigen_A_exp(1,0); A_exp[1][1] |= eigen_A_exp(1,1); + } + + return A_exp; + } + + CtcLinobs::CtcLinobs(const Matrix& A, const Vector& b) + : DynCtc(), m_A(new Matrix(A)), m_b(new Vector(b)) + { + assert(A.nb_cols() == 2 && A.nb_rows() == 2); + assert(b.size() == 2); + } + + CtcLinobs::~CtcLinobs() + { + delete m_A; + delete m_b; + } + + // Static members for contractor signature (mainly used for CN Exceptions) + const string CtcLinobs::m_ctc_name = "CtcLinobs"; + vector CtcLinobs::m_str_expected_doms( + { + "TubeVector, Tube" + }); + + void CtcLinobs::contract(vector& v_domains) + { + /*if(v_domains.size() != 2 + || v_domains[0]->type() != Domain::Type::T_TUBE_VECTOR + || v_domains[1]->type() != Domain::Type::T_TUBE) + throw DomainsTypeException(m_ctc_name, v_domains, m_str_expected_doms); + + contract(v_domains[0]->tube_vector(), v_domains[1]->tube());*/ + } + + void CtcLinobs::contract(Tube& x, const Tube& u, TimePropag t_propa, bool compute_envelopes) + { + assert(x.tdomain() == u.tdomain()); + + if(t_propa & TimePropag::FORWARD) + for(auto it = x.begin(); it != x.end(); ++it) + { + if((*it).is_gate()) continue; + const shared_ptr> su = static_pointer_cast>((*it).tslice().slices().at(&u)); + contract(*it, *su, TimePropag::FORWARD, compute_envelopes && !(t_propa & TimePropag::BACKWARD)); + // Envelopes are contracted in the bwd iteration if selected + } + + if(t_propa & TimePropag::BACKWARD) + for(auto it = x.rbegin(); it != x.rend(); ++it) + { + if((*it).is_gate()) continue; + const shared_ptr> su = static_pointer_cast>((*it).tslice().slices().at(&u)); + contract(*it, *su, TimePropag::BACKWARD, compute_envelopes); + } + } + + /*void CtcLinobs::contract(Tube& x1, Tube& x2, const Tube& u, TimePropag t_propa) + { + vector v_t; + vector v_y; + vector v_p_k; + contract(v_t, v_y, x1, x2, u, v_p_k, t_propa); + } + + void CtcLinobs::contract(TubeVector& x, const Tube& u, TimePropag t_propa) + { + vector v_p_k; + contract(x, u, v_p_k, t_propa); + } + + void CtcLinobs::contract(TubeVector& x, const Tube& u, vector& v_p_k, TimePropag t_propa) + { + vector v_t; + vector v_y; + contract(v_t, v_y, x, u, v_p_k, t_propa); + } + + void CtcLinobs::contract(double& t, IntervalVector& y, TubeVector& x, const Tube& u, TimePropag t_propa) + { + vector v_p_k; + contract(t, y, x, u, v_p_k, t_propa); + } + + void CtcLinobs::contract(double& t, IntervalVector& y, TubeVector& x, const Tube& u, vector& v_p_k, TimePropag t_propa) + { + vector v_t(1, t); + vector v_y(1, y); + + contract(v_t, v_y, x, u, v_p_k, t_propa); + + y &= v_y[0]; + } + + void CtcLinobs::contract(vector& v_t, vector& v_y, TubeVector& x, const Tube& u, TimePropag t_propa) + { + vector v_p_k; + contract(v_t, v_y, x, u, v_p_k, t_propa); + } + + void CtcLinobs::contract(vector& v_t, vector& v_y, TubeVector& x, const Tube& u, vector& v_p_k, TimePropag t_propa) + { + assert(x.size() == 2 && "operation not supported for other dimensions"); + contract(v_t, v_y, x[0], x[1], u, v_p_k, t_propa); + } +*/ + + void CtcLinobs::contract(Slice& x, const Slice& u, TimePropag t_propa, bool compute_envelope) + { + if(x.is_gate()) + return; + + if((t_propa & TimePropag::FORWARD) && x.next_slice_ptr()) + { + ConvexPolygon output_gate = x.output_gate(); + ctc_fwd_gate(output_gate, x.input_gate(), x.t0_tf().diam(), *m_A, *m_b, u.codomain()); + x.next_slice_ptr()->set(output_gate); + } + + if((t_propa & TimePropag::BACKWARD) && x.prev_slice_ptr()) + { + ConvexPolygon input_gate = x.input_gate(); + ctc_bwd_gate(input_gate, x.output_gate(), x.t0_tf().diam(), *m_A, *m_b, u.codomain()); + x.prev_slice_ptr()->set(input_gate); + } + + if(compute_envelope) + x.set(polygon_envelope(x.input_gate(), x.t0_tf().diam(), *m_A, *m_b, u.codomain())); + } + + /*void CtcLinobs::contract(vector& v_t, vector& v_y, Tube& x1, Tube& x2, const Tube& u, vector& v_p_k, TimePropag t_propa) + { + assert(Tube::same_slicing(x1, u)); + assert(Tube::same_slicing(x2, u)); + assert(v_t.size() == v_y.size()); + #ifdef DEBUG + for(const auto& t : v_t) + assert(x1.tdomain().contains(t)); + for(const auto& y : v_y) + assert(y.size() == 2); + #endif + + int k = x1.nb_slices(); + + // Unbounded polygons are not supported yet, so we limit their size + // (and so the envelope of the tube) + + IntervalVector box_domain(2, Interval(-9999.,9999.)); + x1 &= box_domain[0]; + x2 &= box_domain[1]; + + // Creating a vector of (k+1) polygons + + v_p_k.clear(); + v_p_k = vector(k+1); + + int i = 0; + const Slice *su; + Slice *s1 = x1.first_slice(), *s2 = x2.first_slice(); + + v_p_k[i] = ConvexPolygon({s1->input_gate(), s2->input_gate()}); + while(s1) + { + i++; + v_p_k[i] = ConvexPolygon({s1->output_gate(), s2->output_gate()}); + s1 = s1->next_slice(); s2 = s2->next_slice(); + } + assert(i == k); + + // Forward contractions + + if(t_propa & TimePropag::FORWARD) + { + i = 1; + s1 = x1.first_slice(); + s2 = x2.first_slice(); + su = u.first_slice(); + + while(s1) + { + const Interval tkm1_tk = s1->tdomain(); // [t_{k-1},t_k] + + if(tkm1_tk.intersects(m_restricted_tdomain)) + { + ctc_fwd_gate(v_p_k[i], v_p_k[i-1], tkm1_tk.diam(), *m_A, *m_b, su->codomain()); + + for(size_t j = 0 ; j < v_t.size() ; j++) // observations at uncertain times + if(tkm1_tk.contains(v_t[j])) + ctc_fwd_gate(v_p_k[i], ConvexPolygon(v_y[j]), tkm1_tk.ub()-v_t[j], *m_A, *m_b, su->codomain()); + // todo: contraction of the observations + + IntervalVector ouputgate_box = v_p_k[i].box(); + s1->set_output_gate(ouputgate_box[0]); + s2->set_output_gate(ouputgate_box[1]); + + if(t_propa & TimePropag::BACKWARD) + { + // The slice envelope can be computed only from the gates, + // and so it will be computed during the backward process. + } + + else + { + IntervalVector envelope_box = polygon_envelope(v_p_k[i-1], tkm1_tk.diam(), *m_A, *m_b, su->codomain()).box(); + s1->set_envelope(envelope_box[0]); + s2->set_envelope(envelope_box[1]); + } + } + + s1 = s1->next_slice(); s2 = s2->next_slice(); su = su->next_slice(); + i++; + } + } + + // Backward contractions + + if(t_propa & TimePropag::BACKWARD) + { + i = k-1; + s1 = x1.last_slice(); + s2 = x2.last_slice(); + su = u.last_slice(); + + while(s1) + { + const Interval tk_kp1 = s1->tdomain(); // [t_k,t_{k+1}] + + if(tk_kp1.intersects(m_restricted_tdomain)) + { + ctc_bwd_gate(v_p_k[i], v_p_k[i+1], tk_kp1.diam(), *m_A, *m_b, su->codomain()); + + for(size_t j = 0 ; j < v_t.size() ; j++) // observations at uncertain times + if(tk_kp1.contains(v_t[j])) + ctc_bwd_gate(v_p_k[i], ConvexPolygon(v_y[j]), v_t[j]-tk_kp1.lb(), *m_A, *m_b, su->codomain()); + // todo: contraction of the observations + + IntervalVector polybox = v_p_k[i].box(); + s1->set_input_gate(polybox[0]); + s2->set_input_gate(polybox[1]); + + IntervalVector envelope_box = polygon_envelope(v_p_k[i], tk_kp1.diam(), *m_A, *m_b, su->codomain()).box(); + s1->set_envelope(envelope_box[0]); + s2->set_envelope(envelope_box[1]); + } + + s1 = s1->prev_slice(); s2 = s2->prev_slice(); su = su->prev_slice(); + i--; + } + } + }*/ + + void CtcLinobs::ctc_fwd_gate(ConvexPolygon& p_k, const ConvexPolygon& p_km1, + double dt_km1_k, const Matrix& A, const Vector& b, const Interval& u_km1) + { + p_k = p_k & (exp_At(A,dt_km1_k)*p_km1 + dt_km1_k*exp_At(A,Interval(0.,dt_km1_k))*(u_km1*b)); + p_k.simplify(m_polygon_max_edges); + } + + void CtcLinobs::ctc_bwd_gate(ConvexPolygon& p_k, const ConvexPolygon& p_kp1, + double dt_k_kp1, const Matrix& A, const Vector& b, const Interval& u_k) + { + p_k = p_k & (exp_At(-A,dt_k_kp1)*p_kp1 - dt_k_kp1*exp_At(-A,Interval(0.,dt_k_kp1))*(u_k*b)); + p_k.simplify(m_polygon_max_edges); + } + + ConvexPolygon CtcLinobs::polygon_envelope(const ConvexPolygon& p_k, + double dt_k_kp1, const Matrix& A, const Vector& b, const Interval& u_k) + { + return exp_At(A,Interval(0.,dt_k_kp1))*p_k + Interval(0.,dt_k_kp1)*exp_At(A,Interval(0.,dt_k_kp1))*(u_k*b); + } +} \ No newline at end of file diff --git a/src/core/2/contractors/codac2_CtcLinobs.h b/src/core/2/contractors/codac2_CtcLinobs.h new file mode 100644 index 000000000..a47a43071 --- /dev/null +++ b/src/core/2/contractors/codac2_CtcLinobs.h @@ -0,0 +1,76 @@ +/** + * \file + * + * ---------------------------------------------------------------------------- + * \date 2022 + * \author Simon Rohou + * \copyright Copyright 2022 Codac Team + * \license This program is distributed under the terms of + * the GNU Lesser General Public License (LGPL). + */ + +#ifndef __CODAC2_CTCLINOBS_H__ +#define __CODAC2_CTCLINOBS_H__ + +#include +#include +#include +#include "codac_DynCtc.h" +#include "codac2_Tube.h" +#include "codac_ConvexPolygon.h" +#include "codac_IntervalMatrix.h" + +namespace codac2 +{ + /** + * \class CtcLinobs + */ + class CtcLinobs : public codac::DynCtc + { + public: + + /** + * \brief Creates a contractor object \f$\mathcal{C}_\textrm{linobs}\f$ + */ + CtcLinobs(const codac::Matrix& A, const codac::Vector& b); // /!\ auto evaluation of e^At not reliable + ~CtcLinobs(); + + void contract(std::vector& v_domains); + //void contract(Tube& x1, Tube& x2, const Tube& u, TimePropag t_propa = TimePropag::FORWARD | TimePropag::BACKWARD); + void contract(Tube& x, const Tube& u, codac::TimePropag t_propa = codac::TimePropag::FORWARD | codac::TimePropag::BACKWARD, bool compute_envelopes = true); + void contract(Slice& x, const Slice& u, codac::TimePropag t_propa = codac::TimePropag::FORWARD | codac::TimePropag::BACKWARD, bool compute_envelope = true); + + /*void contract(TubeVector& x, const Tube& u, std::vector& v_p_k, TimePropag t_propa = TimePropag::FORWARD | TimePropag::BACKWARD); + + // todo: contraction of the observations + void contract(double& t, IntervalVector& y, TubeVector& x, const Tube& u, TimePropag t_propa = TimePropag::FORWARD | TimePropag::BACKWARD); + void contract(double& t, IntervalVector& y, TubeVector& x, const Tube& u, std::vector& v_p_k, TimePropag t_propa = TimePropag::FORWARD | TimePropag::BACKWARD); + + // todo: contraction of the observations + void contract(std::vector& v_t, std::vector& v_y, TubeVector& x, const Tube& u, TimePropag t_propa = TimePropag::FORWARD | TimePropag::BACKWARD); + void contract(std::vector& v_t, std::vector& v_y, TubeVector& x, const Tube& u, std::vector& v_p_k, TimePropag t_propa = TimePropag::FORWARD | TimePropag::BACKWARD); + void contract(std::vector& v_t, std::vector& v_y, Tube& x1, Tube& x2, const Tube& u, std::vector& v_p_k, TimePropag t_propa = TimePropag::FORWARD | TimePropag::BACKWARD); +*/ + codac::ConvexPolygon polygon_envelope(const codac::ConvexPolygon& p_k, double dt_k_kp1, const codac::Matrix& A, const codac::Vector& b, const codac::Interval& u_k); + + + protected: + + void ctc_fwd_gate(codac::ConvexPolygon& p_k, const codac::ConvexPolygon& p_km1, double dt_km1_k, const codac::Matrix& A, const codac::Vector& b, const codac::Interval& u_km1); + void ctc_bwd_gate(codac::ConvexPolygon& p_k, const codac::ConvexPolygon& p_kp1, double dt_k_kp1, const codac::Matrix& A, const codac::Vector& b, const codac::Interval& u_k); + + + protected: + + const codac::Matrix* m_A; + const codac::Vector* m_b; + + const int m_polygon_max_edges = 15; + + static const std::string m_ctc_name; //!< class name (mainly used for CN Exceptions) + static std::vector m_str_expected_doms; //!< allowed domains signatures (mainly used for CN Exceptions) + friend class ContractorNetwork; + }; +} + +#endif \ No newline at end of file diff --git a/src/core/2/domains/tube/codac2_AbstractConstTube.h b/src/core/2/domains/tube/codac2_AbstractConstTube.h index e35352c59..03eec5867 100644 --- a/src/core/2/domains/tube/codac2_AbstractConstTube.h +++ b/src/core/2/domains/tube/codac2_AbstractConstTube.h @@ -18,6 +18,7 @@ #include "codac_Interval.h" #include "codac_IntervalVector.h" #include "codac_TrajectoryVector.h" +#include "codac_BoolInterval.h" namespace codac2 { @@ -25,6 +26,7 @@ namespace codac2 using codac::TrajectoryVector; using codac::Interval; using codac::IntervalVector; + using codac::BoolInterval; template class AbstractConstTube @@ -44,7 +46,7 @@ namespace codac2 } virtual size_t size() const = 0; - virtual bool contains(const TrajectoryVector& value) const = 0; + virtual BoolInterval contains(const TrajectoryVector& value) const = 0; virtual Interval t0_tf() const = 0; virtual W codomain() const = 0; // virtual W operator()(double t) const = 0; diff --git a/src/core/2/domains/tube/codac2_Slice.h b/src/core/2/domains/tube/codac2_Slice.h index f1da9ac27..79ca9d2ae 100644 --- a/src/core/2/domains/tube/codac2_Slice.h +++ b/src/core/2/domains/tube/codac2_Slice.h @@ -20,12 +20,16 @@ #include "codac_TrajectoryVector.h" #include "codac_Exception.h" #include "codac2_AbstractSlice.h" +#include "codac_BoolInterval.h" + +#define EPSILON_CONTAINS ibex::next_float(0.) * 1000. //!< epsilon limit of the contains() algorithm namespace codac2 { using codac::Interval; using codac::IntervalVector; using codac::TrajectoryVector; + using codac::BoolInterval; class AbstractSlicedTube; class TSlice; @@ -98,9 +102,63 @@ namespace codac2 return codomain().is_unbounded(); } - bool contains(const TrajectoryVector& value) const + BoolInterval contains(const TrajectoryVector& x) const { - return true; // todo + assert(t0_tf().is_subset(x.tdomain())); + + /*IntervalVector traj_tdomain = x(t0_tf()); + // Using x(Interval(double)) for reliable evaluation: + IntervalVector traj_input = x(Interval(t0_tf().lb())); + IntervalVector traj_output = x(Interval(t0_tf().ub())); + + if(_codomain.intersects(traj_tdomain) == BoolInterval::NO + || input_gate().intersects(traj_input) == BoolInterval::NO + || output_gate().intersects(traj_output) == BoolInterval::NO) + return BoolInterval::NO; + + else + { + if(!input_gate().is_superset(traj_input) || !output_gate().is_superset(traj_output)) + return BoolInterval::MAYBE; + + else if(_codomain.is_superset(traj_tdomain)) + return BoolInterval::YES; + + else // too much pessimism for the trajectory evaluation on t0_tf() + { + // Bisections are performed to reach an accurate evaluation + + std::list s_subtdomains; + s_subtdomains.push_front(t0_tf()); + + while(!s_subtdomains.empty()) + { + Interval t = s_subtdomains.front(); + s_subtdomains.pop_front(); + + IntervalVector thinner_eval = x(t); + + if(!_codomain.intersects(thinner_eval)) + { + return BoolInterval::NO; + } + + else if(!_codomain.is_superset(thinner_eval)) + { + if(t.diam() < EPSILON_CONTAINS) + return BoolInterval::MAYBE; + + s_subtdomains.push_front(Interval(t.lb(), t.lb() + t.diam() / 2.)); + s_subtdomains.push_front(Interval(t.lb() + t.diam() / 2., t.ub())); + } + } + + return BoolInterval::YES; + } + }*/ + + // todo + return BoolInterval::YES; } const std::shared_ptr> prev_slice_ptr() const @@ -133,7 +191,7 @@ namespace codac2 T input_gate() const { T gate = codomain(); - if(prev_slice_ptr()) + if(!is_gate() && prev_slice_ptr()) gate &= prev_slice_ptr()->codomain(); return gate; } @@ -141,14 +199,15 @@ namespace codac2 T output_gate() const { T gate = codomain(); - if(next_slice_ptr()) + if(!is_gate() && next_slice_ptr()) gate &= next_slice_ptr()->codomain(); return gate; } void set(const T& x) { - assert((size_t)codomain().size() == size()); + if constexpr(!std::is_same::value) + assert((size_t)codomain().size() == size()); _codomain = x; if(is_gate()) _codomain &= prev_slice_ptr()->codomain() & next_slice_ptr()->codomain(); @@ -162,6 +221,13 @@ namespace codac2 _codomain[i] &= prev_slice_ptr()->codomain()[i] & next_slice_ptr()->codomain()[i]; } + const Slice& inflate(double rad) + { + assert(rad >= 0. && "cannot inflate negative value"); + _codomain.inflate(rad); + return *this; + } + friend std::ostream& operator<<(std::ostream& os, const Slice& x) { os << x.t0_tf() diff --git a/src/core/2/domains/tube/codac2_TDomain.cpp b/src/core/2/domains/tube/codac2_TDomain.cpp index eca35fcfb..ca6459566 100644 --- a/src/core/2/domains/tube/codac2_TDomain.cpp +++ b/src/core/2/domains/tube/codac2_TDomain.cpp @@ -111,6 +111,19 @@ namespace codac2 return _tslices; } + void TDomain::delete_gates() + { + list::iterator it = _tslices.begin(); + while(it != _tslices.end()) + { + if(it->t0_tf().is_degenerated()) + _tslices.erase(it++); + + else + ++it; + } + } + ostream& operator<<(ostream& os, const TDomain& x) { os << x.t0_tf() diff --git a/src/core/2/domains/tube/codac2_TDomain.h b/src/core/2/domains/tube/codac2_TDomain.h index fee3cba3c..29db26688 100644 --- a/src/core/2/domains/tube/codac2_TDomain.h +++ b/src/core/2/domains/tube/codac2_TDomain.h @@ -38,6 +38,7 @@ namespace codac2 std::list::iterator sample(double t, bool allow_gate = true); friend std::ostream& operator<<(std::ostream& os, const TDomain& x); const std::list& tslices() const; + void delete_gates(); protected: diff --git a/src/core/2/domains/tube/codac2_Tube.cpp b/src/core/2/domains/tube/codac2_Tube.cpp index 13e51eb48..320b4d135 100644 --- a/src/core/2/domains/tube/codac2_Tube.cpp +++ b/src/core/2/domains/tube/codac2_Tube.cpp @@ -39,4 +39,14 @@ namespace codac2 x_.set(s.codomain(), s.t0_tf()); return x_; } + + template <> + Tube::Tube(const std::shared_ptr& tdomain, const TFnc& f) : + Tube(tdomain, IntervalVector(f.image_dim())) + { + assert(f.nb_var() == 0 && "function's inputs must be limited to system variable"); + + for(auto& s : *this) + s.set(f.eval_vector(s.t0_tf())); + } } // namespace codac \ No newline at end of file diff --git a/src/core/2/domains/tube/codac2_Tube.h b/src/core/2/domains/tube/codac2_Tube.h index ff978b370..9b1e27234 100644 --- a/src/core/2/domains/tube/codac2_Tube.h +++ b/src/core/2/domains/tube/codac2_Tube.h @@ -17,13 +17,16 @@ #include "codac_TFnc.h" #include "codac2_TSlice.h" #include "codac_Tube.h" // to be removed +#include "codac_TubeVector.h" // to be removed #include "codac2_AbstractSlicedTube.h" #include "codac2_AbstractConstTube.h" #include "codac2_TDomain.h" +#include "codac_ConvexPolygon.h" namespace codac2 { using codac::TFnc; + using codac::BoolInterval; template class Slice; @@ -38,26 +41,27 @@ namespace codac2 { public: - explicit Tube(const std::shared_ptr& tdomain, size_t n = 1) : - Tube(tdomain, T(n)) + explicit Tube(const std::shared_ptr& tdomain) : + Tube(tdomain, T()) { - assert(n > 0); - for(std::list::iterator it = _tdomain->_tslices.begin(); - it != _tdomain->_tslices.end(); ++it) - { - it->_slices.insert( - std::pair>>(this, - std::make_shared>(n, *this, it))); - } + } explicit Tube(const std::shared_ptr& tdomain, const TFnc& f) : - Tube(tdomain, (size_t)f.image_dim()) + Tube(tdomain, T()) { assert(f.nb_var() == 0 && "function's inputs must be limited to system variable"); + if constexpr(std::is_same::value) + assert(f.image_dim() == 1); for(auto& s : *this) - s.set(f.eval_vector(s.t0_tf())); + { + if constexpr(std::is_same::value) + s.set(f.eval(s.t0_tf())); + + else + s.set(f.eval_vector(s.t0_tf())); + } } explicit Tube(const std::shared_ptr& tdomain, const T& default_value) : @@ -167,9 +171,19 @@ namespace codac2 return false; } - bool contains(const TrajectoryVector& value) const + BoolInterval contains(const TrajectoryVector& x) const { - return true; + assert(x.tdomain() == tdomain()->t0_tf()); + + BoolInterval result = BoolInterval::YES; + for(const auto& s : *this) + { + BoolInterval b = s.contains(x); + if(b == BoolInterval::NO) return BoolInterval::NO; + else if(b == BoolInterval::MAYBE) result = BoolInterval::MAYBE; + } + + return result; } T codomain() const @@ -217,6 +231,24 @@ namespace codac2 s.set(codomain); } + void set(const T& codomain, double t) + { + assert((size_t)codomain.size() == size()); + std::list::iterator it = _tdomain->sample(t); + std::static_pointer_cast>(it->_slices.at(this))->set(codomain); + } + + const Tube& inflate(double rad) + { + for(auto& s : *this) + if(!s.is_gate()) + s.inflate(rad); + for(auto& s : *this) + if(s.is_gate()) + s.inflate(rad); + return *this; + } + TubeComponent operator[](size_t i) { assert(i >= 0 && i < size()); @@ -249,22 +281,48 @@ namespace codac2 public: - iterator(const Tube& tube_vector, base_container::iterator it) : - base_container::iterator(it), _tube_vector(tube_vector) { } + iterator(const Tube& x, base_container::iterator it) : + base_container::iterator(it), _x(x) { } reference operator*() { - return static_cast(*((*this)->_slices.at(&_tube_vector))); + return static_cast(*((*this)->_slices.at(&_x))); } protected: - const Tube& _tube_vector; + const Tube& _x; }; iterator begin() { return iterator(*this, _tdomain->_tslices.begin()); } iterator end() { return iterator(*this, _tdomain->_tslices.end()); } + struct reverse_iterator : public base_container::reverse_iterator + { + using iterator_category = typename base_container::reverse_iterator::iterator_category; + using difference_type = typename base_container::reverse_iterator::difference_type; + + using value_type = Slice; + using pointer = Slice*; + using reference = Slice&; + + public: + + reverse_iterator(const Tube& x, base_container::reverse_iterator it) : + base_container::reverse_iterator(it), _x(x) { } + + reference operator*() + { + return static_cast(*((*this)->_slices.at(&_x))); + } + + protected: + + const Tube& _x; + }; + + reverse_iterator rbegin() { return reverse_iterator(*this, _tdomain->_tslices.rbegin()); } + reverse_iterator rend() { return reverse_iterator(*this, _tdomain->_tslices.rend()); } struct const_iterator : public base_container::const_iterator { @@ -277,17 +335,17 @@ namespace codac2 public: - const_iterator(const Tube& tube_vector, base_container::const_iterator it) : - base_container::const_iterator(it), _tube_vector(tube_vector) { } + const_iterator(const Tube& x, base_container::const_iterator it) : + base_container::const_iterator(it), _x(x) { } reference operator*() const { - return static_cast(*((*this)->_slices.at(&_tube_vector))); + return static_cast(*((*this)->_slices.at(&_x))); } protected: - const Tube& _tube_vector; + const Tube& _x; }; const_iterator begin() const { return const_iterator(*this, _tdomain->_tslices.cbegin()); } diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index fc34736f6..49317ffef 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -191,6 +191,8 @@ ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/tube/codac2_TubeEvaluation.h ${CMAKE_CURRENT_SOURCE_DIR}/2/contractors/codac2_CtcDiffInclusion.cpp ${CMAKE_CURRENT_SOURCE_DIR}/2/contractors/codac2_CtcDiffInclusion.h + ${CMAKE_CURRENT_SOURCE_DIR}/2/contractors/codac2_CtcLinobs.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/2/contractors/codac2_CtcLinobs.h ${CMAKE_CURRENT_SOURCE_DIR}/2/integration/codac2_AbstractDomain.cpp ${CMAKE_CURRENT_SOURCE_DIR}/2/integration/codac2_AbstractDomain.h ) From 60f45472f123c8cea62a7f11a9dbeb42d39c3ca0 Mon Sep 17 00:00:00 2001 From: SimonRohou Date: Wed, 14 Sep 2022 17:40:55 +0200 Subject: [PATCH 056/256] [geom] updated Polygon classes --- .../arithmetic/codac_polygon_arithmetic.cpp | 26 +++++++- .../arithmetic/codac_polygon_arithmetic.h | 4 ++ src/core/geometry/codac_ConvexPolygon.cpp | 61 ++++++++++++++++--- src/core/geometry/codac_ConvexPolygon.h | 10 ++- src/core/geometry/codac_Polygon.cpp | 24 ++++++++ src/core/geometry/codac_Polygon.h | 3 + 6 files changed, 118 insertions(+), 10 deletions(-) diff --git a/src/core/arithmetic/codac_polygon_arithmetic.cpp b/src/core/arithmetic/codac_polygon_arithmetic.cpp index b3ae36920..4ccc7a04f 100644 --- a/src/core/arithmetic/codac_polygon_arithmetic.cpp +++ b/src/core/arithmetic/codac_polygon_arithmetic.cpp @@ -75,7 +75,7 @@ namespace codac for(const auto& pt_ : p1.vertices()) { ThickPoint pt(pt_); - if(p2.encloses(pt) != NO) + if(p2.contains(pt) != NO) v_pts.push_back(pt); } @@ -83,7 +83,7 @@ namespace codac for(const auto& pt_ : p2.vertices()) { ThickPoint pt(pt_); - if(p1.encloses(pt) != NO) + if(p1.contains(pt) != NO) v_pts.push_back(pt); } @@ -130,4 +130,26 @@ namespace codac assert(p2.size() == 2 && "other dimensions not supported"); return operator&(p1, ConvexPolygon(p2)); } + + const ConvexPolygon operator|(const ConvexPolygon& p1, const ConvexPolygon& p2) + { + vector v_pts; + for(const auto& pt_ : p1.vertices()) + v_pts.push_back(ThickPoint(pt_)); + for(const auto& pt_ : p2.vertices()) + v_pts.push_back(ThickPoint(pt_)); + return ConvexPolygon(v_pts); + } + + const ConvexPolygon operator|(const IntervalVector& p1, const ConvexPolygon& p2) + { + assert(p1.size() == 2 && "other dimensions not supported"); + return operator|(p2, ConvexPolygon(p1)); + } + + const ConvexPolygon operator|(const ConvexPolygon& p1, const IntervalVector& p2) + { + assert(p2.size() == 2 && "other dimensions not supported"); + return operator|(p1, ConvexPolygon(p2)); + } } \ No newline at end of file diff --git a/src/core/arithmetic/codac_polygon_arithmetic.h b/src/core/arithmetic/codac_polygon_arithmetic.h index 09c2325b1..251bd323c 100644 --- a/src/core/arithmetic/codac_polygon_arithmetic.h +++ b/src/core/arithmetic/codac_polygon_arithmetic.h @@ -31,6 +31,10 @@ namespace codac const ConvexPolygon operator&(const ConvexPolygon& p1, const ConvexPolygon& p2); const ConvexPolygon operator&(const IntervalVector& p1, const ConvexPolygon& p2); const ConvexPolygon operator&(const ConvexPolygon& p1, const IntervalVector& p2); + + const ConvexPolygon operator|(const ConvexPolygon& p1, const ConvexPolygon& p2); + const ConvexPolygon operator|(const IntervalVector& p1, const ConvexPolygon& p2); + const ConvexPolygon operator|(const ConvexPolygon& p1, const IntervalVector& p2); } #endif \ No newline at end of file diff --git a/src/core/geometry/codac_ConvexPolygon.cpp b/src/core/geometry/codac_ConvexPolygon.cpp index 7a14bb254..c99b25532 100644 --- a/src/core/geometry/codac_ConvexPolygon.cpp +++ b/src/core/geometry/codac_ConvexPolygon.cpp @@ -10,6 +10,7 @@ #include #include +#include "codac_polygon_arithmetic.h" #include "codac_IntervalMatrix.h" #include "codac_ConvexPolygon.h" #include "codac_GrahamScan.h" @@ -35,12 +36,8 @@ namespace codac } ConvexPolygon::ConvexPolygon(const IntervalVector& box) - : Polygon() + : Polygon(box) { - assert(box.size() == 2); - assert(!box.is_empty()); - - ThickPoint::push(box, m_v_floating_pts); m_v_floating_pts = GrahamScan::convex_hull(m_v_floating_pts); } @@ -100,7 +97,7 @@ namespace codac for(const auto& pt : vertices()) { - is_subset = is_subset && p.encloses(ThickPoint(pt)); + is_subset = is_subset && p.contains(ThickPoint(pt)); if(is_subset == NO) return NO; } @@ -108,7 +105,30 @@ namespace codac return is_subset; } + const BoolInterval ConvexPolygon::is_subset(const IntervalVector& x) const + { + // todo: this could be optimized in the specific case of a box + return is_subset(ConvexPolygon(x)); + } + + const BoolInterval ConvexPolygon::is_superset(const ConvexPolygon& p) const + { + return p.is_subset(*this); + } + + const BoolInterval ConvexPolygon::is_superset(const IntervalVector& x) const + { + // todo: this could be optimized in the specific case of a box + return is_superset(ConvexPolygon(x)); + } + const BoolInterval ConvexPolygon::encloses(const ThickPoint& p) const + { + cout << "encloses(): deprecated. Use contains() instead." << endl; + return contains(p); + } + + const BoolInterval ConvexPolygon::contains(const ThickPoint& p) const { if(p.does_not_exist() || is_empty()) return NO; @@ -155,12 +175,25 @@ namespace codac return (a & 1) ? YES : NO; } + const BoolInterval ConvexPolygon::contains(const Vector& p) const + { + assert(p.size() == 2); + return contains(ThickPoint(p)); + } + + BoolInterval ConvexPolygon::intersects(const IntervalVector& x) const + { + // todo: improve this algorithm + return fast_intersection(x).is_empty() ? NO : YES; + } + // Setting values const ConvexPolygon& ConvexPolygon::inflate(double rad) { // todo + assert(false); return *this; } @@ -287,9 +320,23 @@ namespace codac ThickPoint::push(reduced_x, v_x_vertices); for(const auto& vertex : v_x_vertices) - if(encloses(vertex) != NO) + if(contains(vertex) != NO) inter |= vertex.box(); return inter; } + + const ConvexPolygon& ConvexPolygon::operator&=(const ConvexPolygon& x) + { + // todo: optimize this? + *this = *this & x; + return *this; + } + + const ConvexPolygon& ConvexPolygon::operator|=(const ConvexPolygon& x) + { + // todo: optimize this? + *this = *this | x; + return *this; + } } \ No newline at end of file diff --git a/src/core/geometry/codac_ConvexPolygon.h b/src/core/geometry/codac_ConvexPolygon.h index cfdf8a01e..e81a62ee7 100644 --- a/src/core/geometry/codac_ConvexPolygon.h +++ b/src/core/geometry/codac_ConvexPolygon.h @@ -35,7 +35,13 @@ namespace codac /// @{ const BoolInterval is_subset(const ConvexPolygon& p) const; - const BoolInterval encloses(const ThickPoint& p) const; + const BoolInterval is_subset(const IntervalVector& x) const; + const BoolInterval is_superset(const ConvexPolygon& p) const; + const BoolInterval is_superset(const IntervalVector& x) const; + const BoolInterval encloses(const ThickPoint& p) const; // deprecated + const BoolInterval contains(const ThickPoint& p) const; + const BoolInterval contains(const Vector& p) const; + BoolInterval intersects(const IntervalVector& x) const; /// @} /// \name Setting values @@ -50,6 +56,8 @@ namespace codac /// @{ const IntervalVector fast_intersection(const IntervalVector& x) const; + const ConvexPolygon& operator&=(const ConvexPolygon& x); + const ConvexPolygon& operator|=(const ConvexPolygon& x); /// @} }; diff --git a/src/core/geometry/codac_Polygon.cpp b/src/core/geometry/codac_Polygon.cpp index 09d07d4a7..a1cb1689f 100644 --- a/src/core/geometry/codac_Polygon.cpp +++ b/src/core/geometry/codac_Polygon.cpp @@ -23,6 +23,7 @@ namespace codac // Definition Polygon::Polygon() + : Polygon(IntervalVector(2,Interval(-9999999999.,9999999999.))) // unbounded polygons are not supported yet { } @@ -32,6 +33,14 @@ namespace codac { } + + Polygon::Polygon(const IntervalVector& box) + { + assert(box.size() == 2); + assert(!box.is_empty()); + + ThickPoint::push(box, m_v_floating_pts); + } Polygon::Polygon(const vector& v_floating_pts) : m_v_floating_pts(v_floating_pts) @@ -39,6 +48,11 @@ namespace codac } + void Polygon::set_empty() + { + m_v_floating_pts.clear(); + } + // Accessing values @@ -81,12 +95,19 @@ namespace codac { IntervalVector box(2, Interval::EMPTY_SET); for(const auto& pt : m_v_floating_pts) + { + if(isinf(pt[0]) || isinf(pt[1])) + return IntervalVector(2); // todo: correct this, issue #87 box |= pt; + } return box; } const ThickPoint Polygon::center() const { + if(is_empty()) + return ThickPoint(); // undefined point + IntervalVector center(2, 0.); for(const auto& pt : m_v_floating_pts) center += pt; @@ -96,6 +117,9 @@ namespace codac const Interval Polygon::area() const { + if(is_empty()) + return 0.; + Interval a(0.); size_t n = m_v_floating_pts.size(); diff --git a/src/core/geometry/codac_Polygon.h b/src/core/geometry/codac_Polygon.h index a7f540198..fb7e4db7e 100644 --- a/src/core/geometry/codac_Polygon.h +++ b/src/core/geometry/codac_Polygon.h @@ -28,8 +28,11 @@ namespace codac Polygon(); Polygon(const Polygon& p); + explicit Polygon(const IntervalVector& box); Polygon(const std::vector& v_floating_pts); + void set_empty(); + /// @} /// \name Accessing values /// @{ From 1efb07d65702d83f2e4091e0cfce959a0110e5de Mon Sep 17 00:00:00 2001 From: SimonRohou Date: Wed, 14 Sep 2022 17:41:37 +0200 Subject: [PATCH 057/256] [graphics] polygon updates + new color map --- src/core/graphics/codac_ColorMap.cpp | 10 ++++++++ src/core/graphics/codac_ColorMap.h | 1 + src/core/graphics/codac_VIBesFig.cpp | 11 +++++++++ src/core/graphics/codac_VIBesFig.h | 10 +++++++- tests/core/CMakeLists.txt | 2 +- tests/core/tests_codac2_tubes.cpp | 34 ++++++++++++++++++++++------ 6 files changed, 59 insertions(+), 9 deletions(-) diff --git a/src/core/graphics/codac_ColorMap.cpp b/src/core/graphics/codac_ColorMap.cpp index 43e1317d4..f7554131b 100644 --- a/src/core/graphics/codac_ColorMap.cpp +++ b/src/core/graphics/codac_ColorMap.cpp @@ -216,6 +216,16 @@ namespace codac const ColorMap ColorMap::BLUE_TUBE = make_blue_tube(); + ColorMap make_red_tube() + { + ColorMap map(InterpolMode::RGB); + map.add_color_point(make_rgb(169,55,0), 0.); + map.add_color_point(make_rgb(241,140,54), 1.); + return map; + } + + const ColorMap ColorMap::RED_TUBE = make_red_tube(); + ColorMap make_rainbow() { ColorMap map(InterpolMode::HSV); diff --git a/src/core/graphics/codac_ColorMap.h b/src/core/graphics/codac_ColorMap.h index 73fe741b8..c6199bf48 100644 --- a/src/core/graphics/codac_ColorMap.h +++ b/src/core/graphics/codac_ColorMap.h @@ -121,6 +121,7 @@ namespace codac static const ColorMap HAXBY; //!< predefined HAXBY color map (mainly used for DEM) static const ColorMap DEFAULT; //!< a predefined default color map static const ColorMap BLUE_TUBE; //!< a predefined color map for tubes + static const ColorMap RED_TUBE; //!< a predefined color map for tubes static const ColorMap RAINBOW; //!< a predefined color map diff --git a/src/core/graphics/codac_VIBesFig.cpp b/src/core/graphics/codac_VIBesFig.cpp index 7afecb2a5..be802eefb 100644 --- a/src/core/graphics/codac_VIBesFig.cpp +++ b/src/core/graphics/codac_VIBesFig.cpp @@ -9,6 +9,7 @@ */ #include "codac_VIBesFig.h" +#include "codac2_Tube.h" using namespace std; using namespace ibex; @@ -278,6 +279,16 @@ namespace codac for(int i = v_p.size()-1 ; i >= 0 ; i--) // we usually prefer to display last poylgons first, that may be larger draw_polygon(v_p[i], rgb2hex(color_map.color(i*1./(v_p.size()-1)))); } + + void VIBesFig::draw_polygon_tube(const codac2::Tube& x, const ColorMap& color_map, const vibes::Params& params) + { + int i = -1; + for(const auto& s : x) + { + if(!s.codomain().box().is_unbounded()) + draw_polygon(s.codomain(), rgb2hex(color_map.color((++i)*1./(x.nb_slices()-1)))); + } + } void VIBesFig::draw_point(const ThickPoint& p, float size, const vibes::Params& params) { diff --git a/src/core/graphics/codac_VIBesFig.h b/src/core/graphics/codac_VIBesFig.h index 1d2073c1a..82697a76c 100644 --- a/src/core/graphics/codac_VIBesFig.h +++ b/src/core/graphics/codac_VIBesFig.h @@ -18,8 +18,15 @@ #include "codac_Polygon.h" #include "codac_ConvexPolygon.h" #include "codac_ColorMap.h" +#include "codac_ConvexPolygon.h" #include "vibes.h" +namespace codac2 +{ + template + class Tube; +} + namespace codac { /** @@ -323,7 +330,8 @@ namespace codac * \param params VIBes parameters related to the polygons (none by default) */ void draw_polygons(const std::vector& v_p, const ColorMap& color_map, const vibes::Params& params = vibes::Params()); - + void draw_polygon_tube(const codac2::Tube& x, const ColorMap& color_map, const vibes::Params& params = vibes::Params()); + /** * \brief Draws a point * diff --git a/tests/core/CMakeLists.txt b/tests/core/CMakeLists.txt index 9bf1484bf..bc3a2da73 100644 --- a/tests/core/CMakeLists.txt +++ b/tests/core/CMakeLists.txt @@ -26,7 +26,7 @@ list(APPEND SRC_TESTS ${CMAKE_CURRENT_SOURCE_DIR}/main.cpp #${CMAKE_CURRENT_SOURCE_DIR}/tests_integration.cpp #${CMAKE_CURRENT_SOURCE_DIR}/tests_operators.cpp #${CMAKE_CURRENT_SOURCE_DIR}/tests_geometry.cpp - #${CMAKE_CURRENT_SOURCE_DIR}/tests_polygons.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/tests_polygons.cpp #${CMAKE_CURRENT_SOURCE_DIR}/tests_serialization.cpp #${CMAKE_CURRENT_SOURCE_DIR}/tests_slices_structure.cpp #${CMAKE_CURRENT_SOURCE_DIR}/tests_trajectory.cpp diff --git a/tests/core/tests_codac2_tubes.cpp b/tests/core/tests_codac2_tubes.cpp index e6812a109..10459aa97 100644 --- a/tests/core/tests_codac2_tubes.cpp +++ b/tests/core/tests_codac2_tubes.cpp @@ -49,11 +49,11 @@ TEST_CASE("Test codac2::tubes") CHECK(tdomain->iterator_tslice(5540.2)->t0_tf() == Interval(1,oo)); CHECK(tdomain->nb_tubes() == 0); - Tube x(tdomain, 2); // adding a tubevector to the tdomain + Tube x(tdomain, IntervalVector(2)); // adding a tubevector to the tdomain CHECK(tdomain->nb_tubes() == 1); { // new scope - Tube v(tdomain, 3); + Tube v(tdomain, IntervalVector(3)); CHECK(tdomain->nb_tubes() == 2); } // end of scope, auto removing the tube @@ -145,7 +145,7 @@ TEST_CASE("Test codac2::tubes") { auto tdomain = create_tdomain(); CHECK(tdomain->nb_tslices() == 1); - Tube x(tdomain, 1); + Tube x(tdomain, IntervalVector(1)); x.set(Interval(4)); x(Interval(0,1)) = IntervalVector({{1,2}}); x(Interval(1,2)) = IntervalVector({{5,6}}); @@ -186,7 +186,7 @@ TEST_CASE("Test codac2::tubes") SECTION("Test basic Tube") { auto tdomain = create_tdomain(Interval(0,1), 0.1, false); - Tube x(tdomain, 3); + Tube x(tdomain, IntervalVector(3)); CHECK(x.size() == 3); CHECK(x.tdomain() == tdomain); @@ -257,7 +257,7 @@ TEST_CASE("Test codac2::tubes") SECTION("Test SliceVector") { auto tdomain = create_tdomain(Interval(0,1), 0.1); - Tube x(tdomain, 2); + Tube x(tdomain, IntervalVector(2)); CHECK(x.nb_slices() == 12); CHECK(x.first_slice_ptr() == tdomain->iterator_tslice(-oo)->_slices.at(&x)); CHECK(x.last_slice_ptr() == tdomain->iterator_tslice(oo)->_slices.at(&x)); @@ -292,7 +292,7 @@ TEST_CASE("Test codac2::tubes") auto tdomain = create_tdomain(Interval(0,10), 0.01, true); // last argument creates "gates" (degenerated slices at scalar timesteps) Tube x(tdomain, codac::TFunction("(sin(sqrt(t)+((t-5)^2)*[-0.01,0.01]) ; cos(t)+sin(t/0.2)*[-0.1,0.1])")); - Tube u(tdomain, 2); + Tube u(tdomain, IntervalVector(2)); CHECK(x.size() == 2); CHECK(u.size() == 2); @@ -318,7 +318,7 @@ TEST_CASE("Test codac2::tubes") { /*{ auto tdomain = create_tdomain(Interval(0,10), 1., false); - Tube x(2, tdomain); + Tube x(tdomain, IntervalVector(2)); for(auto& sx : x) { @@ -362,4 +362,24 @@ TEST_CASE("Test codac2::tubes") CHECK(x.size() == 3); CHECK(x.codomain()[1] == Interval(-1.5,1)); } + + SECTION("Reverse iterator") + { + auto tdomain = create_tdomain(Interval(0,1), 0.5); + Tube x(tdomain); + + auto it1 = x.begin(); + CHECK(it1->t0_tf() == Interval(-oo,0)); ++it1; + CHECK(it1->t0_tf() == Interval(0,0.5)); ++it1; + CHECK(it1->t0_tf() == Interval(0.5,1)); ++it1; + CHECK(it1->t0_tf() == Interval(1,oo)); ++it1; + CHECK(it1 == x.end()); + + auto it2 = x.rbegin(); + CHECK(it2->t0_tf() == Interval(1,oo)); ++it2; + CHECK(it2->t0_tf() == Interval(0.5,1)); ++it2; + CHECK(it2->t0_tf() == Interval(0,0.5)); ++it2; + CHECK(it2->t0_tf() == Interval(-oo,0)); ++it2; + CHECK(it2 == x.rend()); + } } \ No newline at end of file From 60c8ccce81b94859ef1b1ab8e837c9c3c6717ff4 Mon Sep 17 00:00:00 2001 From: SimonRohou Date: Thu, 15 Sep 2022 10:40:52 +0200 Subject: [PATCH 058/256] [linobs] removing matrix pointers --- src/core/2/contractors/codac2_CtcLinobs.cpp | 35 ++++++++++----------- src/core/2/contractors/codac2_CtcLinobs.h | 12 +++---- 2 files changed, 23 insertions(+), 24 deletions(-) diff --git a/src/core/2/contractors/codac2_CtcLinobs.cpp b/src/core/2/contractors/codac2_CtcLinobs.cpp index 60c44353f..780b6eb51 100644 --- a/src/core/2/contractors/codac2_CtcLinobs.cpp +++ b/src/core/2/contractors/codac2_CtcLinobs.cpp @@ -42,7 +42,7 @@ namespace codac2 } CtcLinobs::CtcLinobs(const Matrix& A, const Vector& b) - : DynCtc(), m_A(new Matrix(A)), m_b(new Vector(b)) + : DynCtc(), _A(A), _b(b) { assert(A.nb_cols() == 2 && A.nb_rows() == 2); assert(b.size() == 2); @@ -50,8 +50,7 @@ namespace codac2 CtcLinobs::~CtcLinobs() { - delete m_A; - delete m_b; + } // Static members for contractor signature (mainly used for CN Exceptions) @@ -151,19 +150,19 @@ namespace codac2 if((t_propa & TimePropag::FORWARD) && x.next_slice_ptr()) { ConvexPolygon output_gate = x.output_gate(); - ctc_fwd_gate(output_gate, x.input_gate(), x.t0_tf().diam(), *m_A, *m_b, u.codomain()); + ctc_fwd_gate(output_gate, x.input_gate(), x.t0_tf().diam(), _A, _b, u.codomain()); x.next_slice_ptr()->set(output_gate); } if((t_propa & TimePropag::BACKWARD) && x.prev_slice_ptr()) { ConvexPolygon input_gate = x.input_gate(); - ctc_bwd_gate(input_gate, x.output_gate(), x.t0_tf().diam(), *m_A, *m_b, u.codomain()); + ctc_bwd_gate(input_gate, x.output_gate(), x.t0_tf().diam(), _A, _b, u.codomain()); x.prev_slice_ptr()->set(input_gate); } if(compute_envelope) - x.set(polygon_envelope(x.input_gate(), x.t0_tf().diam(), *m_A, *m_b, u.codomain())); + x.set(polygon_envelope(x.input_gate(), x.t0_tf().diam(), _A, _b, u.codomain())); } /*void CtcLinobs::contract(vector& v_t, vector& v_y, Tube& x1, Tube& x2, const Tube& u, vector& v_p_k, TimePropag t_propa) @@ -220,11 +219,11 @@ namespace codac2 if(tkm1_tk.intersects(m_restricted_tdomain)) { - ctc_fwd_gate(v_p_k[i], v_p_k[i-1], tkm1_tk.diam(), *m_A, *m_b, su->codomain()); + ctc_fwd_gate(v_p_k[i], v_p_k[i-1], tkm1_tk.diam(), *_A, *_b, su->codomain()); for(size_t j = 0 ; j < v_t.size() ; j++) // observations at uncertain times if(tkm1_tk.contains(v_t[j])) - ctc_fwd_gate(v_p_k[i], ConvexPolygon(v_y[j]), tkm1_tk.ub()-v_t[j], *m_A, *m_b, su->codomain()); + ctc_fwd_gate(v_p_k[i], ConvexPolygon(v_y[j]), tkm1_tk.ub()-v_t[j], *_A, *_b, su->codomain()); // todo: contraction of the observations IntervalVector ouputgate_box = v_p_k[i].box(); @@ -239,7 +238,7 @@ namespace codac2 else { - IntervalVector envelope_box = polygon_envelope(v_p_k[i-1], tkm1_tk.diam(), *m_A, *m_b, su->codomain()).box(); + IntervalVector envelope_box = polygon_envelope(v_p_k[i-1], tkm1_tk.diam(), *_A, *_b, su->codomain()).box(); s1->set_envelope(envelope_box[0]); s2->set_envelope(envelope_box[1]); } @@ -265,18 +264,18 @@ namespace codac2 if(tk_kp1.intersects(m_restricted_tdomain)) { - ctc_bwd_gate(v_p_k[i], v_p_k[i+1], tk_kp1.diam(), *m_A, *m_b, su->codomain()); + ctc_bwd_gate(v_p_k[i], v_p_k[i+1], tk_kp1.diam(), *_A, *_b, su->codomain()); for(size_t j = 0 ; j < v_t.size() ; j++) // observations at uncertain times if(tk_kp1.contains(v_t[j])) - ctc_bwd_gate(v_p_k[i], ConvexPolygon(v_y[j]), v_t[j]-tk_kp1.lb(), *m_A, *m_b, su->codomain()); + ctc_bwd_gate(v_p_k[i], ConvexPolygon(v_y[j]), v_t[j]-tk_kp1.lb(), *_A, *_b, su->codomain()); // todo: contraction of the observations IntervalVector polybox = v_p_k[i].box(); s1->set_input_gate(polybox[0]); s2->set_input_gate(polybox[1]); - IntervalVector envelope_box = polygon_envelope(v_p_k[i], tk_kp1.diam(), *m_A, *m_b, su->codomain()).box(); + IntervalVector envelope_box = polygon_envelope(v_p_k[i], tk_kp1.diam(), *_A, *_b, su->codomain()).box(); s1->set_envelope(envelope_box[0]); s2->set_envelope(envelope_box[1]); } @@ -288,22 +287,22 @@ namespace codac2 }*/ void CtcLinobs::ctc_fwd_gate(ConvexPolygon& p_k, const ConvexPolygon& p_km1, - double dt_km1_k, const Matrix& A, const Vector& b, const Interval& u_km1) + double dt_km1_k, const Interval& u_km1) { - p_k = p_k & (exp_At(A,dt_km1_k)*p_km1 + dt_km1_k*exp_At(A,Interval(0.,dt_km1_k))*(u_km1*b)); + p_k = p_k & (exp_At(_A,dt_km1_k)*p_km1 + dt_km1_k*exp_At(_A,Interval(0.,dt_km1_k))*(u_km1*_b)); p_k.simplify(m_polygon_max_edges); } void CtcLinobs::ctc_bwd_gate(ConvexPolygon& p_k, const ConvexPolygon& p_kp1, - double dt_k_kp1, const Matrix& A, const Vector& b, const Interval& u_k) + double dt_k_kp1, const Interval& u_k) { - p_k = p_k & (exp_At(-A,dt_k_kp1)*p_kp1 - dt_k_kp1*exp_At(-A,Interval(0.,dt_k_kp1))*(u_k*b)); + p_k = p_k & (exp_At(-_A,dt_k_kp1)*p_kp1 - dt_k_kp1*exp_At(-_A,Interval(0.,dt_k_kp1))*(u_k*_b)); p_k.simplify(m_polygon_max_edges); } ConvexPolygon CtcLinobs::polygon_envelope(const ConvexPolygon& p_k, - double dt_k_kp1, const Matrix& A, const Vector& b, const Interval& u_k) + double dt_k_kp1, const Interval& u_k) { - return exp_At(A,Interval(0.,dt_k_kp1))*p_k + Interval(0.,dt_k_kp1)*exp_At(A,Interval(0.,dt_k_kp1))*(u_k*b); + return exp_At(_A,Interval(0.,dt_k_kp1))*p_k + Interval(0.,dt_k_kp1)*exp_At(_A,Interval(0.,dt_k_kp1))*(u_k*_b); } } \ No newline at end of file diff --git a/src/core/2/contractors/codac2_CtcLinobs.h b/src/core/2/contractors/codac2_CtcLinobs.h index a47a43071..428919350 100644 --- a/src/core/2/contractors/codac2_CtcLinobs.h +++ b/src/core/2/contractors/codac2_CtcLinobs.h @@ -51,19 +51,19 @@ namespace codac2 void contract(std::vector& v_t, std::vector& v_y, TubeVector& x, const Tube& u, std::vector& v_p_k, TimePropag t_propa = TimePropag::FORWARD | TimePropag::BACKWARD); void contract(std::vector& v_t, std::vector& v_y, Tube& x1, Tube& x2, const Tube& u, std::vector& v_p_k, TimePropag t_propa = TimePropag::FORWARD | TimePropag::BACKWARD); */ - codac::ConvexPolygon polygon_envelope(const codac::ConvexPolygon& p_k, double dt_k_kp1, const codac::Matrix& A, const codac::Vector& b, const codac::Interval& u_k); - + protected: - void ctc_fwd_gate(codac::ConvexPolygon& p_k, const codac::ConvexPolygon& p_km1, double dt_km1_k, const codac::Matrix& A, const codac::Vector& b, const codac::Interval& u_km1); - void ctc_bwd_gate(codac::ConvexPolygon& p_k, const codac::ConvexPolygon& p_kp1, double dt_k_kp1, const codac::Matrix& A, const codac::Vector& b, const codac::Interval& u_k); + void ctc_fwd_gate(codac::ConvexPolygon& p_k, const codac::ConvexPolygon& p_km1, double dt_km1_k, const codac::Interval& u_km1); + void ctc_bwd_gate(codac::ConvexPolygon& p_k, const codac::ConvexPolygon& p_kp1, double dt_k_kp1, const codac::Interval& u_k); + codac::ConvexPolygon polygon_envelope(const codac::ConvexPolygon& p_k, double dt_k_kp1, const codac::Interval& u_k); protected: - const codac::Matrix* m_A; - const codac::Vector* m_b; + const codac::Matrix& _A; + const codac::Vector& _b; const int m_polygon_max_edges = 15; From 3041f2f72cc1aaef1e4acbe8e5c7a2cd05f0530e Mon Sep 17 00:00:00 2001 From: SimonRohou Date: Thu, 15 Sep 2022 10:46:19 +0200 Subject: [PATCH 059/256] [linobs] removing matrix pointers (again) --- src/core/2/contractors/codac2_CtcLinobs.cpp | 179 +------------------- src/core/2/contractors/codac2_CtcLinobs.h | 16 -- 2 files changed, 4 insertions(+), 191 deletions(-) diff --git a/src/core/2/contractors/codac2_CtcLinobs.cpp b/src/core/2/contractors/codac2_CtcLinobs.cpp index 780b6eb51..fe23d5e23 100644 --- a/src/core/2/contractors/codac2_CtcLinobs.cpp +++ b/src/core/2/contractors/codac2_CtcLinobs.cpp @@ -92,56 +92,6 @@ namespace codac2 } } - /*void CtcLinobs::contract(Tube& x1, Tube& x2, const Tube& u, TimePropag t_propa) - { - vector v_t; - vector v_y; - vector v_p_k; - contract(v_t, v_y, x1, x2, u, v_p_k, t_propa); - } - - void CtcLinobs::contract(TubeVector& x, const Tube& u, TimePropag t_propa) - { - vector v_p_k; - contract(x, u, v_p_k, t_propa); - } - - void CtcLinobs::contract(TubeVector& x, const Tube& u, vector& v_p_k, TimePropag t_propa) - { - vector v_t; - vector v_y; - contract(v_t, v_y, x, u, v_p_k, t_propa); - } - - void CtcLinobs::contract(double& t, IntervalVector& y, TubeVector& x, const Tube& u, TimePropag t_propa) - { - vector v_p_k; - contract(t, y, x, u, v_p_k, t_propa); - } - - void CtcLinobs::contract(double& t, IntervalVector& y, TubeVector& x, const Tube& u, vector& v_p_k, TimePropag t_propa) - { - vector v_t(1, t); - vector v_y(1, y); - - contract(v_t, v_y, x, u, v_p_k, t_propa); - - y &= v_y[0]; - } - - void CtcLinobs::contract(vector& v_t, vector& v_y, TubeVector& x, const Tube& u, TimePropag t_propa) - { - vector v_p_k; - contract(v_t, v_y, x, u, v_p_k, t_propa); - } - - void CtcLinobs::contract(vector& v_t, vector& v_y, TubeVector& x, const Tube& u, vector& v_p_k, TimePropag t_propa) - { - assert(x.size() == 2 && "operation not supported for other dimensions"); - contract(v_t, v_y, x[0], x[1], u, v_p_k, t_propa); - } -*/ - void CtcLinobs::contract(Slice& x, const Slice& u, TimePropag t_propa, bool compute_envelope) { if(x.is_gate()) @@ -150,142 +100,21 @@ namespace codac2 if((t_propa & TimePropag::FORWARD) && x.next_slice_ptr()) { ConvexPolygon output_gate = x.output_gate(); - ctc_fwd_gate(output_gate, x.input_gate(), x.t0_tf().diam(), _A, _b, u.codomain()); + ctc_fwd_gate(output_gate, x.input_gate(), x.t0_tf().diam(), u.codomain()); x.next_slice_ptr()->set(output_gate); } if((t_propa & TimePropag::BACKWARD) && x.prev_slice_ptr()) { ConvexPolygon input_gate = x.input_gate(); - ctc_bwd_gate(input_gate, x.output_gate(), x.t0_tf().diam(), _A, _b, u.codomain()); + ctc_bwd_gate(input_gate, x.output_gate(), x.t0_tf().diam(), u.codomain()); x.prev_slice_ptr()->set(input_gate); } if(compute_envelope) - x.set(polygon_envelope(x.input_gate(), x.t0_tf().diam(), _A, _b, u.codomain())); + x.set(polygon_envelope(x.input_gate(), x.t0_tf().diam(), u.codomain())); } - - /*void CtcLinobs::contract(vector& v_t, vector& v_y, Tube& x1, Tube& x2, const Tube& u, vector& v_p_k, TimePropag t_propa) - { - assert(Tube::same_slicing(x1, u)); - assert(Tube::same_slicing(x2, u)); - assert(v_t.size() == v_y.size()); - #ifdef DEBUG - for(const auto& t : v_t) - assert(x1.tdomain().contains(t)); - for(const auto& y : v_y) - assert(y.size() == 2); - #endif - - int k = x1.nb_slices(); - - // Unbounded polygons are not supported yet, so we limit their size - // (and so the envelope of the tube) - - IntervalVector box_domain(2, Interval(-9999.,9999.)); - x1 &= box_domain[0]; - x2 &= box_domain[1]; - - // Creating a vector of (k+1) polygons - - v_p_k.clear(); - v_p_k = vector(k+1); - - int i = 0; - const Slice *su; - Slice *s1 = x1.first_slice(), *s2 = x2.first_slice(); - - v_p_k[i] = ConvexPolygon({s1->input_gate(), s2->input_gate()}); - while(s1) - { - i++; - v_p_k[i] = ConvexPolygon({s1->output_gate(), s2->output_gate()}); - s1 = s1->next_slice(); s2 = s2->next_slice(); - } - assert(i == k); - - // Forward contractions - - if(t_propa & TimePropag::FORWARD) - { - i = 1; - s1 = x1.first_slice(); - s2 = x2.first_slice(); - su = u.first_slice(); - - while(s1) - { - const Interval tkm1_tk = s1->tdomain(); // [t_{k-1},t_k] - - if(tkm1_tk.intersects(m_restricted_tdomain)) - { - ctc_fwd_gate(v_p_k[i], v_p_k[i-1], tkm1_tk.diam(), *_A, *_b, su->codomain()); - - for(size_t j = 0 ; j < v_t.size() ; j++) // observations at uncertain times - if(tkm1_tk.contains(v_t[j])) - ctc_fwd_gate(v_p_k[i], ConvexPolygon(v_y[j]), tkm1_tk.ub()-v_t[j], *_A, *_b, su->codomain()); - // todo: contraction of the observations - - IntervalVector ouputgate_box = v_p_k[i].box(); - s1->set_output_gate(ouputgate_box[0]); - s2->set_output_gate(ouputgate_box[1]); - - if(t_propa & TimePropag::BACKWARD) - { - // The slice envelope can be computed only from the gates, - // and so it will be computed during the backward process. - } - - else - { - IntervalVector envelope_box = polygon_envelope(v_p_k[i-1], tkm1_tk.diam(), *_A, *_b, su->codomain()).box(); - s1->set_envelope(envelope_box[0]); - s2->set_envelope(envelope_box[1]); - } - } - - s1 = s1->next_slice(); s2 = s2->next_slice(); su = su->next_slice(); - i++; - } - } - - // Backward contractions - - if(t_propa & TimePropag::BACKWARD) - { - i = k-1; - s1 = x1.last_slice(); - s2 = x2.last_slice(); - su = u.last_slice(); - - while(s1) - { - const Interval tk_kp1 = s1->tdomain(); // [t_k,t_{k+1}] - - if(tk_kp1.intersects(m_restricted_tdomain)) - { - ctc_bwd_gate(v_p_k[i], v_p_k[i+1], tk_kp1.diam(), *_A, *_b, su->codomain()); - - for(size_t j = 0 ; j < v_t.size() ; j++) // observations at uncertain times - if(tk_kp1.contains(v_t[j])) - ctc_bwd_gate(v_p_k[i], ConvexPolygon(v_y[j]), v_t[j]-tk_kp1.lb(), *_A, *_b, su->codomain()); - // todo: contraction of the observations - - IntervalVector polybox = v_p_k[i].box(); - s1->set_input_gate(polybox[0]); - s2->set_input_gate(polybox[1]); - - IntervalVector envelope_box = polygon_envelope(v_p_k[i], tk_kp1.diam(), *_A, *_b, su->codomain()).box(); - s1->set_envelope(envelope_box[0]); - s2->set_envelope(envelope_box[1]); - } - - s1 = s1->prev_slice(); s2 = s2->prev_slice(); su = su->prev_slice(); - i--; - } - } - }*/ - + void CtcLinobs::ctc_fwd_gate(ConvexPolygon& p_k, const ConvexPolygon& p_km1, double dt_km1_k, const Interval& u_km1) { diff --git a/src/core/2/contractors/codac2_CtcLinobs.h b/src/core/2/contractors/codac2_CtcLinobs.h index 428919350..11827fb09 100644 --- a/src/core/2/contractors/codac2_CtcLinobs.h +++ b/src/core/2/contractors/codac2_CtcLinobs.h @@ -29,29 +29,13 @@ namespace codac2 { public: - /** - * \brief Creates a contractor object \f$\mathcal{C}_\textrm{linobs}\f$ - */ CtcLinobs(const codac::Matrix& A, const codac::Vector& b); // /!\ auto evaluation of e^At not reliable ~CtcLinobs(); void contract(std::vector& v_domains); - //void contract(Tube& x1, Tube& x2, const Tube& u, TimePropag t_propa = TimePropag::FORWARD | TimePropag::BACKWARD); void contract(Tube& x, const Tube& u, codac::TimePropag t_propa = codac::TimePropag::FORWARD | codac::TimePropag::BACKWARD, bool compute_envelopes = true); void contract(Slice& x, const Slice& u, codac::TimePropag t_propa = codac::TimePropag::FORWARD | codac::TimePropag::BACKWARD, bool compute_envelope = true); - /*void contract(TubeVector& x, const Tube& u, std::vector& v_p_k, TimePropag t_propa = TimePropag::FORWARD | TimePropag::BACKWARD); - - // todo: contraction of the observations - void contract(double& t, IntervalVector& y, TubeVector& x, const Tube& u, TimePropag t_propa = TimePropag::FORWARD | TimePropag::BACKWARD); - void contract(double& t, IntervalVector& y, TubeVector& x, const Tube& u, std::vector& v_p_k, TimePropag t_propa = TimePropag::FORWARD | TimePropag::BACKWARD); - - // todo: contraction of the observations - void contract(std::vector& v_t, std::vector& v_y, TubeVector& x, const Tube& u, TimePropag t_propa = TimePropag::FORWARD | TimePropag::BACKWARD); - void contract(std::vector& v_t, std::vector& v_y, TubeVector& x, const Tube& u, std::vector& v_p_k, TimePropag t_propa = TimePropag::FORWARD | TimePropag::BACKWARD); - void contract(std::vector& v_t, std::vector& v_y, Tube& x1, Tube& x2, const Tube& u, std::vector& v_p_k, TimePropag t_propa = TimePropag::FORWARD | TimePropag::BACKWARD); -*/ - protected: From caeaf90085c5ba6e448eacbd59973effaf36b824 Mon Sep 17 00:00:00 2001 From: SimonRohou Date: Thu, 22 Sep 2022 10:16:53 +0200 Subject: [PATCH 060/256] [polygons] updates --- doc/doc/tmp/rencontres-rob/exercice.py | 26 +++++++++++++++++++ src/core/2/contractors/codac2_CtcLinobs.cpp | 6 +++++ src/core/2/domains/tube/codac2_Slice.h | 12 +++++++++ .../arithmetic/codac_polygon_arithmetic.cpp | 2 ++ src/core/geometry/codac_ConvexPolygon.cpp | 12 +++++++++ src/core/geometry/codac_ThickEdge.cpp | 4 +++ 6 files changed, 62 insertions(+) create mode 100644 doc/doc/tmp/rencontres-rob/exercice.py diff --git a/doc/doc/tmp/rencontres-rob/exercice.py b/doc/doc/tmp/rencontres-rob/exercice.py new file mode 100644 index 000000000..66ee813f3 --- /dev/null +++ b/doc/doc/tmp/rencontres-rob/exercice.py @@ -0,0 +1,26 @@ +from codac import * +import math +import random +import time +import numpy as np + +dt = 0.02 # temporal discretization +tdomain = Interval(0,15) # [t0,tf] + +# System input +u = Trajectory(tdomain, TFunction("3*(sin(t)^2)+t/100"), dt) + +# Actual state trajectory +# Note that this trajectory is unknown of the resolution +x_truth = TrajectoryVector(3) +x_truth[2] = u.primitive() +x_truth[0] = (10*cos(x_truth[2])).primitive() +x_truth[1] = (10*sin(x_truth[2])).primitive() + +beginDrawing() +fig_map = VIBesFigMap("Top view") +fig_map.set_properties(50, 50, 800, 600) +fig_map.add_trajectory(x_truth, "x*", 0, 1, "black") +fig_map.smooth_tube_drawing(True) +fig_map.show(1.) + diff --git a/src/core/2/contractors/codac2_CtcLinobs.cpp b/src/core/2/contractors/codac2_CtcLinobs.cpp index fe23d5e23..731f44f5d 100644 --- a/src/core/2/contractors/codac2_CtcLinobs.cpp +++ b/src/core/2/contractors/codac2_CtcLinobs.cpp @@ -94,6 +94,12 @@ namespace codac2 void CtcLinobs::contract(Slice& x, const Slice& u, TimePropag t_propa, bool compute_envelope) { + if(x.is_empty() || u.is_empty()) + { + x.set_empty(); + return; + } + if(x.is_gate()) return; diff --git a/src/core/2/domains/tube/codac2_Slice.h b/src/core/2/domains/tube/codac2_Slice.h index 79ca9d2ae..bbc134ca4 100644 --- a/src/core/2/domains/tube/codac2_Slice.h +++ b/src/core/2/domains/tube/codac2_Slice.h @@ -213,6 +213,18 @@ namespace codac2 _codomain &= prev_slice_ptr()->codomain() & next_slice_ptr()->codomain(); } + void set_empty() + { + _codomain.set_empty(); + if(!is_gate()) + { + if(prev_slice_ptr()->is_gate()) + prev_slice_ptr()->set_empty(); + if(next_slice_ptr()->is_gate()) + next_slice_ptr()->set_empty(); + } + } + void set_component(size_t i, const Interval& xi) { assert((size_t)codomain().size() == size()); diff --git a/src/core/arithmetic/codac_polygon_arithmetic.cpp b/src/core/arithmetic/codac_polygon_arithmetic.cpp index 4ccc7a04f..dae4cb777 100644 --- a/src/core/arithmetic/codac_polygon_arithmetic.cpp +++ b/src/core/arithmetic/codac_polygon_arithmetic.cpp @@ -95,6 +95,8 @@ namespace codac if(!intersection_pt.does_not_exist()) { + assert(!e1.does_not_exist() && !e2.does_not_exist()); + // If edges are possibly parallel: if(ThickEdge::parallel(e1, e2) != NO) { diff --git a/src/core/geometry/codac_ConvexPolygon.cpp b/src/core/geometry/codac_ConvexPolygon.cpp index c99b25532..7b0152856 100644 --- a/src/core/geometry/codac_ConvexPolygon.cpp +++ b/src/core/geometry/codac_ConvexPolygon.cpp @@ -136,6 +136,9 @@ namespace codac if(!p.box().intersects(box())) return NO; // fast test + if(box() == IntervalVector(2)) + return YES; // fast test + // Using the ray tracing method: // A ray is defined from p to the right ; if it crosses // 'a' times one of the edges, and (a & 1), then p is inside @@ -143,7 +146,9 @@ namespace codac vector v_edges = edges(); int a = 0; // the crossing number counter size_t n = v_edges.size(); + const ThickEdge ray(p, ThickPoint(box()[0].ub()+1., p[1])); // horizontal edge to the right + assert(!v_edges[n-1].does_not_exist() && !ray.does_not_exist()); ThickPoint prev_e = v_edges[n-1] & ray; // Loop through all edges of the polygon, looking for intersections @@ -153,6 +158,7 @@ namespace codac continue; // Intersecting point + assert(!v_edges[i].does_not_exist() && !ray.does_not_exist()); const ThickPoint e = v_edges[i] & ray; if(!e.does_not_exist()) // intersection to the left of p, not considered { @@ -305,6 +311,9 @@ namespace codac if(is_empty() || x.is_empty()) return IntervalVector(2, Interval::EMPTY_SET); + if(box() == IntervalVector(2)) + return x; + IntervalVector reduced_x = x & box(); if(reduced_x.is_empty()) @@ -314,7 +323,10 @@ namespace codac vector v_edges = edges(); for(const auto& edge : v_edges) + { + assert(!edge.does_not_exist()); inter |= edge & reduced_x; + } vector v_x_vertices; ThickPoint::push(reduced_x, v_x_vertices); diff --git a/src/core/geometry/codac_ThickEdge.cpp b/src/core/geometry/codac_ThickEdge.cpp index d74029590..5d4454f74 100644 --- a/src/core/geometry/codac_ThickEdge.cpp +++ b/src/core/geometry/codac_ThickEdge.cpp @@ -159,7 +159,10 @@ namespace codac vector v_box_edges; ThickEdge::push(x, v_box_edges); for(size_t i = 0 ; i < v_box_edges.size() ; i++) + { + assert(!does_not_exist() && !v_box_edges[i].does_not_exist()); inter |= (*this & v_box_edges[i]).box(); + } return inter; } } @@ -170,6 +173,7 @@ namespace codac const ThickPoint ThickEdge::operator&(const ThickEdge& e) const { + assert(!does_not_exist() && !e.does_not_exist()); const ThickPoint proj = proj_intersection(*this, e); return ThickPoint(proj[0] & box()[0] & e.box()[0], proj[1] & box()[1] & e.box()[1]); } From 592a45da38dd2f2b5c23f1ccb300fe83e6be143b Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Sun, 11 Sep 2022 18:06:44 +0200 Subject: [PATCH 061/256] Updated .nupkg --- packages/choco/codac/codac.nuspec | 2 +- packages/choco/codac/tools/LICENSE.txt | 170 ------------------ packages/choco/codac/tools/VERIFICATION.txt | 6 - .../choco/codac/tools/chocolateyinstall.ps1 | 18 +- 4 files changed, 11 insertions(+), 185 deletions(-) delete mode 100644 packages/choco/codac/tools/LICENSE.txt delete mode 100644 packages/choco/codac/tools/VERIFICATION.txt diff --git a/packages/choco/codac/codac.nuspec b/packages/choco/codac/codac.nuspec index 27826d88b..06e556e79 100644 --- a/packages/choco/codac/codac.nuspec +++ b/packages/choco/codac/codac.nuspec @@ -39,7 +39,7 @@ choco install -y -f --ignore-dependencies codac --params "'/url:https://github.c - + diff --git a/packages/choco/codac/tools/LICENSE.txt b/packages/choco/codac/tools/LICENSE.txt deleted file mode 100644 index dc1e48ed1..000000000 --- a/packages/choco/codac/tools/LICENSE.txt +++ /dev/null @@ -1,170 +0,0 @@ - -From: https://github.com/codac-team/codac/blob/master/COPYING.LESSER - -LICENSE - -GNU LESSER GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - - This version of the GNU Lesser General Public License incorporates -the terms and conditions of version 3 of the GNU General Public -License, supplemented by the additional permissions listed below. - - 0. Additional Definitions. - - As used herein, "this License" refers to version 3 of the GNU Lesser -General Public License, and the "GNU GPL" refers to version 3 of the GNU -General Public License. - - "The Library" refers to a covered work governed by this License, -other than an Application or a Combined Work as defined below. - - An "Application" is any work that makes use of an interface provided -by the Library, but which is not otherwise based on the Library. -Defining a subclass of a class defined by the Library is deemed a mode -of using an interface provided by the Library. - - A "Combined Work" is a work produced by combining or linking an -Application with the Library. The particular version of the Library -with which the Combined Work was made is also called the "Linked -Version". - - The "Minimal Corresponding Source" for a Combined Work means the -Corresponding Source for the Combined Work, excluding any source code -for portions of the Combined Work that, considered in isolation, are -based on the Application, and not on the Linked Version. - - The "Corresponding Application Code" for a Combined Work means the -object code and/or source code for the Application, including any data -and utility programs needed for reproducing the Combined Work from the -Application, but excluding the System Libraries of the Combined Work. - - 1. Exception to Section 3 of the GNU GPL. - - You may convey a covered work under sections 3 and 4 of this License -without being bound by section 3 of the GNU GPL. - - 2. Conveying Modified Versions. - - If you modify a copy of the Library, and, in your modifications, a -facility refers to a function or data to be supplied by an Application -that uses the facility (other than as an argument passed when the -facility is invoked), then you may convey a copy of the modified -version: - - a) under this License, provided that you make a good faith effort to - ensure that, in the event an Application does not supply the - function or data, the facility still operates, and performs - whatever part of its purpose remains meaningful, or - - b) under the GNU GPL, with none of the additional permissions of - this License applicable to that copy. - - 3. Object Code Incorporating Material from Library Header Files. - - The object code form of an Application may incorporate material from -a header file that is part of the Library. You may convey such object -code under terms of your choice, provided that, if the incorporated -material is not limited to numerical parameters, data structure -layouts and accessors, or small macros, inline functions and templates -(ten or fewer lines in length), you do both of the following: - - a) Give prominent notice with each copy of the object code that the - Library is used in it and that the Library and its use are - covered by this License. - - b) Accompany the object code with a copy of the GNU GPL and this license - document. - - 4. Combined Works. - - You may convey a Combined Work under terms of your choice that, -taken together, effectively do not restrict modification of the -portions of the Library contained in the Combined Work and reverse -engineering for debugging such modifications, if you also do each of -the following: - - a) Give prominent notice with each copy of the Combined Work that - the Library is used in it and that the Library and its use are - covered by this License. - - b) Accompany the Combined Work with a copy of the GNU GPL and this license - document. - - c) For a Combined Work that displays copyright notices during - execution, include the copyright notice for the Library among - these notices, as well as a reference directing the user to the - copies of the GNU GPL and this license document. - - d) Do one of the following: - - 0) Convey the Minimal Corresponding Source under the terms of this - License, and the Corresponding Application Code in a form - suitable for, and under terms that permit, the user to - recombine or relink the Application with a modified version of - the Linked Version to produce a modified Combined Work, in the - manner specified by section 6 of the GNU GPL for conveying - Corresponding Source. - - 1) Use a suitable shared library mechanism for linking with the - Library. A suitable mechanism is one that (a) uses at run time - a copy of the Library already present on the user's computer - system, and (b) will operate properly with a modified version - of the Library that is interface-compatible with the Linked - Version. - - e) Provide Installation Information, but only if you would otherwise - be required to provide such information under section 6 of the - GNU GPL, and only to the extent that such information is - necessary to install and execute a modified version of the - Combined Work produced by recombining or relinking the - Application with a modified version of the Linked Version. (If - you use option 4d0, the Installation Information must accompany - the Minimal Corresponding Source and Corresponding Application - Code. If you use option 4d1, you must provide the Installation - Information in the manner specified by section 6 of the GNU GPL - for conveying Corresponding Source.) - - 5. Combined Libraries. - - You may place library facilities that are a work based on the -Library side by side in a single library together with other library -facilities that are not Applications and are not covered by this -License, and convey such a combined library under terms of your -choice, if you do both of the following: - - a) Accompany the combined library with a copy of the same work based - on the Library, uncombined with any other library facilities, - conveyed under the terms of this License. - - b) Give prominent notice with the combined library that part of it - is a work based on the Library, and explaining where to find the - accompanying uncombined form of the same work. - - 6. Revised Versions of the GNU Lesser General Public License. - - The Free Software Foundation may publish revised and/or new versions -of the GNU Lesser General Public License from time to time. Such new -versions will be similar in spirit to the present version, but may -differ in detail to address new problems or concerns. - - Each version is given a distinguishing version number. If the -Library as you received it specifies that a certain numbered version -of the GNU Lesser General Public License "or any later version" -applies to it, you have the option of following the terms and -conditions either of that published version or of any later version -published by the Free Software Foundation. If the Library as you -received it does not specify a version number of the GNU Lesser -General Public License, you may choose any version of the GNU Lesser -General Public License ever published by the Free Software Foundation. - - If the Library as you received it specifies that a proxy can decide -whether future versions of the GNU Lesser General Public License shall -apply, that proxy's public statement of acceptance of any version is -permanent authorization for you to choose that version for the -Library. diff --git a/packages/choco/codac/tools/VERIFICATION.txt b/packages/choco/codac/tools/VERIFICATION.txt deleted file mode 100644 index f7ec2e812..000000000 --- a/packages/choco/codac/tools/VERIFICATION.txt +++ /dev/null @@ -1,6 +0,0 @@ - -VERIFICATION -Verification is intended to assist the Chocolatey moderators and community -in verifying that this package's contents are trustworthy. - -This package is generated from https://github.com/codac-team/codac (the official Codac GitHub source repository) or https://github.com/lebarsfa/codac/master, which is a fork (the modifications made can be listed on https://github.com/codac-team/codac/compare/master...lebarsfa:master). The automated build log can be seen by clicking on "Windows MinGW 8.1.0 x64" and "Windows MinGW 8.1.0 x86" jobs on https://github.com/codac-team/codac/actions/workflows/unixmatrix.yml or https://github.com/lebarsfa/codac/actions/workflows/unixmatrix.yml, and signing in with GitHub. In particular, a sha256 checksum is visible for each generated .nupkg, as it is computed in .github/workflows/unixmatrix.yml using checksum command. Those temporary .nupkg are available on https://github.com/codac-team/codac/releases or https://github.com/lebarsfa/codac/releases and are used to create this package : it can be checked e.g. using WinMerge that x86 folder from this package contains the bin, include, lib, share folders from the "--x86" package on GitHub and all the other folders are from the non-"--x86" package. diff --git a/packages/choco/codac/tools/chocolateyinstall.ps1 b/packages/choco/codac/tools/chocolateyinstall.ps1 index 81a8a975b..02b08784c 100644 --- a/packages/choco/codac/tools/chocolateyinstall.ps1 +++ b/packages/choco/codac/tools/chocolateyinstall.ps1 @@ -3,15 +3,17 @@ # Source variables which are shared between install and uninstall. . $PSScriptRoot\sharedVars.ps1 -$pp = Get-PackageParameters +$toolsDir = "$(Split-Path -parent $MyInvocation.MyCommand.Definition)" -$root = "$env:ChocolateyPackageFolder\.." +$pp = Get-PackageParameters +$packageDir = Join-Path "$toolsDir" ".." -Resolve +$root = "$packageDir\.." if (!$pp['url']) { $url = 'https://github.com/lebarsfa/codac/releases/download/codac-1/codac_x86_mingw8.zip' - $checksum = '7855B0393DF389855E126E712C51DAB8649F19BE0101579C753FBEDF108B0132' + $checksum = 'EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE' $url64 = 'https://github.com/lebarsfa/codac/releases/download/codac-1/codac_x64_mingw8.zip' - $checksum64 = '89C6BB0DE09BD86524947827B3ED88CBBDCAB63141AEE5F7FF273CFB416C0D22' + $checksum64 = 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF' $packageArgs = @{ packageName = $env:ChocolateyPackageName unzipLocation = "$root" @@ -83,13 +85,13 @@ else if (!$pp['NoRegistry']) { New-Item "$CMakeSystemRepositoryPath\$CMakePackageName" -ItemType directory -Force - New-ItemProperty -Name "CMakePackageDir" -PropertyType String -Value "$env:ChocolateyPackageFolder\share\$CMakePackageName\cmake" -Path "$CMakeSystemRepositoryPath\$CMakePackageName" -Force + New-ItemProperty -Name "CMakePackageDir" -PropertyType String -Value "$packageDir\share\$CMakePackageName\cmake" -Path "$CMakeSystemRepositoryPath\$CMakePackageName" -Force } -#$pathtoadd = "$env:ChocolateyPackageFolder\bin" +#$pathtoadd = "$packageDir\bin" #if (!($pp['NoPath']) -and !([environment]::GetEnvironmentVariable("Path","Machine") -match [regex]::escape($pathtoadd))) { # $newpath = [environment]::GetEnvironmentVariable("Path","Machine") + ";$pathtoadd" # [environment]::SetEnvironmentVariable("Path",$newpath,"Machine") #} -#Install-BinFile -Name libcodac.a -Path "$env:ChocolateyPackageFolder\lib" -#Install-BinFile -Name libcodac-rob.a -Path "$env:ChocolateyPackageFolder\lib" +#Install-BinFile -Name libcodac.a -Path "$packageDir\lib" +#Install-BinFile -Name libcodac-rob.a -Path "$packageDir\lib" From 110f62c949208d269e2d2cf2b55936bb04b6731f Mon Sep 17 00:00:00 2001 From: SimonRohou Date: Tue, 6 Dec 2022 18:15:30 +0100 Subject: [PATCH 062/256] [doc] minor updates --- doc/doc/dev/info_dev.rst | 7 +++++-- doc/doc/index.rst | 2 +- doc/doc/use-cases/lie-symmetries/index.rst | 2 +- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/doc/doc/dev/info_dev.rst b/doc/doc/dev/info_dev.rst index 4b5797e34..b3d01c4a3 100644 --- a/doc/doc/dev/info_dev.rst +++ b/doc/doc/dev/info_dev.rst @@ -165,7 +165,7 @@ Get Pybind11 as submodule: git submodule init git submodule update -Then, configure ``cmake`` with custom options and ``-DWITH_PYTHON=ON``: +Then, configure ``cmake`` with your custom options ``<...>`` and ``-DWITH_PYTHON=ON``: .. code-block:: bash @@ -175,10 +175,13 @@ This configuration generates header files containing docstrings for Python, base the content of XML files made by Doxygen. The documentation of any C++/Python function is then located in the C++ header files of the :file:`/src` directory. -Finally, after the compilation: +Note that you also have to configure IBEX with the ``-DCMAKE_CXX_FLAGS="-fPIC"`` flag. + +Finally, after the compilation of Codac (and IBEX): .. code-block:: bash + # from codac repository cd build/python/python_package python3 setup.py develop --user diff --git a/doc/doc/index.rst b/doc/doc/index.rst index 63e1a884c..4912e1789 100644 --- a/doc/doc/index.rst +++ b/doc/doc/index.rst @@ -432,7 +432,7 @@ Main related publications .. |desrochers-phd-year| replace:: 2018 .. |damers-phd-title| replace:: Lie Groups applied to localisation of mobile robots -.. _damers-phd-title: https://julien-damers.fr/phd/complete.pdf +.. _damers-phd-title: https://julien-damers.fr/phd/Lie_Groups_applied_to_localisation_of_mobile_robots.pdf .. |damers-phd-authors| replace:: Damers .. |damers-phd-journal| replace:: PhD thesis .. |damers-phd-year| replace:: 2022 diff --git a/doc/doc/use-cases/lie-symmetries/index.rst b/doc/doc/use-cases/lie-symmetries/index.rst index 74880c698..11aa55672 100644 --- a/doc/doc/use-cases/lie-symmetries/index.rst +++ b/doc/doc/use-cases/lie-symmetries/index.rst @@ -161,7 +161,7 @@ Related content .. _lie-pdf: http://julien-damers.fr/publis/lie_groups_applied_to_guaranteed_integration.pdf .. |thesis-pdf| replace:: **Download the thesis** -.. _thesis-pdf: https://julien-damers.fr/phd/complete.pdf +.. _thesis-pdf: https://julien-damers.fr/phd/Lie_Groups_applied_to_localisation_of_mobile_robots.pdf .. admonition:: Related publication From 7bc11ee27bc2e1cd2cbf5d63ace973fe7671adb2 Mon Sep 17 00:00:00 2001 From: SimonRohou Date: Tue, 6 Dec 2022 18:16:47 +0100 Subject: [PATCH 063/256] [burnov] minor updates --- examples/brunovsky/CMakeLists.txt | 2 +- examples/brunovsky/main.cpp | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/brunovsky/CMakeLists.txt b/examples/brunovsky/CMakeLists.txt index ed4ee820d..a0cd1bf5b 100755 --- a/examples/brunovsky/CMakeLists.txt +++ b/examples/brunovsky/CMakeLists.txt @@ -49,6 +49,6 @@ # Compilation - add_executable(${PROJECT_NAME} main.cpp) + add_executable(${PROJECT_NAME} main_debug.cpp) target_include_directories(${PROJECT_NAME} SYSTEM PUBLIC ${CODAC_INCLUDE_DIRS} ${EIGEN3_INCLUDE_DIRS}) target_link_libraries(${PROJECT_NAME} PUBLIC ${CODAC_LIBRARIES} Ibex::ibex ${CODAC_LIBRARIES}) \ No newline at end of file diff --git a/examples/brunovsky/main.cpp b/examples/brunovsky/main.cpp index cd018fccb..93d0d846d 100755 --- a/examples/brunovsky/main.cpp +++ b/examples/brunovsky/main.cpp @@ -22,7 +22,7 @@ int main() { /* =========== TRUTH =========== */ - double dt = 0.003; // tubes timestep + double dt = 0.01; // tubes timestep Interval tdomain(0.,3.); // temporal domain // Unknown truth @@ -53,7 +53,7 @@ int main() TubeVector u(u_truth, dt); // centered on analytical expression // Intermediate variables: - TubeVector v(tdomain, dt, 4), a(tdomain, dt, 4); + TubeVector v(tdomain, dt, 4), a(tdomain, dt, 2); /* =========== CREATING CONTRACTORS =========== */ @@ -66,7 +66,7 @@ int main() v[2]-u[0] ; \ v[3]-u[1])")); - CtcFunction ctc_a(Function("a[4]", "x[4]", "u[2]", + CtcFunction ctc_a(Function("a[2]", "x[4]", "u[2]", "(u[1]*cos(x[2])-x[3]*sin(x[2])*u[0]-a[0] ; \ u[1]*sin(x[2])+x[3]*cos(x[2])*u[0]-a[1])")); From 38140183706f74758f96fee6f945e29c8289b419 Mon Sep 17 00:00:00 2001 From: SimonRohou Date: Tue, 6 Dec 2022 18:17:43 +0100 Subject: [PATCH 064/256] [codac2] strong updates (tube structure) --- python/codac/__init__.py | 4 +- src/CMakeLists.txt | 2 +- src/core/2/contractors/codac2_CtcLinobs.cpp | 35 +- src/core/2/contractors/codac2_CtcLinobs.h | 7 +- .../2/domains/tube/codac2_AbstractConstTube.h | 2 +- .../2/domains/tube/codac2_AbstractSlice.h | 1 + .../tube/codac2_AbstractSlicedTube.cpp | 6 + .../domains/tube/codac2_AbstractSlicedTube.h | 1 + src/core/2/domains/tube/codac2_Slice.h | 188 +++++++--- src/core/2/domains/tube/codac2_TDomain.cpp | 199 +++++++--- src/core/2/domains/tube/codac2_TDomain.h | 12 +- src/core/2/domains/tube/codac2_TSlice.cpp | 15 + src/core/2/domains/tube/codac2_TSlice.h | 3 + src/core/2/domains/tube/codac2_Tube.cpp | 54 ++- src/core/2/domains/tube/codac2_Tube.h | 130 ++++++- .../2/domains/tube/codac2_TubeEvaluation.h | 78 +++- .../arithmetic/codac_polygon_arithmetic.cpp | 2 - src/core/contractors/dyn/codac_CtcChain.cpp | 20 + src/core/contractors/dyn/codac_CtcChain.h | 3 + src/core/contractors/dyn/codac_CtcDeriv.cpp | 25 ++ src/core/contractors/dyn/codac_CtcDeriv.h | 4 + src/core/contractors/dyn/codac_CtcLohner.cpp | 2 +- src/core/geometry/codac_ConvexPolygon.cpp | 68 ++-- src/core/geometry/codac_ConvexPolygon.h | 9 +- src/core/geometry/codac_Polygon.cpp | 26 +- src/core/geometry/codac_Polygon.h | 3 +- src/core/geometry/codac_SepPolygon.h | 6 +- src/core/geometry/codac_ThickEdge.cpp | 4 - src/core/separators/codac_SepProj.cpp | 10 +- tests/core/CMakeLists.txt | 2 +- tests/core/tests_codac2_tubes.cpp | 353 +++++++++++++----- tests/core/tests_polygons.cpp | 58 +-- 32 files changed, 979 insertions(+), 353 deletions(-) diff --git a/python/codac/__init__.py b/python/codac/__init__.py index e2dee98b4..9d205fdf8 100644 --- a/python/codac/__init__.py +++ b/python/codac/__init__.py @@ -26,7 +26,7 @@ def pySIVIA(X0, ops, epsilon, figure_name='', draw_boxes=True, save_result=True, print('\nWarning: pySIVIA(..) is deprecated and has been replaced by SIVIA(..).') print('More information can be found with help(SIVIA).\n') print('Example of use:\n') - print(' SIVIA(x0, sep, 0.05, display_result=True, fig_name="SIVIA", return_result=True, color_map={SetValue.IN:"k[r]", SetValue.OUT:"k[b]", SetValue.UNKNOWN:"k[y]"})\n') + print(' SIVIA(x0, sep, 0.05, regular_paving=False, display_result=True, fig_name="SIVIA", return_result=True, color_map={SetValue.IN:"k[r]", SetValue.OUT:"k[b]", SetValue.UNKNOWN:"k[y]"})\n') cmap = {} if color_in: @@ -36,4 +36,4 @@ def pySIVIA(X0, ops, epsilon, figure_name='', draw_boxes=True, save_result=True, if color_maybe: cmap[SetValue.UNKNOWN] = color_maybe - return SIVIA(X0, ops, epsilon, display_result=draw_boxes, fig_name=figure_name, return_result=save_result, color_map=cmap) \ No newline at end of file + return SIVIA(X0, ops, epsilon, regular_paving=False, display_result=draw_boxes, fig_name=figure_name, return_result=save_result, color_map=cmap) \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 518cd8b6b..1bb27a682 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -78,7 +78,7 @@ if((CMAKE_BUILD_TYPE MATCHES Debug) AND (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")) # programs in Release (otherwise it would be only added by Ibex if the program # is built in Debug)... file(APPEND ${CODAC_CMAKE_CONFIG_FILE} " -set(CMAKE_EXE_LINKER_FLAGS \"${CMAKE_EXE_LINKER_FLAGS} -pg\") +set(CMAKE_EXE_LINKER_FLAGS \"${CMAKE_EXE_LINKER_FLAGS}\") ") endif() diff --git a/src/core/2/contractors/codac2_CtcLinobs.cpp b/src/core/2/contractors/codac2_CtcLinobs.cpp index 731f44f5d..d314696e8 100644 --- a/src/core/2/contractors/codac2_CtcLinobs.cpp +++ b/src/core/2/contractors/codac2_CtcLinobs.cpp @@ -70,14 +70,24 @@ namespace codac2 contract(v_domains[0]->tube_vector(), v_domains[1]->tube());*/ } + void CtcLinobs::contract(codac::TubeVector& x, const codac::Tube& u, TimePropag t_propa) + { + auto tdomain = codac2::create_tdomain(x.tdomain(), x[0].slice(0)->tdomain().diam(), true); + Tube _x(tdomain, ConvexPolygon()); + Tube _u(tdomain, Interval()); + + } + void CtcLinobs::contract(Tube& x, const Tube& u, TimePropag t_propa, bool compute_envelopes) { assert(x.tdomain() == u.tdomain()); + assert(x.tdomain()->all_gates_defined()); if(t_propa & TimePropag::FORWARD) for(auto it = x.begin(); it != x.end(); ++it) { if((*it).is_gate()) continue; + if((*it).t0_tf().is_unbounded()) continue; const shared_ptr> su = static_pointer_cast>((*it).tslice().slices().at(&u)); contract(*it, *su, TimePropag::FORWARD, compute_envelopes && !(t_propa & TimePropag::BACKWARD)); // Envelopes are contracted in the bwd iteration if selected @@ -87,6 +97,7 @@ namespace codac2 for(auto it = x.rbegin(); it != x.rend(); ++it) { if((*it).is_gate()) continue; + if((*it).t0_tf().is_unbounded()) continue; const shared_ptr> su = static_pointer_cast>((*it).tslice().slices().at(&u)); contract(*it, *su, TimePropag::BACKWARD, compute_envelopes); } @@ -103,35 +114,41 @@ namespace codac2 if(x.is_gate()) return; - if((t_propa & TimePropag::FORWARD) && x.next_slice_ptr()) + auto next = x.next_slice_ptr(); + + if((t_propa & TimePropag::FORWARD) && next) { - ConvexPolygon output_gate = x.output_gate(); + assert(next->is_gate()); + ConvexPolygon output_gate = next->codomain(); ctc_fwd_gate(output_gate, x.input_gate(), x.t0_tf().diam(), u.codomain()); - x.next_slice_ptr()->set(output_gate); + next->set(output_gate); } - if((t_propa & TimePropag::BACKWARD) && x.prev_slice_ptr()) + auto prev = x.prev_slice_ptr(); + + if((t_propa & TimePropag::BACKWARD) && prev) { - ConvexPolygon input_gate = x.input_gate(); + assert(prev->is_gate()); + ConvexPolygon input_gate = prev->codomain(); ctc_bwd_gate(input_gate, x.output_gate(), x.t0_tf().diam(), u.codomain()); - x.prev_slice_ptr()->set(input_gate); + prev->set(input_gate); } if(compute_envelope) - x.set(polygon_envelope(x.input_gate(), x.t0_tf().diam(), u.codomain())); + x.set(polygon_envelope(prev->codomain(), x.t0_tf().diam(), u.codomain())); } void CtcLinobs::ctc_fwd_gate(ConvexPolygon& p_k, const ConvexPolygon& p_km1, double dt_km1_k, const Interval& u_km1) { - p_k = p_k & (exp_At(_A,dt_km1_k)*p_km1 + dt_km1_k*exp_At(_A,Interval(0.,dt_km1_k))*(u_km1*_b)); + p_k &= exp_At(_A,dt_km1_k)*p_km1 + dt_km1_k*exp_At(_A,Interval(0.,dt_km1_k))*(u_km1*_b); p_k.simplify(m_polygon_max_edges); } void CtcLinobs::ctc_bwd_gate(ConvexPolygon& p_k, const ConvexPolygon& p_kp1, double dt_k_kp1, const Interval& u_k) { - p_k = p_k & (exp_At(-_A,dt_k_kp1)*p_kp1 - dt_k_kp1*exp_At(-_A,Interval(0.,dt_k_kp1))*(u_k*_b)); + p_k &= exp_At(-_A,dt_k_kp1)*p_kp1 - dt_k_kp1*exp_At(-_A,Interval(0.,dt_k_kp1))*(u_k*_b); p_k.simplify(m_polygon_max_edges); } diff --git a/src/core/2/contractors/codac2_CtcLinobs.h b/src/core/2/contractors/codac2_CtcLinobs.h index 11827fb09..feee4c540 100644 --- a/src/core/2/contractors/codac2_CtcLinobs.h +++ b/src/core/2/contractors/codac2_CtcLinobs.h @@ -32,6 +32,9 @@ namespace codac2 CtcLinobs(const codac::Matrix& A, const codac::Vector& b); // /!\ auto evaluation of e^At not reliable ~CtcLinobs(); + // to be removed: + void contract(codac::TubeVector& x, const codac::Tube& u, codac::TimePropag t_propa); + void contract(std::vector& v_domains); void contract(Tube& x, const Tube& u, codac::TimePropag t_propa = codac::TimePropag::FORWARD | codac::TimePropag::BACKWARD, bool compute_envelopes = true); void contract(Slice& x, const Slice& u, codac::TimePropag t_propa = codac::TimePropag::FORWARD | codac::TimePropag::BACKWARD, bool compute_envelope = true); @@ -46,8 +49,8 @@ namespace codac2 protected: - const codac::Matrix& _A; - const codac::Vector& _b; + const codac::Matrix _A; + const codac::Vector _b; const int m_polygon_max_edges = 15; diff --git a/src/core/2/domains/tube/codac2_AbstractConstTube.h b/src/core/2/domains/tube/codac2_AbstractConstTube.h index 03eec5867..c84addb12 100644 --- a/src/core/2/domains/tube/codac2_AbstractConstTube.h +++ b/src/core/2/domains/tube/codac2_AbstractConstTube.h @@ -38,7 +38,7 @@ namespace codac2 } - explicit AbstractConstTube(const T& x); + AbstractConstTube(const T& x); virtual ~AbstractConstTube() { diff --git a/src/core/2/domains/tube/codac2_AbstractSlice.h b/src/core/2/domains/tube/codac2_AbstractSlice.h index 0f856f352..6328549aa 100644 --- a/src/core/2/domains/tube/codac2_AbstractSlice.h +++ b/src/core/2/domains/tube/codac2_AbstractSlice.h @@ -36,6 +36,7 @@ namespace codac2 AbstractSlice(const AbstractSlicedTube& tubevector, const std::list::iterator& _it_tslice); virtual std::shared_ptr duplicate() const = 0; virtual size_t size() const = 0; + virtual void set_unbounded() = 0; const Interval& t0_tf() const; const TSlice& tslice() const; diff --git a/src/core/2/domains/tube/codac2_AbstractSlicedTube.cpp b/src/core/2/domains/tube/codac2_AbstractSlicedTube.cpp index f259afcaf..0c29243e7 100644 --- a/src/core/2/domains/tube/codac2_AbstractSlicedTube.cpp +++ b/src/core/2/domains/tube/codac2_AbstractSlicedTube.cpp @@ -21,6 +21,12 @@ namespace codac2 } + shared_ptr& AbstractSlicedTube::tdomain() + { + return const_cast&>( + static_cast(*this).tdomain()); + } + const shared_ptr& AbstractSlicedTube::tdomain() const { return _tdomain; diff --git a/src/core/2/domains/tube/codac2_AbstractSlicedTube.h b/src/core/2/domains/tube/codac2_AbstractSlicedTube.h index 528f6b015..84041ff59 100644 --- a/src/core/2/domains/tube/codac2_AbstractSlicedTube.h +++ b/src/core/2/domains/tube/codac2_AbstractSlicedTube.h @@ -27,6 +27,7 @@ namespace codac2 virtual const std::shared_ptr& first_abstract_slice_ptr() const = 0; virtual const std::shared_ptr& last_abstract_slice_ptr() const = 0; + std::shared_ptr& tdomain(); const std::shared_ptr& tdomain() const; Interval t0_tf() const; diff --git a/src/core/2/domains/tube/codac2_Slice.h b/src/core/2/domains/tube/codac2_Slice.h index bbc134ca4..4115faabc 100644 --- a/src/core/2/domains/tube/codac2_Slice.h +++ b/src/core/2/domains/tube/codac2_Slice.h @@ -21,6 +21,7 @@ #include "codac_Exception.h" #include "codac2_AbstractSlice.h" #include "codac_BoolInterval.h" +#include "codac_ConvexPolygon.h" #define EPSILON_CONTAINS ibex::next_float(0.) * 1000. //!< epsilon limit of the contains() algorithm @@ -87,6 +88,14 @@ namespace codac2 return codomain().size(); } + double volume() const + { + if constexpr(std::is_same::value) + return codomain().diam(); + else + return codomain().volume(); + } + bool is_gate() const { return t0_tf().is_degenerated(); @@ -94,7 +103,10 @@ namespace codac2 bool is_empty() const { - return input_gate().is_empty() || output_gate().is_empty(); + if(is_gate()) + return _codomain.is_empty(); + else + return input_gate().is_empty() || output_gate().is_empty(); } bool is_unbounded() const @@ -104,61 +116,73 @@ namespace codac2 BoolInterval contains(const TrajectoryVector& x) const { - assert(t0_tf().is_subset(x.tdomain())); - - /*IntervalVector traj_tdomain = x(t0_tf()); - // Using x(Interval(double)) for reliable evaluation: - IntervalVector traj_input = x(Interval(t0_tf().lb())); - IntervalVector traj_output = x(Interval(t0_tf().ub())); - - if(_codomain.intersects(traj_tdomain) == BoolInterval::NO - || input_gate().intersects(traj_input) == BoolInterval::NO - || output_gate().intersects(traj_output) == BoolInterval::NO) - return BoolInterval::NO; + if constexpr(std::is_same::value) + { + assert(false && "not implemented"); + return BoolInterval::MAYBE; + } else { - if(!input_gate().is_superset(traj_input) || !output_gate().is_superset(traj_output)) + if(!t0_tf().intersects(x.tdomain())) return BoolInterval::MAYBE; - else if(_codomain.is_superset(traj_tdomain)) - return BoolInterval::YES; + BoolInterval is_contained = BoolInterval::YES; + if(!t0_tf().is_subset(x.tdomain())) + is_contained = BoolInterval::MAYBE; - else // too much pessimism for the trajectory evaluation on t0_tf() + Interval t_ = t0_tf() & x.tdomain(); + T traj_tdomain(x(t_)); + // Using x(Interval(double)) for reliable evaluation: + T traj_input(x(Interval(t_.lb()))); + T traj_output(x(Interval(t_.ub()))); + + if(_codomain.intersects(traj_tdomain) == BoolInterval::NO + || input_gate().intersects(traj_input) == BoolInterval::NO + || output_gate().intersects(traj_output) == BoolInterval::NO) + return BoolInterval::NO; + + else { - // Bisections are performed to reach an accurate evaluation + if(!traj_input.is_subset(input_gate()) || !traj_output.is_subset(output_gate())) + return BoolInterval::MAYBE; - std::list s_subtdomains; - s_subtdomains.push_front(t0_tf()); + else if(traj_tdomain.is_subset(_codomain)) + return is_contained; - while(!s_subtdomains.empty()) + else // too much pessimism for the trajectory evaluation on t0_tf() { - Interval t = s_subtdomains.front(); - s_subtdomains.pop_front(); + // Bisections are performed to reach an accurate evaluation - IntervalVector thinner_eval = x(t); + std::list s_subtdomains; + s_subtdomains.push_front(t_); - if(!_codomain.intersects(thinner_eval)) + while(!s_subtdomains.empty()) { - return BoolInterval::NO; - } + Interval t = s_subtdomains.front(); + s_subtdomains.pop_front(); - else if(!_codomain.is_superset(thinner_eval)) - { - if(t.diam() < EPSILON_CONTAINS) - return BoolInterval::MAYBE; + T thinner_eval(x(t)); + + if(!_codomain.intersects(thinner_eval)) + { + return BoolInterval::NO; + } - s_subtdomains.push_front(Interval(t.lb(), t.lb() + t.diam() / 2.)); - s_subtdomains.push_front(Interval(t.lb() + t.diam() / 2., t.ub())); + else if(!thinner_eval.is_subset(_codomain)) + { + if(t.diam() < EPSILON_CONTAINS) + return BoolInterval::MAYBE; + + s_subtdomains.push_front(Interval(t.lb(), t.lb() + t.diam() / 2.)); + s_subtdomains.push_front(Interval(t.lb() + t.diam() / 2., t.ub())); + } } - } - return BoolInterval::YES; + return is_contained; + } } - }*/ - - // todo - return BoolInterval::YES; + } } const std::shared_ptr> prev_slice_ptr() const @@ -190,27 +214,64 @@ namespace codac2 T input_gate() const { - T gate = codomain(); - if(!is_gate() && prev_slice_ptr()) - gate &= prev_slice_ptr()->codomain(); - return gate; + if(!prev_slice_ptr()) + return codomain(); + + else + { + if(prev_slice_ptr()->is_gate()) + return prev_slice_ptr()->codomain(); + else + return codomain() & prev_slice_ptr()->codomain(); + } } T output_gate() const { - T gate = codomain(); - if(!is_gate() && next_slice_ptr()) - gate &= next_slice_ptr()->codomain(); - return gate; + if(!next_slice_ptr()) + return codomain(); + + else + { + if(next_slice_ptr()->is_gate()) + return next_slice_ptr()->codomain(); + else + return codomain() & next_slice_ptr()->codomain(); + } } void set(const T& x) { - if constexpr(!std::is_same::value) + if constexpr(!std::is_same::value) // 'if' to be removed with virtual set classes + { assert((size_t)codomain().size() == size()); + } + _codomain = x; - if(is_gate()) - _codomain &= prev_slice_ptr()->codomain() & next_slice_ptr()->codomain(); + + if(prev_slice_ptr()) + { + if constexpr(!std::is_same::value) // 'if' to be removed with virtual set classes + { + assert((size_t)prev_slice_ptr()->codomain().size() == size()); + } + if(is_gate()) + _codomain &= prev_slice_ptr()->codomain(); + else if(prev_slice_ptr()->is_gate()) + prev_slice_ptr()->_codomain &= _codomain; + } + + if(next_slice_ptr()) + { + if constexpr(!std::is_same::value) // 'if' to be removed with virtual set classes + { + assert((size_t)next_slice_ptr()->codomain().size() == size()); + } + if(is_gate()) + _codomain &= next_slice_ptr()->codomain(); + else if(next_slice_ptr()->is_gate()) + next_slice_ptr()->_codomain &= _codomain; + } } void set_empty() @@ -218,19 +279,32 @@ namespace codac2 _codomain.set_empty(); if(!is_gate()) { - if(prev_slice_ptr()->is_gate()) + if(prev_slice_ptr() && prev_slice_ptr()->is_gate()) prev_slice_ptr()->set_empty(); - if(next_slice_ptr()->is_gate()) + if(next_slice_ptr() && next_slice_ptr()->is_gate()) next_slice_ptr()->set_empty(); } } + void set_unbounded() + { + if constexpr(std::is_same::value) // 'if' to be removed with virtual set classes + _codomain = T(size()); + else + _codomain = T(); + } + void set_component(size_t i, const Interval& xi) { assert((size_t)codomain().size() == size()); _codomain[i] = xi; if(is_gate()) - _codomain[i] &= prev_slice_ptr()->codomain()[i] & next_slice_ptr()->codomain()[i]; + { + if(prev_slice_ptr()) + _codomain[i] &= prev_slice_ptr()->codomain()[i]; + if(next_slice_ptr()) + _codomain[i] &= next_slice_ptr()->codomain()[i]; + } } const Slice& inflate(double rad) @@ -240,6 +314,16 @@ namespace codac2 return *this; } + bool operator==(const Slice& x) const + { + return _codomain == x._codomain; + } + + bool operator!=(const Slice& x) const + { + return _codomain != x._codomain; + } + friend std::ostream& operator<<(std::ostream& os, const Slice& x) { os << x.t0_tf() diff --git a/src/core/2/domains/tube/codac2_TDomain.cpp b/src/core/2/domains/tube/codac2_TDomain.cpp index ca6459566..001d1cf19 100644 --- a/src/core/2/domains/tube/codac2_TDomain.cpp +++ b/src/core/2/domains/tube/codac2_TDomain.cpp @@ -20,42 +20,36 @@ using namespace codac; namespace codac2 { - TDomain::TDomain(const Interval& t0_tf, bool with_gates) : - TDomain(t0_tf, t0_tf.diam(), with_gates) + TDomain::TDomain(const Interval& t0_tf) + : _tslices({ TSlice(t0_tf) }) { } TDomain::TDomain(const Interval& t0_tf, double dt, bool with_gates) { + assert(!t0_tf.is_unbounded() && !t0_tf.is_degenerated()); assert(!t0_tf.is_empty()); assert(dt > 0.); - if(isinf(dt)) - _tslices.push_back(TSlice(t0_tf)); - - else + for(double t = t0_tf.lb() ; t < t0_tf.ub()+dt ; t=t+dt) { - double prev_t = -oo; - for(double t = t0_tf.lb() ; t < t0_tf.ub()+dt ; t+=dt) - { - double t_ = min(t, t0_tf.ub()); - - _tslices.push_back(TSlice(Interval(prev_t,t_))); - if(with_gates) - _tslices.push_back(TSlice(Interval(t_,t_))); - - prev_t = t_; - } - - _tslices.push_back(TSlice(Interval(t0_tf.ub(),oo))); + double t_next = min(t0_tf.ub(),t+dt); + if(with_gates) + _tslices.push_back(TSlice(Interval(t))); + _tslices.push_back(TSlice(Interval(t,t_next))); + if(t_next == t0_tf.ub()) + break; } + + if(with_gates) + _tslices.push_back(TSlice(Interval(t0_tf.ub()))); } const Interval TDomain::t0_tf() const { - return Interval(_tslices.front().t0_tf().ub(), - prev(_tslices.end())->t0_tf().lb()); + return Interval(_tslices.front().t0_tf().lb(), + prev(_tslices.end())->t0_tf().ub()); } size_t TDomain::nb_tslices() const @@ -68,10 +62,40 @@ namespace codac2 return _tslices.front().slices().size(); } + bool TDomain::all_gates_defined() const + { + if(t0_tf().is_degenerated()) + return true; + + else if(nb_tslices() == 1) + return false; + + else + { + list::const_iterator it = std::next(_tslices.begin()); + while(it != _tslices.end()) + { + if(it->t0_tf().is_degenerated()) + return false; + + it++; + + if(it != _tslices.end()) + { + if(!it->t0_tf().is_degenerated()) + return false; + it++; + } + } + + return true; + } + } + list::iterator TDomain::iterator_tslice(double t) { - if(t == -oo) return _tslices.begin(); - if(t == oo) return prev(_tslices.end()); + if(!t0_tf().contains(t)) + return _tslices.end(); list::iterator it; for(it = _tslices.begin(); it != _tslices.end(); ++it) @@ -81,29 +105,99 @@ namespace codac2 || (tdom.lb() <= t && tdom.ub() > t)) // slice return it; } - assert(false && "time must belong to one slice because tdomain is unbounded"); - return _tslices.end(); + + it = _tslices.end(); it--; + return it; } - list::iterator TDomain::sample(double t, bool allow_gate) + list::iterator TDomain::sample(double t, bool with_gates) { - list::iterator it = iterator_tslice(t); - assert(it != _tslices.end()); - const Interval tdomain = it->t0_tf(); - assert(tdomain.contains(t)); + assert(!isnan(t)); + list::iterator it; - if((tdomain.lb() == t && !allow_gate) || tdomain.is_degenerated()) - return it; + if(t <= t0_tf().lb()) // if outside the already defined tdomain + { + it = _tslices.begin(); - it->set_tdomain(Interval(tdomain.lb(), t)); - TSlice ts(*it, Interval(t, tdomain.ub())); + if(it->is_gate() && it->t0_tf().lb() == t) + return it; - ++it; - it = _tslices.insert(it, ts); - for(auto& [k,s] : it->_slices) // adding the new iterator pointer to the new slices - s->_it_tslice = it; - - return it; + TSlice ts(*it, Interval(t, t0_tf().lb())); // duplicate with different tdomain + it = _tslices.insert(it, ts); + for(auto& [k,s] : it->_slices) + { + s->_it_tslice = it; + s->set_unbounded(); // reinitialization + } + + if(with_gates) + { + if(it->t0_tf().is_degenerated()) // iterator already pointing to a gate + return it; + else + return sample(t, true); // recursive + } + else + return it; + } + + else if(t > t0_tf().ub()) // if outside the already defined tdomain + { + it = _tslices.end(); + TSlice ts(*std::prev(it), Interval(t0_tf().ub(),t)); // duplicate with different tdomain + it = _tslices.insert(it, ts); + for(auto& [k,s] : it->_slices) + { + s->_it_tslice = it; + s->set_unbounded(); // reinitialization + } + + if(with_gates) + return sample(t, true); // recursive + else + return it; + } + + else // inside [t0,tf] + { + it = iterator_tslice(t); + assert(it != _tslices.end()); + const Interval tdomain_it = it->t0_tf(); + assert(tdomain_it.contains(t)); + + if(tdomain_it.is_degenerated() + || (!with_gates && (tdomain_it.lb() == t || t == t0_tf().ub()))) + return it; // nothing more to do + + // Else, performing sampling + it->set_tdomain(Interval(tdomain_it.lb(), t)); + bool new_gate_added = it->t0_tf().is_degenerated(); + list::iterator it_gate = it; + TSlice ts(*it, Interval(t, tdomain_it.ub())); // duplicate with different tdomain + // (*it) is the TSlice to copy + + // From C++ insert() doc: the container is extended by inserting new elements before the element at the specified position + ++it; // we will insert the new tslice before the next TSlice [t.ub(),..] + it = _tslices.insert(it, ts); // then, it points to the newly inserted element + for(auto& [k,s] : it->_slices) // adding the new iterator pointer to the new slices + s->_it_tslice = it; + + // In case the sampling includes the creation of a gate, the method is called again at same t + if(new_gate_added) + return it_gate; + else if(with_gates && !new_gate_added) + return sample(t, true); + else + return it; + } + } + + void TDomain::sample(const Interval& t0_tf, double dt, bool with_gates) + { + assert(dt >= 0.); + assert(!t0_tf.is_degenerated()); + for(double t = t0_tf.lb() ; t < t0_tf.ub()+dt ; t=t+dt) + sample(min(t0_tf.ub(),t), with_gates); } const list& TDomain::tslices() const @@ -111,6 +205,12 @@ namespace codac2 return _tslices; } + list& TDomain::tslices() + { + return const_cast&>( + static_cast(*this).tslices()); + } + void TDomain::delete_gates() { list::iterator it = _tslices.begin(); @@ -135,13 +235,28 @@ namespace codac2 return os; } - shared_ptr create_tdomain(const Interval& t0_tf, bool with_gates) + shared_ptr create_tdomain(const Interval& t0_tf) { - return make_shared(t0_tf, with_gates); + return make_shared(t0_tf); } shared_ptr create_tdomain(const Interval& t0_tf, double dt, bool with_gates) { return make_shared(t0_tf, dt, with_gates); } + + bool TDomain::are_same(const shared_ptr& tdom1, const shared_ptr& tdom2) + { + if(tdom1 == tdom2) + return true; + if(tdom1->nb_tslices() != tdom2->nb_tslices()) + return false; + list::const_iterator it1 = tdom1->tslices().cbegin(), it2 = tdom2->tslices().cbegin(); + while(it1 != tdom1->tslices().cend()) + { + if(*it1 != *it2) return false; + it1++; it2++; + } + return true; + } } // namespace codac \ No newline at end of file diff --git a/src/core/2/domains/tube/codac2_TDomain.h b/src/core/2/domains/tube/codac2_TDomain.h index 29db26688..cfd8a9204 100644 --- a/src/core/2/domains/tube/codac2_TDomain.h +++ b/src/core/2/domains/tube/codac2_TDomain.h @@ -29,16 +29,20 @@ namespace codac2 { public: - explicit TDomain(const Interval& t0_tf = Interval(-oo,oo), bool with_gates = false); + explicit TDomain(const Interval& t0_tf); explicit TDomain(const Interval& t0_tf, double dt, bool with_gates = false); const Interval t0_tf() const; // todo: keep this method? - std::list::iterator iterator_tslice(double t); + std::list::iterator iterator_tslice(double t); // returns it on last slice if t==t_f, not end size_t nb_tslices() const; size_t nb_tubes() const; - std::list::iterator sample(double t, bool allow_gate = true); + bool all_gates_defined() const; + std::list::iterator sample(double t, bool with_gate = false); + void sample(const Interval& t0_tf, double dt, bool with_gates = false); friend std::ostream& operator<<(std::ostream& os, const TDomain& x); const std::list& tslices() const; + std::list& tslices(); void delete_gates(); + static bool are_same(const std::shared_ptr& tdom1, const std::shared_ptr& tdom2); protected: @@ -49,7 +53,7 @@ namespace codac2 friend class Tube; }; - std::shared_ptr create_tdomain(const Interval& t0_tf = Interval(-oo,oo), bool with_gates = false); + std::shared_ptr create_tdomain(const Interval& t0_tf = Interval(-oo,oo)); std::shared_ptr create_tdomain(const Interval& t0_tf, double dt, bool with_gates = false); } // namespace codac diff --git a/src/core/2/domains/tube/codac2_TSlice.cpp b/src/core/2/domains/tube/codac2_TSlice.cpp index 80aefcfe5..1c89ab5fa 100644 --- a/src/core/2/domains/tube/codac2_TSlice.cpp +++ b/src/core/2/domains/tube/codac2_TSlice.cpp @@ -36,6 +36,11 @@ namespace codac2 return _t0_tf; } + bool TSlice::is_gate() const + { + return _t0_tf.is_degenerated(); + } + void TSlice::set_tdomain(const Interval& tdomain) { assert(!tdomain.is_empty()); @@ -47,6 +52,16 @@ namespace codac2 return _slices; } + bool TSlice::operator==(const TSlice& x) const + { + return _t0_tf == x._t0_tf; + } + + bool TSlice::operator!=(const TSlice& x) const + { + return !operator==(x); + } + ostream& operator<<(ostream& os, const TSlice& x) { os << x._t0_tf; diff --git a/src/core/2/domains/tube/codac2_TSlice.h b/src/core/2/domains/tube/codac2_TSlice.h index 2ded9a5f7..fc0223a30 100644 --- a/src/core/2/domains/tube/codac2_TSlice.h +++ b/src/core/2/domains/tube/codac2_TSlice.h @@ -35,7 +35,10 @@ namespace codac2 explicit TSlice(const Interval& tdomain); TSlice(const TSlice& tslice, const Interval& tdomain); // performs a deep copy on slices const Interval& t0_tf() const; + bool is_gate() const; const std::map>& slices() const; + bool operator==(const TSlice& x) const; + bool operator!=(const TSlice& x) const; friend std::ostream& operator<<(std::ostream& os, const TSlice& x); protected: diff --git a/src/core/2/domains/tube/codac2_Tube.cpp b/src/core/2/domains/tube/codac2_Tube.cpp index 320b4d135..55085eb80 100644 --- a/src/core/2/domains/tube/codac2_Tube.cpp +++ b/src/core/2/domains/tube/codac2_Tube.cpp @@ -10,6 +10,7 @@ */ #include "codac2_Tube.h" +#include "codac_polygon_arithmetic.h" using namespace std; using namespace codac; @@ -20,7 +21,7 @@ namespace codac2 { codac::Tube x_(x.t0_tf()); for(const auto& s : x) - if(!s.t0_tf().is_unbounded()) + if(!s.t0_tf().is_unbounded()) // temporaly unbounded tslices not supported in codac1 x_.set(s.codomain(), s.t0_tf()); for(const auto& s : x) // setting gate (were overwritten) if(s.t0_tf().is_degenerated()) @@ -32,7 +33,7 @@ namespace codac2 { codac::TubeVector x_(x.t0_tf(), x.size()); for(const auto& s : x) - if(!s.t0_tf().is_unbounded()) + if(!s.t0_tf().is_unbounded()) // temporaly unbounded tslices not supported in codac1 x_.set(s.codomain(), s.t0_tf()); for(const auto& s : x) // setting gate (were overwritten) if(s.t0_tf().is_degenerated()) @@ -40,6 +41,55 @@ namespace codac2 return x_; } + codac::TubeVector to_codac1_poly(const Tube& x) + { + codac::TubeVector x_(x.t0_tf(), x.size()); + for(const auto& s : x) + if(!s.t0_tf().is_unbounded()) // temporaly unbounded tslices not supported in codac1 + x_.set(s.codomain().box(), s.t0_tf()); + for(const auto& s : x) // setting gate (were overwritten) + if(s.t0_tf().is_degenerated()) + x_.set(s.codomain().box(), s.t0_tf()); + return x_; + } + + codac2::Tube to_codac2(const codac::Tube& x_) + { + auto tdomain = create_tdomain(x_.tdomain()); + for(const codac::Slice *s = x_.first_slice() ; s != nullptr ; s=s->next_slice()) + tdomain->sample(s->tdomain().lb(), true); // with gate + tdomain->sample(x_.tdomain().ub(), true); // with gate + codac2::Tube x(tdomain, codac::Interval()); + for(auto& s : x) // includes gates + s.set(x_(s.t0_tf())); + return x; + } + + codac2::Tube to_codac2(const codac::TubeVector& x_) + { + auto tdomain = create_tdomain(x_[0].tdomain()); + for(const codac::Slice *s = x_[0].first_slice() ; s != nullptr ; s=s->next_slice()) + tdomain->sample(s->tdomain().lb(), true); // with gate + tdomain->sample(x_[0].tdomain().ub(), true); // with gate + codac2::Tube x(tdomain, codac::IntervalVector(x_.size())); + for(auto& s : x) // includes gates + s.set(x_(s.t0_tf())); + return x; + } + + codac2::Tube to_codac2_poly(const codac::TubeVector& x_) + { + assert(x_.size() == 2); + auto tdomain = create_tdomain(x_[0].tdomain()); + for(const codac::Slice *s = x_[0].first_slice() ; s != nullptr ; s=s->next_slice()) + tdomain->sample(s->tdomain().lb(), true); // with gate + tdomain->sample(x_[0].tdomain().ub(), true); // with gate + codac2::Tube x(tdomain, ConvexPolygon()); + for(auto& s : x) // includes gates + s.set(ConvexPolygon(x_(s.t0_tf()))); + return x; + } + template <> Tube::Tube(const std::shared_ptr& tdomain, const TFnc& f) : Tube(tdomain, IntervalVector(f.image_dim())) diff --git a/src/core/2/domains/tube/codac2_Tube.h b/src/core/2/domains/tube/codac2_Tube.h index 9b1e27234..15bed3ed3 100644 --- a/src/core/2/domains/tube/codac2_Tube.h +++ b/src/core/2/domains/tube/codac2_Tube.h @@ -33,6 +33,8 @@ namespace codac2 template class TubeEvaluation; template + class ConstTubeEvaluation; + template class TubeComponent; @@ -41,11 +43,11 @@ namespace codac2 { public: - explicit Tube(const std::shared_ptr& tdomain) : - Tube(tdomain, T()) - { - - } + // need to specify T (dim at least) explicit Tube(const std::shared_ptr& tdomain) : + // need to specify T (dim at least) Tube(tdomain, T()) + // need to specify T (dim at least) { + // need to specify T (dim at least) + // need to specify T (dim at least) } explicit Tube(const std::shared_ptr& tdomain, const TFnc& f) : Tube(tdomain, T()) @@ -76,15 +78,14 @@ namespace codac2 } } - explicit Tube(const Tube& x) : + Tube(const Tube& x) : AbstractSlicedTube(x.tdomain()) { for(std::list::iterator it = _tdomain->_tslices.begin(); it != _tdomain->_tslices.end(); ++it) { - it->_slices.insert( - std::pair>>(this, - std::make_shared>(it->_slices.at(&x)->size(), *this, it))); + std::shared_ptr> s_ptr = std::make_shared>(x(it)); + it->_slices.insert(std::pair>>(this, s_ptr)); } } @@ -113,6 +114,14 @@ namespace codac2 return _tdomain->nb_tslices(); } + double volume() const + { + double volume = 0.; + for(const auto& s : *this) + volume += s.volume(); + return volume; + } + virtual const std::shared_ptr& first_abstract_slice_ptr() const { return _tdomain->tslices().front().slices().at(this); @@ -157,8 +166,13 @@ namespace codac2 bool is_empty() const { + // Fast evaluation by considering gates first, then envelopes, + // which allows to quickly identify an empty set + for(const auto& s : *this) + if(s.is_gate() && s.is_empty()) + return true; for(const auto& s : *this) - if(s.is_empty()) + if(!s.is_gate() && s.is_empty()) return true; return false; } @@ -178,6 +192,7 @@ namespace codac2 BoolInterval result = BoolInterval::YES; for(const auto& s : *this) { + if(s.is_gate()) continue; BoolInterval b = s.contains(x); if(b == BoolInterval::NO) return BoolInterval::NO; else if(b == BoolInterval::MAYBE) result = BoolInterval::MAYBE; @@ -195,23 +210,56 @@ namespace codac2 return codomain; } + Slice& operator()(const std::list::iterator& it) + { + return const_cast&>( + static_cast(*this).operator()(it)); + } + + const Slice& operator()(const std::list::iterator& it) const + { + return *std::static_pointer_cast>(it->slices().at(this)); + } + TubeEvaluation operator()(double t) { return TubeEvaluation(this, t); } + const TubeEvaluation operator()(double t) const + { + return ConstTubeEvaluation(this, t); + } + TubeEvaluation operator()(const Interval& t) { return TubeEvaluation(this, t); } + const TubeEvaluation operator()(const Interval& t) const + { + return TubeEvaluation(this, t); + } + T eval(double t) const { - return std::static_pointer_cast>(_tdomain->iterator_tslice(t)->_slices.at(this))->codomain(); + if(!tdomain()->t0_tf().contains(t)) + return T(); // todo: case of dimension to specify? + std::list::iterator it_t = _tdomain->iterator_tslice(t); + assert(it_t != _tdomain->_tslices.end()); + T x = std::static_pointer_cast>(it_t->_slices.at(this))->codomain(); + if(!it_t->is_gate() && t==it_t->t0_tf().lb() && it_t!=_tdomain->_tslices.begin()) + x &= std::static_pointer_cast>((--it_t)->_slices.at(this))->codomain(); + return x; } T eval(const Interval& t) const { + if(!tdomain()->t0_tf().is_superset(t)) + return T(size()); // todo: case of dimension to specify? + if(t.is_degenerated()) + return eval(t.lb()); + std::list::iterator it = _tdomain->iterator_tslice(t.lb()); T codomain = std::static_pointer_cast>(it->_slices.at(this))->codomain(); @@ -226,16 +274,22 @@ namespace codac2 void set(const T& codomain) { - assert((size_t)codomain.size() == size()); + if constexpr(!std::is_same::value) + assert((size_t)codomain.size() == size()); + for(auto& s : *this) + if(!s.is_gate()) + s.set(codomain); for(auto& s : *this) - s.set(codomain); + if(s.is_gate()) + s.set(codomain); } void set(const T& codomain, double t) { - assert((size_t)codomain.size() == size()); - std::list::iterator it = _tdomain->sample(t); - std::static_pointer_cast>(it->_slices.at(this))->set(codomain); + if constexpr(!std::is_same::value) + assert((size_t)codomain.size() == size()); + std::list::iterator it = _tdomain->sample(t,true); + (*this)(it).set(codomain); } const Tube& inflate(double rad) @@ -255,6 +309,42 @@ namespace codac2 return TubeComponent(*this, i); } + bool operator==(const Tube& x) const + { + if(!TDomain::are_same(tdomain(), x.tdomain())) + return false; + + std::list::iterator it_this = _tdomain->_tslices.begin(); + std::list::const_iterator it_x = x.tdomain()->tslices().cbegin(); + + while(it_this != _tdomain->tslices().end()) + { + if(*std::static_pointer_cast>(it_this->_slices.at(this)) != + *std::static_pointer_cast>(it_x->slices().at(&x))) + return false; + it_this++; it_x++; + } + + return true; + } + + Tube operator&=(const Tube& x) + { + assert(TDomain::are_same(tdomain(), x.tdomain())); + std::list::iterator it_this = _tdomain->_tslices.begin(); + std::list::const_iterator it_x = x.tdomain()->tslices().cbegin(); + + while(it_this != _tdomain->tslices().end()) + { + std::shared_ptr> s = std::static_pointer_cast>(it_this->_slices.at(this)); + s->set(s->codomain() & std::static_pointer_cast>(it_x->slices().at(&x))->codomain()); + it_this++; it_x++; + } + + assert(it_x == x.tdomain()->tslices().cend()); + return *this; + } + friend std::ostream& operator<<(std::ostream& os, const Tube& x) { x.AbstractConstTube>::print(os); @@ -352,8 +442,12 @@ namespace codac2 const_iterator end() const { return const_iterator(*this, _tdomain->_tslices.cend()); } }; - codac::Tube to_codac1(const Tube& x); - codac::TubeVector to_codac1(const Tube& x); + codac::Tube to_codac1(const codac2::Tube& x); + codac::TubeVector to_codac1(const codac2::Tube& x); + codac::TubeVector to_codac1_poly(const codac2::Tube& x); + codac2::Tube to_codac2(const codac::Tube& x); + codac2::Tube to_codac2(const codac::TubeVector& x); + codac2::Tube to_codac2_poly(const codac::TubeVector& x); #include "codac2_TubeEvaluation.h" diff --git a/src/core/2/domains/tube/codac2_TubeEvaluation.h b/src/core/2/domains/tube/codac2_TubeEvaluation.h index 2a90698f2..eafc7453a 100644 --- a/src/core/2/domains/tube/codac2_TubeEvaluation.h +++ b/src/core/2/domains/tube/codac2_TubeEvaluation.h @@ -17,50 +17,100 @@ class TubeEvaluation { public: - const TubeEvaluation& operator=(const T& x) + TubeEvaluation& operator=(const T& x) { // Sampling the tube only if affectation is performed // (i.e. this is not done in the constructor) - std::list::iterator it_lb = _tubevector->_tdomain->sample(_t.lb(), false); - std::list::iterator it_ub = _tubevector->_tdomain->sample(_t.ub(), _t.is_degenerated()); + std::list::iterator it_lb = _tube->tdomain()->sample(_t.lb(), _t.is_degenerated()); + std::list::iterator it_ub; + + if(!_t.is_degenerated()) + { + it_ub = _tube->tdomain()->sample(_t.ub(), false); + it_ub--; // pointing to the tslice [..,_t.ub()] + + if(it_lb->t0_tf().ub() == _t.lb()) + it_lb++; + } + + else + it_ub = it_lb; do { - std::static_pointer_cast>(it_lb->_slices.at(_tubevector))->set(x); - it_lb++; - } while(it_lb != it_ub); + _tube->operator()(it_lb).set(x); + } while(it_lb != it_ub && (++it_lb) != _tube->tdomain()->tslices().end()); return *this; } - explicit operator T() + explicit operator T() const { - return _tubevector->eval(_t); + return _tube->eval(_t); } friend std::ostream& operator<<(std::ostream& os, const TubeEvaluation& x) { - os << x._tubevector->eval(x._t) << std::flush; + os << x._tube->eval(x._t) << std::flush; + return os; + } + + + protected: + + explicit TubeEvaluation(Tube *tubevector, double t) : + _t(Interval(t)), _tube(tubevector) + { + + } + + explicit TubeEvaluation(Tube *tubevector, const Interval& t) : + _t(t), _tube(tubevector) + { + + } + + const Interval _t; + Tube* _tube; + template + friend class Tube; +}; + +template +class ConstTubeEvaluation +{ + public: + + explicit operator T() const + { + return _tube->eval(_t); + } + + friend std::ostream& operator<<(std::ostream& os, const ConstTubeEvaluation& x) + { + os << x._tube->eval(x._t) << std::flush; return os; } protected: - explicit TubeEvaluation(const Tube* tubevector, double t) : - _t(Interval(t)), _tubevector(tubevector) + explicit ConstTubeEvaluation(const Tube *tubevector, double t) : + _t(Interval(t)), _tube(tubevector) { } - explicit TubeEvaluation(const Tube* tubevector, const Interval& t) : - _t(t), _tubevector(tubevector) + explicit ConstTubeEvaluation(const Tube *tubevector, const Interval& t) : + _t(t), _tube(tubevector) { } const Interval _t; - const Tube* _tubevector; + const Tube* _tube; + template + friend class Tube; }; #endif \ No newline at end of file diff --git a/src/core/arithmetic/codac_polygon_arithmetic.cpp b/src/core/arithmetic/codac_polygon_arithmetic.cpp index dae4cb777..4ccc7a04f 100644 --- a/src/core/arithmetic/codac_polygon_arithmetic.cpp +++ b/src/core/arithmetic/codac_polygon_arithmetic.cpp @@ -95,8 +95,6 @@ namespace codac if(!intersection_pt.does_not_exist()) { - assert(!e1.does_not_exist() && !e2.does_not_exist()); - // If edges are possibly parallel: if(ThickEdge::parallel(e1, e2) != NO) { diff --git a/src/core/contractors/dyn/codac_CtcChain.cpp b/src/core/contractors/dyn/codac_CtcChain.cpp index 3cbbc3717..89dd63fb5 100755 --- a/src/core/contractors/dyn/codac_CtcChain.cpp +++ b/src/core/contractors/dyn/codac_CtcChain.cpp @@ -45,4 +45,24 @@ namespace codac { CtcLinobs::contract(x, v, a, t_propa); } + + void CtcChain::contract(codac2::Tube& x, const codac2::Tube& a, TimePropag t_propa) + { + assert(x.size() == 2 && codac2::TDomain::are_same(x.tdomain(),a.tdomain())); + codac::TubeVector _x = to_codac1(x); + codac::Tube _a = to_codac1(a); + contract(_x[0], _x[1], _a, t_propa); + x &= codac2::to_codac2(_x); + } + + void CtcChain::contract(codac2::Tube& x, codac2::Tube& v, const codac2::Tube& a, TimePropag t_propa) + { + assert(codac2::TDomain::are_same(x.tdomain(),v.tdomain()) && codac2::TDomain::are_same(x.tdomain(),a.tdomain())); + codac::Tube _x = to_codac1(x); + codac::Tube _v = to_codac1(v); + codac::Tube _a = to_codac1(a); + contract(_x, _v, _a, t_propa); + x &= codac2::to_codac2(_x); + v &= codac2::to_codac2(_v); + } } \ No newline at end of file diff --git a/src/core/contractors/dyn/codac_CtcChain.h b/src/core/contractors/dyn/codac_CtcChain.h index f958937a5..ae5ae66ed 100755 --- a/src/core/contractors/dyn/codac_CtcChain.h +++ b/src/core/contractors/dyn/codac_CtcChain.h @@ -13,6 +13,7 @@ #define __CODAC_CTCCHAIN_H__ #include "codac_CtcLinobs.h" +#include "codac2_Tube.h" namespace codac { @@ -30,6 +31,8 @@ namespace codac void contract(std::vector& v_domains); void contract(Tube& x, Tube& v, const Tube& a, TimePropag t_propa = TimePropag::FORWARD | TimePropag::BACKWARD); + void contract(codac2::Tube& x, const codac2::Tube& a, TimePropag t_propa = TimePropag::FORWARD | TimePropag::BACKWARD); + void contract(codac2::Tube& x, codac2::Tube& v, const codac2::Tube& a, TimePropag t_propa = TimePropag::FORWARD | TimePropag::BACKWARD); protected: diff --git a/src/core/contractors/dyn/codac_CtcDeriv.cpp b/src/core/contractors/dyn/codac_CtcDeriv.cpp index 5970c1e55..15c2e304f 100644 --- a/src/core/contractors/dyn/codac_CtcDeriv.cpp +++ b/src/core/contractors/dyn/codac_CtcDeriv.cpp @@ -116,7 +116,32 @@ namespace codac for(int i = 0 ; i < x.size() ; i++) contract(x[i], v[i], t_propa); } + + void CtcDeriv::contract(codac2::Tube& x, const codac2::Tube& v, TimePropag t_propa) + { + Tube _x = codac2::to_codac1(x); + Tube _v = codac2::to_codac1(v); + contract(_x,_v,t_propa); + x &= codac2::to_codac2(_x); + } + + void CtcDeriv::contract(codac2::Tube& xv, TimePropag t_propa) + { + Tube x = codac2::to_codac1(xv)[0]; + Tube v = codac2::to_codac1(xv)[1]; + contract(x,v,t_propa); + xv &= codac2::to_codac2(TubeVector({x,v})); + } + void CtcDeriv::contract(codac2::Tube& x, int i, codac2::Tube& v, int j, TimePropag t_propa) + { + TubeVector _x = codac2::to_codac1(x); + TubeVector _v = codac2::to_codac1(v); + contract(_x[i],_v[j],t_propa); + x &= codac2::to_codac2(_x); + v &= codac2::to_codac2(_v); + } + void CtcDeriv::contract(Slice& x, const Slice& v, TimePropag t_propa) { assert(x.tdomain() == v.tdomain()); diff --git a/src/core/contractors/dyn/codac_CtcDeriv.h b/src/core/contractors/dyn/codac_CtcDeriv.h index 356ab4766..aaf53ccfa 100644 --- a/src/core/contractors/dyn/codac_CtcDeriv.h +++ b/src/core/contractors/dyn/codac_CtcDeriv.h @@ -14,6 +14,7 @@ #include "codac_DynCtc.h" #include "codac_Slice.h" +#include "codac2_Tube.h" namespace codac { @@ -69,6 +70,9 @@ namespace codac * (forward or backward in time, both ways by default) */ void contract(TubeVector& x, const TubeVector& v, TimePropag t_propa = TimePropag::FORWARD | TimePropag::BACKWARD); + void contract(codac2::Tube& x, const codac2::Tube& v, TimePropag t_propa = TimePropag::FORWARD | TimePropag::BACKWARD); + void contract(codac2::Tube& xv, TimePropag t_propa = TimePropag::FORWARD | TimePropag::BACKWARD); + void contract(codac2::Tube& x, int i, codac2::Tube& v, int j, TimePropag t_propa = TimePropag::FORWARD | TimePropag::BACKWARD); /** * \brief \f$\mathcal{C}_{\frac{d}{dt}}\big(\llbracket x\rrbracket(\cdot),\llbracket v\rrbracket(\cdot)\big)\f$: diff --git a/src/core/contractors/dyn/codac_CtcLohner.cpp b/src/core/contractors/dyn/codac_CtcLohner.cpp index bedcec017..9888536b4 100644 --- a/src/core/contractors/dyn/codac_CtcLohner.cpp +++ b/src/core/contractors/dyn/codac_CtcLohner.cpp @@ -12,7 +12,7 @@ #include #include -#include +#include #include #include diff --git a/src/core/geometry/codac_ConvexPolygon.cpp b/src/core/geometry/codac_ConvexPolygon.cpp index 7b0152856..e9179ac3a 100644 --- a/src/core/geometry/codac_ConvexPolygon.cpp +++ b/src/core/geometry/codac_ConvexPolygon.cpp @@ -10,11 +10,11 @@ #include #include -#include "codac_polygon_arithmetic.h" #include "codac_IntervalMatrix.h" #include "codac_ConvexPolygon.h" #include "codac_GrahamScan.h" #include "codac_VIBesFig.h" +#include "codac_polygon_arithmetic.h" using namespace std; using namespace ibex; @@ -24,7 +24,7 @@ namespace codac // Definition ConvexPolygon::ConvexPolygon() - : Polygon() + : ConvexPolygon(IntervalVector(2,Interval(-9999.,9999.))) { } @@ -36,8 +36,12 @@ namespace codac } ConvexPolygon::ConvexPolygon(const IntervalVector& box) - : Polygon(box) + : Polygon() { + assert(box.size() == 2); + assert(!box.is_empty()); + + ThickPoint::push(box, m_v_floating_pts); m_v_floating_pts = GrahamScan::convex_hull(m_v_floating_pts); } @@ -104,22 +108,10 @@ namespace codac return is_subset; } - - const BoolInterval ConvexPolygon::is_subset(const IntervalVector& x) const - { - // todo: this could be optimized in the specific case of a box - return is_subset(ConvexPolygon(x)); - } - - const BoolInterval ConvexPolygon::is_superset(const ConvexPolygon& p) const - { - return p.is_subset(*this); - } - - const BoolInterval ConvexPolygon::is_superset(const IntervalVector& x) const + + bool ConvexPolygon::is_unbounded() const { - // todo: this could be optimized in the specific case of a box - return is_superset(ConvexPolygon(x)); + return false; // todo } const BoolInterval ConvexPolygon::encloses(const ThickPoint& p) const @@ -128,6 +120,13 @@ namespace codac return contains(p); } + const BoolInterval ConvexPolygon::intersects(const ConvexPolygon& p) const + { + // todo: optimize this + ConvexPolygon inter = *this & p; + return inter.is_empty() ? BoolInterval::NO : BoolInterval::MAYBE; + } + const BoolInterval ConvexPolygon::contains(const ThickPoint& p) const { if(p.does_not_exist() || is_empty()) @@ -136,9 +135,6 @@ namespace codac if(!p.box().intersects(box())) return NO; // fast test - if(box() == IntervalVector(2)) - return YES; // fast test - // Using the ray tracing method: // A ray is defined from p to the right ; if it crosses // 'a' times one of the edges, and (a & 1), then p is inside @@ -146,9 +142,7 @@ namespace codac vector v_edges = edges(); int a = 0; // the crossing number counter size_t n = v_edges.size(); - const ThickEdge ray(p, ThickPoint(box()[0].ub()+1., p[1])); // horizontal edge to the right - assert(!v_edges[n-1].does_not_exist() && !ray.does_not_exist()); ThickPoint prev_e = v_edges[n-1] & ray; // Loop through all edges of the polygon, looking for intersections @@ -158,7 +152,6 @@ namespace codac continue; // Intersecting point - assert(!v_edges[i].does_not_exist() && !ray.does_not_exist()); const ThickPoint e = v_edges[i] & ray; if(!e.does_not_exist()) // intersection to the left of p, not considered { @@ -181,25 +174,12 @@ namespace codac return (a & 1) ? YES : NO; } - const BoolInterval ConvexPolygon::contains(const Vector& p) const - { - assert(p.size() == 2); - return contains(ThickPoint(p)); - } - - BoolInterval ConvexPolygon::intersects(const IntervalVector& x) const - { - // todo: improve this algorithm - return fast_intersection(x).is_empty() ? NO : YES; - } - // Setting values const ConvexPolygon& ConvexPolygon::inflate(double rad) { // todo - assert(false); return *this; } @@ -237,6 +217,14 @@ namespace codac assert(!inter.is_unbounded()); assert(e1 != e2); assert(!e1.does_not_exist() && !e2.does_not_exist()); + if(inter.does_not_exist()) + { + cout << "inter.does_not_exist():" << endl; + cout << "inter=" << inter << ", e1=" << e1 << ", e2=" << e2 << endl; + cout << box_limit << endl; + *this = ConvexPolygon(box_limit); + return *this; + } assert(!inter.does_not_exist()); if(!box_limit.contains(inter.mid())) @@ -311,9 +299,6 @@ namespace codac if(is_empty() || x.is_empty()) return IntervalVector(2, Interval::EMPTY_SET); - if(box() == IntervalVector(2)) - return x; - IntervalVector reduced_x = x & box(); if(reduced_x.is_empty()) @@ -323,10 +308,7 @@ namespace codac vector v_edges = edges(); for(const auto& edge : v_edges) - { - assert(!edge.does_not_exist()); inter |= edge & reduced_x; - } vector v_x_vertices; ThickPoint::push(reduced_x, v_x_vertices); diff --git a/src/core/geometry/codac_ConvexPolygon.h b/src/core/geometry/codac_ConvexPolygon.h index e81a62ee7..228fc58f4 100644 --- a/src/core/geometry/codac_ConvexPolygon.h +++ b/src/core/geometry/codac_ConvexPolygon.h @@ -35,13 +35,10 @@ namespace codac /// @{ const BoolInterval is_subset(const ConvexPolygon& p) const; - const BoolInterval is_subset(const IntervalVector& x) const; - const BoolInterval is_superset(const ConvexPolygon& p) const; - const BoolInterval is_superset(const IntervalVector& x) const; - const BoolInterval encloses(const ThickPoint& p) const; // deprecated + bool is_unbounded() const; const BoolInterval contains(const ThickPoint& p) const; - const BoolInterval contains(const Vector& p) const; - BoolInterval intersects(const IntervalVector& x) const; + const BoolInterval encloses(const ThickPoint& p) const; + const BoolInterval intersects(const ConvexPolygon& p) const; /// @} /// \name Setting values diff --git a/src/core/geometry/codac_Polygon.cpp b/src/core/geometry/codac_Polygon.cpp index a1cb1689f..eaf139a69 100644 --- a/src/core/geometry/codac_Polygon.cpp +++ b/src/core/geometry/codac_Polygon.cpp @@ -23,7 +23,6 @@ namespace codac // Definition Polygon::Polygon() - : Polygon(IntervalVector(2,Interval(-9999999999.,9999999999.))) // unbounded polygons are not supported yet { } @@ -33,21 +32,13 @@ namespace codac { } - - Polygon::Polygon(const IntervalVector& box) - { - assert(box.size() == 2); - assert(!box.is_empty()); - - ThickPoint::push(box, m_v_floating_pts); - } Polygon::Polygon(const vector& v_floating_pts) : m_v_floating_pts(v_floating_pts) { } - + void Polygon::set_empty() { m_v_floating_pts.clear(); @@ -95,19 +86,12 @@ namespace codac { IntervalVector box(2, Interval::EMPTY_SET); for(const auto& pt : m_v_floating_pts) - { - if(isinf(pt[0]) || isinf(pt[1])) - return IntervalVector(2); // todo: correct this, issue #87 box |= pt; - } return box; } const ThickPoint Polygon::center() const { - if(is_empty()) - return ThickPoint(); // undefined point - IntervalVector center(2, 0.); for(const auto& pt : m_v_floating_pts) center += pt; @@ -117,9 +101,6 @@ namespace codac const Interval Polygon::area() const { - if(is_empty()) - return 0.; - Interval a(0.); size_t n = m_v_floating_pts.size(); @@ -134,6 +115,11 @@ namespace codac return 0.5*a; } + double Polygon::volume() const + { + return area().ub(); + } + // Tests diff --git a/src/core/geometry/codac_Polygon.h b/src/core/geometry/codac_Polygon.h index fb7e4db7e..c7cd45406 100644 --- a/src/core/geometry/codac_Polygon.h +++ b/src/core/geometry/codac_Polygon.h @@ -28,9 +28,7 @@ namespace codac Polygon(); Polygon(const Polygon& p); - explicit Polygon(const IntervalVector& box); Polygon(const std::vector& v_floating_pts); - void set_empty(); /// @} @@ -46,6 +44,7 @@ namespace codac const IntervalVector box() const; const ThickPoint center() const; const Interval area() const; + double volume() const; /// @} /// \name Tests diff --git a/src/core/geometry/codac_SepPolygon.h b/src/core/geometry/codac_SepPolygon.h index a1453428c..8b466e254 100644 --- a/src/core/geometry/codac_SepPolygon.h +++ b/src/core/geometry/codac_SepPolygon.h @@ -74,7 +74,7 @@ class SepPolygon : public ibex::SepBoundaryCtc { * The polygon boundary contractor is composed of a union of * contractor on segments (CtcSegment). * This contractor is minimal as an union of minimal contractors. - * See ibex::SepBoundaryCtc. + * See #ibex::SepBoundaryCtc. * * \param points list of segments representing the edges of the polygon in the format of ( ((a1_x, a1_y), (b1_x, b1_x)), ((a2_x, a2_y), (b2_x, b2_x)), ...) */ @@ -89,7 +89,7 @@ class SepPolygon : public ibex::SepBoundaryCtc { * The polygon boundary contractor is composed of a union of * contractor on segments (CtcSegment). * This contractor is minimal as an union of minimal contractors. - * See ibex::SepBoundaryCtc. + * See #ibex::SepBoundaryCtc. * * \param vertices list of vertices on the form (ax, ay), (bx, by), (cx, cy), ... */ @@ -104,7 +104,7 @@ class SepPolygon : public ibex::SepBoundaryCtc { * The polygon boundary contractor is composed of a union of * contractor on segments (CtcSegment). * This contractor is minimal as an union of minimal contractors. - * See ibex::SepBoundaryCtc. + * See #ibex::SepBoundaryCtc. * * \param ax list of x coordinate of the first point of each segment * \param ay list of y coordinate of the first point of each segment diff --git a/src/core/geometry/codac_ThickEdge.cpp b/src/core/geometry/codac_ThickEdge.cpp index 5d4454f74..d74029590 100644 --- a/src/core/geometry/codac_ThickEdge.cpp +++ b/src/core/geometry/codac_ThickEdge.cpp @@ -159,10 +159,7 @@ namespace codac vector v_box_edges; ThickEdge::push(x, v_box_edges); for(size_t i = 0 ; i < v_box_edges.size() ; i++) - { - assert(!does_not_exist() && !v_box_edges[i].does_not_exist()); inter |= (*this & v_box_edges[i]).box(); - } return inter; } } @@ -173,7 +170,6 @@ namespace codac const ThickPoint ThickEdge::operator&(const ThickEdge& e) const { - assert(!does_not_exist() && !e.does_not_exist()); const ThickPoint proj = proj_intersection(*this, e); return ThickPoint(proj[0] & box()[0] & e.box()[0], proj[1] & box()[1] & e.box()[1]); } diff --git a/src/core/separators/codac_SepProj.cpp b/src/core/separators/codac_SepProj.cpp index cf3f2df49..54a9d9965 100644 --- a/src/core/separators/codac_SepProj.cpp +++ b/src/core/separators/codac_SepProj.cpp @@ -56,6 +56,8 @@ bool SepProj::process(IntervalVector& x_in, IntervalVector& x_out, IntervalVecto // Separate the product [x].[y] IntervalVector XinFull = cart_prod(x, y); IntervalVector XoutFull = cart_prod(x, y); + //IntervalVector _XinFull = cart_prod(x, y); + //IntervalVector _XoutFull = cart_prod(x, y); // IntervalVector XinFull0(XinFull); // IntervalVector XoutFull0(XoutFull); @@ -79,7 +81,9 @@ bool SepProj::process(IntervalVector& x_in, IntervalVector& x_out, IntervalVecto // Handle error case if (XinFull.is_empty() && XoutFull.is_empty()){ - cout << "Erreur !!!! line" << __LINE__ << "\n "; + cout << "Error: XinFull.is_empty() && XoutFull.is_empty()" << endl; + cout << " found on codac_SepProj.cpp" << endl; + //cout << " " << x << ", " << y << ", " << _XinFull << ", " << _XoutFull << endl; exit(-1); } @@ -98,11 +102,11 @@ bool SepProj::process(IntervalVector& x_in, IntervalVector& x_out, IntervalVecto x_out.set_empty(); if( use_point == false) impact.setCoutFlags(x_out, x); - y.set_empty(); +// y.set_empty(); return true; } else { x_out = XoutFull.subvector(0, x_out.size()-1); - y = XoutFull.subvector(x_out.size(), XoutFull.size()-1); +// y = XoutFull.subvector(x_out.size(), XoutFull.size()-1); if( use_point == false) impact.setCoutFlags(x_out, x); } diff --git a/tests/core/CMakeLists.txt b/tests/core/CMakeLists.txt index bc3a2da73..9bf1484bf 100644 --- a/tests/core/CMakeLists.txt +++ b/tests/core/CMakeLists.txt @@ -26,7 +26,7 @@ list(APPEND SRC_TESTS ${CMAKE_CURRENT_SOURCE_DIR}/main.cpp #${CMAKE_CURRENT_SOURCE_DIR}/tests_integration.cpp #${CMAKE_CURRENT_SOURCE_DIR}/tests_operators.cpp #${CMAKE_CURRENT_SOURCE_DIR}/tests_geometry.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_polygons.cpp + #${CMAKE_CURRENT_SOURCE_DIR}/tests_polygons.cpp #${CMAKE_CURRENT_SOURCE_DIR}/tests_serialization.cpp #${CMAKE_CURRENT_SOURCE_DIR}/tests_slices_structure.cpp #${CMAKE_CURRENT_SOURCE_DIR}/tests_trajectory.cpp diff --git a/tests/core/tests_codac2_tubes.cpp b/tests/core/tests_codac2_tubes.cpp index 10459aa97..4399f585b 100644 --- a/tests/core/tests_codac2_tubes.cpp +++ b/tests/core/tests_codac2_tubes.cpp @@ -28,9 +28,10 @@ TEST_CASE("Test codac2::tubes") { SECTION("Test TDomain") { - auto tdomain = create_tdomain(Interval(0,1), 0.5); + auto tdomain = create_tdomain(); + tdomain->sample(Interval(0,1), 0.5); CHECK(tdomain->nb_tslices() == 4); - CHECK(tdomain->t0_tf() == Interval(0,1)); + CHECK(tdomain->t0_tf() == Interval(-oo,oo)); const list& tslices = tdomain->tslices(); vector vector_tslices{ @@ -60,27 +61,27 @@ TEST_CASE("Test codac2::tubes") CHECK(tdomain->nb_tubes() == 1); } - SECTION("Test degenerated TDomain") - { - auto tdomain = create_tdomain(Interval(1), 0.5); - CHECK(tdomain->nb_tslices() == 2); - CHECK(tdomain->t0_tf() == Interval(1)); - CHECK(tdomain->nb_tubes() == 0); - - const list& tslices = tdomain->tslices(); - vector vector_tslices{ - make_move_iterator(tslices.begin()), - make_move_iterator(tslices.end()) }; - - CHECK(vector_tslices.size() == 2); - CHECK(vector_tslices[0].t0_tf() == Interval(-oo,1)); - CHECK(vector_tslices[1].t0_tf() == Interval(1,oo)); - } + // should assert SECTION("Test degenerated TDomain") + // should assert { + // should assert auto tdomain = create_tdomain(Interval(1), 0.5); + // should assert CHECK(tdomain->nb_tslices() == 2); + // should assert CHECK(tdomain->t0_tf() == Interval(1)); + // should assert CHECK(tdomain->nb_tubes() == 0); + // should assert + // should assert const list& tslices = tdomain->tslices(); + // should assert vector vector_tslices{ + // should assert make_move_iterator(tslices.begin()), + // should assert make_move_iterator(tslices.end()) }; + // should assert + // should assert CHECK(vector_tslices.size() == 2); + // should assert CHECK(vector_tslices[0].t0_tf() == Interval(-oo,1)); + // should assert CHECK(vector_tslices[1].t0_tf() == Interval(1,oo)); + // should assert } SECTION("Test TDomain with gates") { auto tdomain = create_tdomain(Interval(0,1), 0.5, true); - CHECK(tdomain->nb_tslices() == 7); + CHECK(tdomain->nb_tslices() == 5); CHECK(tdomain->t0_tf() == Interval(0,1)); CHECK(tdomain->nb_tubes() == 0); @@ -89,32 +90,30 @@ TEST_CASE("Test codac2::tubes") make_move_iterator(tslices.begin()), make_move_iterator(tslices.end()) }; - CHECK(vector_tslices.size() == 7); - CHECK(vector_tslices[0].t0_tf() == Interval(-oo,0)); - CHECK(vector_tslices[1].t0_tf() == Interval(0)); - CHECK(vector_tslices[2].t0_tf() == Interval(0,0.5)); - CHECK(vector_tslices[3].t0_tf() == Interval(0.5)); - CHECK(vector_tslices[4].t0_tf() == Interval(0.5,1)); - CHECK(vector_tslices[5].t0_tf() == Interval(1,1)); - CHECK(vector_tslices[6].t0_tf() == Interval(1,oo)); - - CHECK(tdomain->iterator_tslice(-oo)->t0_tf() == Interval(-oo,0)); - CHECK(tdomain->iterator_tslice(-10.)->t0_tf() == Interval(-oo,0)); + CHECK(vector_tslices.size() == 5); + CHECK(vector_tslices[0].t0_tf() == Interval(0)); + CHECK(vector_tslices[1].t0_tf() == Interval(0,0.5)); + CHECK(vector_tslices[2].t0_tf() == Interval(0.5)); + CHECK(vector_tslices[3].t0_tf() == Interval(0.5,1)); + CHECK(vector_tslices[4].t0_tf() == Interval(1,1)); + CHECK(tdomain->iterator_tslice(0.)->t0_tf() == Interval(0)); + CHECK(tdomain->iterator_tslice(0.1)->t0_tf() == Interval(0,0.5)); + CHECK(tdomain->iterator_tslice(0.5)->t0_tf() == Interval(0.5)); CHECK(tdomain->iterator_tslice(0.6)->t0_tf() == Interval(0.5,1)); - CHECK(tdomain->iterator_tslice(5540.2)->t0_tf() == Interval(1,oo)); - CHECK(tdomain->iterator_tslice(oo)->t0_tf() == Interval(1,oo)); + CHECK(tdomain->iterator_tslice(1.)->t0_tf() == Interval(1)); } SECTION("Test TDomain with sampling") { - auto tdomain = create_tdomain(Interval(1), 0.5); + auto tdomain = create_tdomain(); + tdomain->sample(1.); CHECK(tdomain->nb_tslices() == 2); - tdomain->sample(10.); + tdomain->sample(10.,false); // no gate CHECK(tdomain->nb_tslices() == 3); - tdomain->sample(10.); // second sampling which creates a gate + tdomain->sample(10.,true); // second sampling with gate CHECK(tdomain->nb_tslices() == 4); - tdomain->sample(10.); // no more action + tdomain->sample(10.,true); // no more action CHECK(tdomain->nb_tslices() == 4); const list& tslices = tdomain->tslices(); @@ -132,11 +131,12 @@ TEST_CASE("Test codac2::tubes") SECTION("Test unbounded TDomain") { auto tdomain = create_tdomain(); + CHECK(tdomain->nb_tslices() == 1); + CHECK(tdomain->t0_tf() == Interval(-oo,oo)); const list& tslices = tdomain->tslices(); vector vector_tslices{ make_move_iterator(tslices.begin()), make_move_iterator(tslices.end()) }; - CHECK(tdomain->nb_tslices() == 1); CHECK(vector_tslices.size() == 1); CHECK(vector_tslices[0].t0_tf() == Interval(-oo,oo)); } @@ -145,42 +145,116 @@ TEST_CASE("Test codac2::tubes") { auto tdomain = create_tdomain(); CHECK(tdomain->nb_tslices() == 1); + CHECK(tdomain->t0_tf() == Interval(-oo,oo)); Tube x(tdomain, IntervalVector(1)); - x.set(Interval(4)); - x(Interval(0,1)) = IntervalVector({{1,2}}); - x(Interval(1,2)) = IntervalVector({{5,6}}); - x(Interval(2,3)) = IntervalVector({{8,9}}); - CHECK(x.codomain() == IntervalVector({{1,9}})); - CHECK(static_cast(x(0.5)) == IntervalVector({{1,2}})); - CHECK(static_cast(x(1.5)) == IntervalVector({{5,6}})); - CHECK(static_cast(x(2.5)) == IntervalVector({{8,9}})); + //x.set(Interval(4)); + x(Interval(0,1)) = IntervalVector({{1,5}}); + x(Interval(1,2)) = IntervalVector({{2,8}}); + x(Interval(2,3)) = IntervalVector({{6,9}}); + // Checking structure + vector*> v; + for(const auto& s : x) + v.push_back(&s); + CHECK(v[0]->t0_tf() == Interval(-oo,0)); + CHECK(v[0]->codomain() == IntervalVector({{-oo,oo}})); + CHECK(v[1]->t0_tf() == Interval(0,1)); + CHECK(v[1]->codomain() == IntervalVector({{1,5}})); + CHECK(v[2]->t0_tf() == Interval(1,2)); + CHECK(v[2]->codomain() == IntervalVector({{2,8}})); + CHECK(v[3]->t0_tf() == Interval(2,3)); + CHECK(v[3]->codomain() == IntervalVector({{6,9}})); + CHECK(v[4]->t0_tf() == Interval(3,oo)); + CHECK(v[4]->codomain() == IntervalVector({{-oo,oo}})); + + CHECK(tdomain->nb_tslices() == 5); // with [-oo,0] and [3,oo] + CHECK(static_cast(x(Interval(0,3))) == IntervalVector({{1,9}})); + CHECK(static_cast(x(-1)) == IntervalVector(1)); + CHECK(static_cast(x(0.5)) == IntervalVector({{1,5}})); + CHECK(static_cast(x(1.5)) == IntervalVector({{2,8}})); + CHECK(static_cast(x(2.5)) == IntervalVector({{6,9}})); + // No gates: testing values between slices + CHECK(static_cast(x(1.)) == IntervalVector({{2,5}})); + CHECK(static_cast(x(2.)) == IntervalVector({{6,8}})); + CHECK(static_cast(x(3.)) == IntervalVector({{6,9}})); + CHECK(static_cast(x(999.)) == IntervalVector(1)); const std::shared_ptr> s0 = x.first_slice_ptr(); CHECK(s0->t0_tf() == Interval(-oo,0)); - CHECK(s0->codomain() == IntervalVector({{4}})); + CHECK(s0->codomain() == IntervalVector({{-oo,oo}})); const std::shared_ptr> s1 = s0->next_slice_ptr(); CHECK(s1->t0_tf() == Interval(0,1)); - CHECK(s1->codomain() == IntervalVector({{1,2}})); + CHECK(s1->codomain() == IntervalVector({{1,5}})); const std::shared_ptr> s2 = s1->next_slice_ptr(); CHECK(s2->t0_tf() == Interval(1,2)); - CHECK(s2->codomain() == IntervalVector({{5,6}})); + CHECK(s2->codomain() == IntervalVector({{2,8}})); const std::shared_ptr> s3 = s2->next_slice_ptr(); CHECK(s3->t0_tf() == Interval(2,3)); - CHECK(s3->codomain() == IntervalVector({{8,9}})); + CHECK(s3->codomain() == IntervalVector({{6,9}})); const std::shared_ptr> s4 = s3->next_slice_ptr(); CHECK(s4->t0_tf() == Interval(3,oo)); - CHECK(s4->codomain() == IntervalVector({{4}})); + CHECK(s4->codomain() == IntervalVector({{-oo,oo}})); CHECK(tdomain->nb_tslices() == 5); tdomain->sample(1.3); CHECK(tdomain->nb_tslices() == 6); CHECK(s2->t0_tf() == Interval(1,1.3)); - CHECK(s2->codomain() == IntervalVector({{5,6}})); + CHECK(s2->codomain() == IntervalVector({{2,8}})); const std::shared_ptr> s2bis = s2->next_slice_ptr(); CHECK(s2bis->t0_tf() == Interval(1.3,2)); - CHECK(s2bis->codomain() == IntervalVector({{5,6}})); + CHECK(s2bis->codomain() == IntervalVector({{2,8}})); CHECK(s3->t0_tf() == Interval(2,3)); - CHECK(s3->codomain() == IntervalVector({{8,9}})); + CHECK(s3->codomain() == IntervalVector({{6,9}})); + } + + SECTION("Sampling inside tdomain") + { + auto tdomain = create_tdomain(); + CHECK(tdomain->t0_tf() == Interval(-oo,oo)); + CHECK(tdomain->nb_tslices() == 1); + tdomain->sample(1., false); + CHECK(tdomain->t0_tf() == Interval(-oo,oo)); + CHECK(tdomain->nb_tslices() == 2); + tdomain->sample(1., false); + CHECK(tdomain->t0_tf() == Interval(-oo,oo)); + CHECK(tdomain->nb_tslices() == 2); + tdomain->sample(1., true); + CHECK(tdomain->t0_tf() == Interval(-oo,oo)); + CHECK(tdomain->nb_tslices() == 3); + std::list::iterator it = tdomain->sample(10., true); + CHECK(tdomain->t0_tf() == Interval(-oo,oo)); + CHECK(tdomain->nb_tslices() == 5); + CHECK(it->t0_tf() == Interval(10.)); + it = tdomain->sample(15., false); + CHECK(tdomain->t0_tf() == Interval(-oo,oo)); + CHECK(tdomain->nb_tslices() == 6); + CHECK(it->t0_tf() == Interval(15.,oo)); + } + + SECTION("Sampling outside tdomain") + { + auto tdomain = create_tdomain(Interval(0,0.5)); + CHECK(tdomain->t0_tf() == Interval(0,0.5)); + CHECK(tdomain->nb_tslices() == 1); + tdomain->sample(1., false); + CHECK(tdomain->t0_tf() == Interval(0,1)); + CHECK(tdomain->nb_tslices() == 2); + tdomain->sample(1., false); + CHECK(tdomain->t0_tf() == Interval(0,1)); + CHECK(tdomain->nb_tslices() == 2); // + tdomain->sample(1., false); + CHECK(tdomain->t0_tf() == Interval(0,1)); + CHECK(tdomain->nb_tslices() == 2); + tdomain->sample(1., true); + CHECK(tdomain->t0_tf() == Interval(0,1)); + CHECK(tdomain->nb_tslices() == 3); + std::list::iterator it = tdomain->sample(10., true); + CHECK(tdomain->t0_tf() == Interval(0,10)); + CHECK(tdomain->nb_tslices() == 5); + CHECK(it->t0_tf() == Interval(10.)); + it = tdomain->sample(15., false); + CHECK(tdomain->t0_tf() == Interval(0,15)); + CHECK(tdomain->nb_tslices() == 6); + CHECK(it->t0_tf() == Interval(10,15)); } SECTION("Test basic Tube") @@ -192,9 +266,9 @@ TEST_CASE("Test codac2::tubes") CHECK(x.tdomain() == tdomain); CHECK(x.t0_tf() == Interval(0,1)); CHECK(x.nb_slices() == tdomain->nb_tslices()); - CHECK(x.nb_slices() == 12); - CHECK(x.first_slice_ptr()->t0_tf() == Interval(-oo,0)); - CHECK(x.last_slice_ptr()->t0_tf() == Interval(1,oo)); + CHECK(x.nb_slices() == 10); + CHECK(x.first_slice_ptr()->t0_tf() == Interval(0,0.1)); + CHECK(ApproxIntv(x.last_slice_ptr()->t0_tf()) == Interval(0.9,1)); CHECK(x.codomain() == IntervalVector(3)); x.set(IntervalVector(3, Interval(-10,10))); CHECK(x.codomain() == IntervalVector(3, Interval(-10,10))); @@ -212,25 +286,52 @@ TEST_CASE("Test codac2::tubes") // Eval CHECK(tdomain->nb_tubes() == 1); - CHECK(static_cast(x(Interval(-oo,oo))) == x.codomain()); - CHECK(static_cast(x(Interval(-1,1))) == x.codomain()); - CHECK(static_cast(x(-42.)) == x.codomain()); + CHECK(static_cast(x(Interval(-oo,oo))) == IntervalVector(3)); + CHECK(static_cast(x(Interval(-1,1))) == IntervalVector(3)); + CHECK(static_cast(x(tdomain->t0_tf())) == x.codomain()); + CHECK(static_cast(x(-42.)) == IntervalVector(3)); // Eval: affectation at scalar t - CHECK(tdomain->nb_tslices() == 12); + CHECK(tdomain->nb_tslices() == 10); x(-42.) = IntervalVector(3,Interval(2.,3.)); - CHECK(tdomain->nb_tslices() == 14); + CHECK(tdomain->nb_tslices() == 12); + + // Checking structure + vector*> v; + for(const auto& s : x) + v.push_back(&s); + CHECK(v[0]->t0_tf() == Interval(-42.)); + CHECK(v[0]->codomain() == IntervalVector(3,Interval(2.,3.))); + CHECK(v[1]->t0_tf() == Interval(-42.,0.)); + CHECK(v[1]->codomain() == IntervalVector(3)); + CHECK(v[2]->t0_tf() == Interval(0.,0.1)); + CHECK(v[2]->codomain() == IntervalVector({Interval(-20,20),Interval(-20,20),Interval(-10,10)})); + CHECK(v[3]->t0_tf() == Interval(0.1,0.2)); + CHECK(v[3]->codomain() == IntervalVector({Interval(-20,20),Interval(-20,20),Interval(-10,10)})); + CHECK(static_cast(x(-42.)) == IntervalVector(3,Interval(2.,3.))); - CHECK(static_cast(x(ibex::previous_float(-42.))) == IntervalVector({Interval(-20,20),Interval(-20,20),Interval(-10,10)})); - CHECK(static_cast(x(ibex::next_float(-42.))) == IntervalVector({Interval(-20,20),Interval(-20,20),Interval(-10,10)})); + CHECK(static_cast(x(ibex::previous_float(-42.))) == IntervalVector(3)); + CHECK(static_cast(x(ibex::next_float(-42.))) == IntervalVector(3)); // Eval: affectation at interval t - CHECK(tdomain->nb_tslices() == 14); + CHECK(x.codomain() == IntervalVector(3)); + CHECK(tdomain->nb_tslices() == 12); x(Interval(44,55)) = IntervalVector(3,Interval(9.,10.)); - CHECK(tdomain->nb_tslices() == 16); + CHECK(tdomain->nb_tslices() == 14); + + v.clear(); + for(const auto& s : x) + v.push_back(&s); + CHECK(ApproxIntv(v[11]->t0_tf()) == Interval(0.9,1)); + CHECK(v[11]->codomain() == IntervalVector({Interval(-20,20),Interval(-20,20),Interval(-10,10)})); + CHECK(v[12]->t0_tf() == Interval(1,44)); + CHECK(v[12]->codomain() == IntervalVector(3)); + CHECK(v[13]->t0_tf() == Interval(44,55)); + CHECK(v[13]->codomain() == IntervalVector(3,Interval(9.,10.))); + CHECK(static_cast(x(Interval(44,55))) == IntervalVector(3,Interval(9.,10.))); - CHECK(static_cast(x(ibex::previous_float(44.))) == IntervalVector({Interval(-20,20),Interval(-20,20),Interval(-10,10)})); - CHECK(static_cast(x(ibex::next_float(55.))) == IntervalVector({Interval(-20,20),Interval(-20,20),Interval(-10,10)})); + CHECK(static_cast(x(ibex::previous_float(44.))) == IntervalVector(3)); + CHECK(static_cast(x(ibex::next_float(55.))) == IntervalVector(3)); // Iterators tests { @@ -258,9 +359,11 @@ TEST_CASE("Test codac2::tubes") { auto tdomain = create_tdomain(Interval(0,1), 0.1); Tube x(tdomain, IntervalVector(2)); - CHECK(x.nb_slices() == 12); - CHECK(x.first_slice_ptr() == tdomain->iterator_tslice(-oo)->_slices.at(&x)); - CHECK(x.last_slice_ptr() == tdomain->iterator_tslice(oo)->_slices.at(&x)); + CHECK(x.nb_slices() == 10); + CHECK(tdomain->iterator_tslice(-oo) == tdomain->_tslices.end()); + CHECK(tdomain->iterator_tslice(oo) == tdomain->_tslices.end()); + CHECK(x.first_slice_ptr() == tdomain->iterator_tslice(0.)->_slices.at(&x)); + CHECK(x.last_slice_ptr() == tdomain->iterator_tslice(1.)->_slices.at(&x)); for(auto& s : x) s.set(IntervalVector(2,s.t0_tf())); @@ -269,22 +372,16 @@ TEST_CASE("Test codac2::tubes") for(const auto& s : x) v.push_back(&s); - CHECK(v[0]->t0_tf() == Interval(-oo,0)); - CHECK(v[0]->codomain() == IntervalVector(2,Interval(-oo,0))); - CHECK(v[0]->input_gate() == IntervalVector(2,Interval(-oo,0))); - CHECK(v[0]->output_gate() == IntervalVector(2,Interval(0))); - - CHECK(v[1]->t0_tf() == Interval(0,0.1)); - CHECK(v[1]->input_gate() == v[0]->output_gate()); - CHECK(v[1]->codomain() == IntervalVector(2,Interval(0,0.1))); - CHECK(v[1]->input_gate() == IntervalVector(2,Interval(0))); - CHECK(v[1]->output_gate() == IntervalVector(2,Interval(0.1))); - - CHECK(v[11]->t0_tf() == Interval(1,oo)); - CHECK(v[11]->input_gate() == v[10]->output_gate()); - CHECK(v[11]->codomain() == IntervalVector(2,Interval(1,oo))); - CHECK(v[11]->input_gate() == IntervalVector(2,Interval(1))); - CHECK(v[11]->output_gate() == IntervalVector(2,Interval(1,oo))); + CHECK(v[0]->t0_tf() == Interval(0,0.1)); + CHECK(v[0]->input_gate() == IntervalVector(2,Interval(0.,0.1))); // nothing before + CHECK(v[0]->codomain() == IntervalVector(2,Interval(0,0.1))); + CHECK(v[0]->output_gate() == IntervalVector(2,Interval(0.1))); + + CHECK(ApproxIntv(v[9]->t0_tf()) == Interval(0.9,1.)); + CHECK(v[9]->input_gate() == v[8]->output_gate()); + CHECK(ApproxIntvVector(v[9]->codomain()) == IntervalVector(2,Interval(0.9,1.))); + CHECK(ApproxIntvVector(v[9]->input_gate()) == IntervalVector(2,Interval(0.9))); + CHECK(ApproxIntvVector(v[9]->output_gate()) == IntervalVector(2,Interval(0.9,1.))); // nothing after } SECTION("Test again 1") @@ -347,7 +444,7 @@ TEST_CASE("Test codac2::tubes") { auto tdomain = create_tdomain(Interval(0,1), 0.1); - x = new Tube(tdomain); + x = new Tube(tdomain, Interval()); CHECK(x->tdomain() == tdomain); } @@ -366,20 +463,92 @@ TEST_CASE("Test codac2::tubes") SECTION("Reverse iterator") { auto tdomain = create_tdomain(Interval(0,1), 0.5); - Tube x(tdomain); + Tube x(tdomain, Interval()); auto it1 = x.begin(); - CHECK(it1->t0_tf() == Interval(-oo,0)); ++it1; CHECK(it1->t0_tf() == Interval(0,0.5)); ++it1; CHECK(it1->t0_tf() == Interval(0.5,1)); ++it1; - CHECK(it1->t0_tf() == Interval(1,oo)); ++it1; CHECK(it1 == x.end()); auto it2 = x.rbegin(); - CHECK(it2->t0_tf() == Interval(1,oo)); ++it2; CHECK(it2->t0_tf() == Interval(0.5,1)); ++it2; CHECK(it2->t0_tf() == Interval(0,0.5)); ++it2; - CHECK(it2->t0_tf() == Interval(-oo,0)); ++it2; CHECK(it2 == x.rend()); } + + SECTION("Conversion of tubes: codac1-codac2") + { + codac::Tube codac1(Interval(0,10),1.,Interval(-3,6)); + auto tdomain = create_tdomain(Interval(0,10),1.,true); // with gates + codac2::Tube codac2(tdomain, Interval(-3,6)); + CHECK(to_codac1(codac2) == codac1); + CHECK(to_codac2(codac1) == codac2); + CHECK(to_codac1(to_codac2(codac1)) == codac1); + CHECK(to_codac2(to_codac1(codac2)) == codac2); + + codac::TubeVector codac1_vector(Interval(0,10),1.,IntervalVector({{-1,2},{6,8}})); + codac2::Tube codac2_vector(tdomain, IntervalVector({{-1,2},{6,8}})); + codac1_vector.set({{1.2,1.3},{6.8,6.9}}, 0.58); + codac2_vector.set({{1.2,1.3},{6.8,6.9}}, 0.58); + codac1_vector.set({{1.6},{7.2}}, 10.); + codac2_vector.set({{1.6},{7.2}}, 10.); + CHECK(tdomain->t0_tf().ub() == 10.); + CHECK(codac1_vector(0.58) == IntervalVector({{1.2,1.3},{6.8,6.9}})); + CHECK(IntervalVector(codac2_vector(0.58)) == IntervalVector({{1.2,1.3},{6.8,6.9}})); + + CHECK(to_codac1(codac2_vector) == codac1_vector); + CHECK(to_codac2(codac1_vector) == codac2_vector); + CHECK(to_codac1(to_codac2(codac1_vector)) == codac1_vector); + CHECK(to_codac2(to_codac1(codac2_vector)) == codac2_vector); + + codac::TubeVector to_codac1_codac2_vector = to_codac1(codac2_vector); + codac2::Tube to_codac2_codac1_vector = to_codac2(codac1_vector); + // t=0.58 + CHECK(to_codac1_codac2_vector(ibex::previous_float(0.58)) == IntervalVector({{-1,2},{6,8}})); + CHECK(to_codac1_codac2_vector(0.58) == IntervalVector({{1.2,1.3},{6.8,6.9}})); + CHECK(to_codac1_codac2_vector(ibex::next_float(0.58)) == IntervalVector({{-1,2},{6,8}})); + CHECK(IntervalVector(to_codac2_codac1_vector(ibex::previous_float(0.58))) == IntervalVector({{-1,2},{6,8}})); + CHECK(IntervalVector(to_codac2_codac1_vector(0.58)) == IntervalVector({{1.2,1.3},{6.8,6.9}})); + CHECK(IntervalVector(to_codac2_codac1_vector(ibex::next_float(0.58))) == IntervalVector({{-1,2},{6,8}})); + // t=10 + CHECK(to_codac1_codac2_vector(ibex::previous_float(10.)) == IntervalVector({{-1,2},{6,8}})); + CHECK(to_codac1_codac2_vector(10.) == IntervalVector({{1.6},{7.2}})); + CHECK(to_codac1_codac2_vector(ibex::next_float(10.)) == IntervalVector(2)); + CHECK(IntervalVector(to_codac2_codac1_vector(ibex::previous_float(10.))) == IntervalVector({{-1,2},{6,8}})); + CHECK(IntervalVector(to_codac2_codac1_vector(10.)) == IntervalVector({{1.6},{7.2}})); + CHECK(IntervalVector(to_codac2_codac1_vector(ibex::next_float(10.))) == IntervalVector(2)); + } + + SECTION("Testing setting values") + { + auto tdomain = create_tdomain(Interval(0,10),1.,true); // with gates + Tube x(tdomain, Interval(-10,10)); + CHECK(x.codomain() == Interval(-10,10)); + + std::list::iterator it = tdomain->tslices().begin(); + CHECK(it->t0_tf() == Interval(0)); + CHECK(x(it).codomain() == Interval(-10,10)); + it++; + CHECK(it->t0_tf() == Interval(0,1)); + CHECK(x(it).codomain() == Interval(-10,10)); + it++; + CHECK(it->t0_tf() == Interval(1,1)); + CHECK(x(it).codomain() == Interval(-10,10)); + it = tdomain->tslices().begin(); + it++; + x(it).set(Interval(2,8)); + CHECK(x(it).codomain() == Interval(2,8)); + CHECK(x(std::prev(it)).codomain() == Interval(2,8)); + CHECK(x(std::next(it)).codomain() == Interval(2,8)); + CHECK(x(std::next(std::next(it))).codomain() == Interval(-10,10)); + CHECK(x.codomain() == Interval(-10,10)); + it++; it++; it++; it++; it++; + CHECK(it->t0_tf() == Interval(3)); + x(it).set(Interval(3,5)); + CHECK(x(it).codomain() == Interval(3,5)); + CHECK(x(std::prev(it)).codomain() == Interval(-10,10)); + CHECK(x(std::next(it)).codomain() == Interval(-10,10)); + CHECK(x(std::next(std::next(it))).codomain() == Interval(-10,10)); + CHECK(x.codomain() == Interval(-10,10)); + } } \ No newline at end of file diff --git a/tests/core/tests_polygons.cpp b/tests/core/tests_polygons.cpp index a0c95b513..8043dd22c 100644 --- a/tests/core/tests_polygons.cpp +++ b/tests/core/tests_polygons.cpp @@ -302,34 +302,34 @@ TEST_CASE("Polygons (intersections)") ConvexPolygon p(v_p); IntervalVector x(2), box_inter(2); - CHECK(p.encloses(ThickPoint(7.,7.)) == YES); - CHECK(p.encloses(ThickPoint(2.,7.)) == YES); - CHECK(p.encloses(ThickPoint(10.,3.)) == YES); - CHECK(p.encloses(ThickPoint(11.,9.)) == YES); - CHECK(p.encloses(ThickPoint(13.5,9.)) == YES); - CHECK(p.encloses(ThickPoint(5.8,12.2)) == YES); - - CHECK(p.encloses(ThickPoint(1.,4.)) == MAYBE); - CHECK(p.encloses(ThickPoint(3.,10.)) == MAYBE); - CHECK(p.encloses(ThickPoint(8.,14.)) == MAYBE); - CHECK(p.encloses(ThickPoint(2.,8.)) == MAYBE); - CHECK(p.encloses(ThickPoint(2.5,9.)) == MAYBE); - CHECK(p.encloses(ThickPoint(5.5,12.5)) == MAYBE); - CHECK(p.encloses(ThickPoint(1.,5.)) == MAYBE); - CHECK(p.encloses(ThickPoint(1.,1.)) == MAYBE); - - CHECK(p.encloses(ThickPoint(10.,2.)) == NO); - CHECK(p.encloses(ThickPoint(0.0,0.0)) == NO); - CHECK(p.encloses(ThickPoint(0.0,0.9)) == NO); - CHECK(p.encloses(ThickPoint(0.9,0.0)) == NO); - CHECK(p.encloses(ThickPoint(0.9,0.9)) == NO); - CHECK(p.encloses(ThickPoint(0.5,1.)) == NO); - CHECK(p.encloses(ThickPoint(5.2,12.8)) == NO); - CHECK(p.encloses(ThickPoint(1.,14.)) == NO); - CHECK(p.encloses(ThickPoint(1.,13.5)) == NO); - CHECK(p.encloses(ThickPoint(14.,1.)) == NO); - CHECK(p.encloses(ThickPoint(14.,14.)) == NO); - CHECK(p.encloses(ThickPoint(5.,14.)) == NO); + CHECK(p.contains(ThickPoint(7.,7.)) == YES); + CHECK(p.contains(ThickPoint(2.,7.)) == YES); + CHECK(p.contains(ThickPoint(10.,3.)) == YES); + CHECK(p.contains(ThickPoint(11.,9.)) == YES); + CHECK(p.contains(ThickPoint(13.5,9.)) == YES); + CHECK(p.contains(ThickPoint(5.8,12.2)) == YES); + + CHECK(p.contains(ThickPoint(1.,4.)) == MAYBE); + CHECK(p.contains(ThickPoint(3.,10.)) == MAYBE); + CHECK(p.contains(ThickPoint(8.,14.)) == MAYBE); + CHECK(p.contains(ThickPoint(2.,8.)) == MAYBE); + CHECK(p.contains(ThickPoint(2.5,9.)) == MAYBE); + CHECK(p.contains(ThickPoint(5.5,12.5)) == MAYBE); + CHECK(p.contains(ThickPoint(1.,5.)) == MAYBE); + CHECK(p.contains(ThickPoint(1.,1.)) == MAYBE); + + CHECK(p.contains(ThickPoint(10.,2.)) == NO); + CHECK(p.contains(ThickPoint(0.0,0.0)) == NO); + CHECK(p.contains(ThickPoint(0.0,0.9)) == NO); + CHECK(p.contains(ThickPoint(0.9,0.0)) == NO); + CHECK(p.contains(ThickPoint(0.9,0.9)) == NO); + CHECK(p.contains(ThickPoint(0.5,1.)) == NO); + CHECK(p.contains(ThickPoint(5.2,12.8)) == NO); + CHECK(p.contains(ThickPoint(1.,14.)) == NO); + CHECK(p.contains(ThickPoint(1.,13.5)) == NO); + CHECK(p.contains(ThickPoint(14.,1.)) == NO); + CHECK(p.contains(ThickPoint(14.,14.)) == NO); + CHECK(p.contains(ThickPoint(5.,14.)) == NO); x[0] = Interval(0.,2.); x[1] = Interval(0.,2.); box_inter = p.fast_intersection(x); @@ -434,7 +434,7 @@ TEST_CASE("Polygons from Slice") // not supported anymore ConvexPolygon p(v_p); // not supported anymore IntervalVector x(2), box_inter(2); // not supported anymore - // not supported anymore CHECK(p.encloses(ThickPoint(3.5,8.))); + // not supported anymore CHECK(p.contains(ThickPoint(3.5,8.))); // not supported anymore // not supported anymore x[0] = Interval(0.5,4.); x[1] = Interval(-1.,1.); // not supported anymore box_inter = p & x; From 4841452db3fc3ae90e69f8a864fffdcceca9c333 Mon Sep 17 00:00:00 2001 From: SimonRohou Date: Mon, 16 Jan 2023 11:35:38 +0100 Subject: [PATCH 065/256] [sivia] graphical updates --- src/core/sivia/codac_sivia.cpp | 84 ++++++++++++++++++---------------- 1 file changed, 45 insertions(+), 39 deletions(-) diff --git a/src/core/sivia/codac_sivia.cpp b/src/core/sivia/codac_sivia.cpp index 38a887078..b97591f84 100644 --- a/src/core/sivia/codac_sivia.cpp +++ b/src/core/sivia/codac_sivia.cpp @@ -40,6 +40,20 @@ namespace codac if(display_result) { + // Some values in the desired color map may not have been defined by the user + // We select default colors in this case + + SetColorMap cm = DEFAULT_SET_COLOR_MAP; + + if(display_result) + { + if(color_map.find(SetValue::OUT) != color_map.end()) + cm[SetValue::OUT] = color_map.at(SetValue::OUT); + + if(color_map.find(SetValue::UNKNOWN) != color_map.end()) + cm[SetValue::UNKNOWN] = color_map.at(SetValue::UNKNOWN); + } + if(!_vibes_initialized) { _vibes_initialized = true; @@ -51,7 +65,9 @@ namespace codac if(!fig_name.empty()) vibes::selectFigure(fig_name); - vibes::drawBox(x0, vibesParams("figure", fig_name)); + vibes::drawBox(x0); + vibes::newGroup("boxes_out", cm.at(SetValue::OUT)); + vibes::newGroup("boxes_unknown", cm.at(SetValue::UNKNOWN)); } map n_boxes; @@ -65,26 +81,13 @@ namespace codac deque stack = { x0 }; int k = 0; - // Some values in the desired color map may not have been defined by the user - // We select default colors in this case - - SetColorMap cm = DEFAULT_SET_COLOR_MAP; - - if(display_result) - { - if(color_map.find(SetValue::OUT) != color_map.end()) - cm[SetValue::OUT] = color_map.at(SetValue::OUT); - - if(color_map.find(SetValue::UNKNOWN) != color_map.end()) - cm[SetValue::UNKNOWN] = color_map.at(SetValue::UNKNOWN); - } - clock_t t_start = clock(); while(!stack.empty()) { k++; - IntervalVector x = stack.front(); + IntervalVector *xx = new IntervalVector(stack.front()); + IntervalVector &x = *xx; stack.pop_front(); IntervalVector x_before_ctc(x); @@ -107,7 +110,7 @@ namespace codac { if(display_result) { - vibes::drawBox(o.subvector(0,1), cm.at(SetValue::OUT)); + vibes::drawBox(o.subvector(0,1), vibesParams("group", "boxes_out")); n_boxes[SetValue::OUT] ++; } @@ -128,7 +131,7 @@ namespace codac { if(display_result) { - vibes::drawBox(x_remaining.subvector(0,1), cm.at(SetValue::UNKNOWN)); + vibes::drawBox(x_remaining.subvector(0,1), vibesParams("group", "boxes_unknown")); n_boxes[SetValue::UNKNOWN] ++; } @@ -164,6 +167,23 @@ namespace codac if(display_result) { + // Some values in the desired color map may not have been defined by the user + // We select default colors in this case + + SetColorMap cm = DEFAULT_SET_COLOR_MAP; + + if(display_result) + { + if(color_map.find(SetValue::OUT) != color_map.end()) + cm[SetValue::OUT] = color_map.at(SetValue::OUT); + + if(color_map.find(SetValue::UNKNOWN) != color_map.end()) + cm[SetValue::UNKNOWN] = color_map.at(SetValue::UNKNOWN); + + if(color_map.find(SetValue::IN) != color_map.end()) + cm[SetValue::IN] = color_map.at(SetValue::IN); + } + if(!_vibes_initialized) { _vibes_initialized = true; @@ -175,7 +195,10 @@ namespace codac if(!fig_name.empty()) vibes::selectFigure(fig_name); - vibes::drawBox(x0, vibesParams("figure", fig_name)); + vibes::drawBox(x0); + vibes::newGroup("boxes_out", cm.at(SetValue::OUT)); + vibes::newGroup("boxes_unknown", cm.at(SetValue::UNKNOWN)); + vibes::newGroup("boxes_in", cm.at(SetValue::IN)); } map n_boxes; @@ -189,23 +212,6 @@ namespace codac deque stack = { x0 }; int k = 0; - // Some values in the desired color map may not have been defined by the user - // We select default colors in this case - - SetColorMap cm = DEFAULT_SET_COLOR_MAP; - - if(display_result) - { - if(color_map.find(SetValue::IN) != color_map.end()) - cm[SetValue::IN] = color_map.at(SetValue::IN); - - if(color_map.find(SetValue::OUT) != color_map.end()) - cm[SetValue::OUT] = color_map.at(SetValue::OUT); - - if(color_map.find(SetValue::UNKNOWN) != color_map.end()) - cm[SetValue::UNKNOWN] = color_map.at(SetValue::UNKNOWN); - } - clock_t t_start = clock(); while(!stack.empty()) @@ -241,7 +247,7 @@ namespace codac { if(display_result) { - vibes::drawBox(i, cm.at(SetValue::IN)); + vibes::drawBox(i.subvector(0,1), vibesParams("group", "boxes_in")); n_boxes[SetValue::IN] ++; } @@ -253,7 +259,7 @@ namespace codac { if(display_result) { - vibes::drawBox(o, cm.at(SetValue::OUT)); + vibes::drawBox(o.subvector(0,1), vibesParams("group", "boxes_out")); n_boxes[SetValue::OUT] ++; } @@ -274,7 +280,7 @@ namespace codac { if(display_result) { - vibes::drawBox(x_remaining.subvector(0,1), cm.at(SetValue::UNKNOWN)); + vibes::drawBox(x_remaining.subvector(0,1), vibesParams("group", "boxes_unknown")); n_boxes[SetValue::UNKNOWN] ++; } From acacca6273b413e1f5feb867c5a96aa76fd9966a Mon Sep 17 00:00:00 2001 From: SimonRohou Date: Mon, 16 Jan 2023 12:08:33 +0100 Subject: [PATCH 066/256] [tube] updates --- CMakeLists.txt | 1 - src/core/2/domains/tube/codac2_Slice.h | 42 +++++++++++++------ src/core/2/domains/tube/codac2_Tube.h | 52 +++++++++++++++++++---- tests/core/tests_codac2_tubes.cpp | 57 +++++++++++++++++++++++++- 4 files changed, 129 insertions(+), 23 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index bd2086804..024108b57 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -38,7 +38,6 @@ endif() endif() - ################################################################################ # Options for directories ################################################################################ diff --git a/src/core/2/domains/tube/codac2_Slice.h b/src/core/2/domains/tube/codac2_Slice.h index 4115faabc..ca136ca02 100644 --- a/src/core/2/domains/tube/codac2_Slice.h +++ b/src/core/2/domains/tube/codac2_Slice.h @@ -22,6 +22,7 @@ #include "codac2_AbstractSlice.h" #include "codac_BoolInterval.h" #include "codac_ConvexPolygon.h" +#include "codac_DynCtc.h" #define EPSILON_CONTAINS ibex::next_float(0.) * 1000. //!< epsilon limit of the contains() algorithm @@ -53,7 +54,13 @@ namespace codac2 } Slice(const Slice& s) : - AbstractSlice(s._tubevector, s._it_tslice), _codomain(s._codomain) + Slice(s, s._tubevector) + { + + } + + Slice(const Slice& s, const AbstractSlicedTube& tubevector) : + AbstractSlice(tubevector, s._it_tslice), _codomain(s._codomain) { } @@ -240,10 +247,9 @@ namespace codac2 } } - void set(const T& x) + void set(const T& x, bool propagate = true) { - if constexpr(!std::is_same::value) // 'if' to be removed with virtual set classes - { + if constexpr(!std::is_same::value) { // 'if' to be removed with virtual set classes assert((size_t)codomain().size() == size()); } @@ -251,8 +257,7 @@ namespace codac2 if(prev_slice_ptr()) { - if constexpr(!std::is_same::value) // 'if' to be removed with virtual set classes - { + if constexpr(!std::is_same::value) { // 'if' to be removed with virtual set classes assert((size_t)prev_slice_ptr()->codomain().size() == size()); } if(is_gate()) @@ -263,8 +268,7 @@ namespace codac2 if(next_slice_ptr()) { - if constexpr(!std::is_same::value) // 'if' to be removed with virtual set classes - { + if constexpr(!std::is_same::value) { // 'if' to be removed with virtual set classes assert((size_t)next_slice_ptr()->codomain().size() == size()); } if(is_gate()) @@ -272,12 +276,24 @@ namespace codac2 else if(next_slice_ptr()->is_gate()) next_slice_ptr()->_codomain &= _codomain; } + + if(propagate && is_empty()) + set_empty(true, codac::TimePropag::FORWARD | codac::TimePropag::BACKWARD); } - void set_empty() + void set_empty(bool propagate = true, codac::TimePropag t_propa = codac::TimePropag::FORWARD | codac::TimePropag::BACKWARD) { _codomain.set_empty(); - if(!is_gate()) + + if(propagate) + { + if(t_propa & codac::TimePropag::BACKWARD && prev_slice_ptr()) + prev_slice_ptr()->set_empty(true, codac::TimePropag::BACKWARD); + if(t_propa & codac::TimePropag::FORWARD && next_slice_ptr()) + next_slice_ptr()->set_empty(true, codac::TimePropag::FORWARD); + } + + else if(!is_gate()) { if(prev_slice_ptr() && prev_slice_ptr()->is_gate()) prev_slice_ptr()->set_empty(); @@ -288,10 +304,10 @@ namespace codac2 void set_unbounded() { - if constexpr(std::is_same::value) // 'if' to be removed with virtual set classes - _codomain = T(size()); - else + if constexpr(std::is_same::value || std::is_same::value) // 'if' to be removed with virtual set classes _codomain = T(); + else + _codomain = T(size()); } void set_component(size_t i, const Interval& xi) diff --git a/src/core/2/domains/tube/codac2_Tube.h b/src/core/2/domains/tube/codac2_Tube.h index 15bed3ed3..a37d04423 100644 --- a/src/core/2/domains/tube/codac2_Tube.h +++ b/src/core/2/domains/tube/codac2_Tube.h @@ -50,11 +50,12 @@ namespace codac2 // need to specify T (dim at least) } explicit Tube(const std::shared_ptr& tdomain, const TFnc& f) : - Tube(tdomain, T()) + Tube(tdomain, (std::is_same::value ? T() : T(f.image_dim()))) { assert(f.nb_var() == 0 && "function's inputs must be limited to system variable"); - if constexpr(std::is_same::value) + if constexpr(std::is_same::value) { assert(f.image_dim() == 1); + } for(auto& s : *this) { @@ -63,6 +64,9 @@ namespace codac2 else s.set(f.eval_vector(s.t0_tf())); + + if(s.is_empty()) + std::cout << "IS EMPTY: " << s << std::endl; } } @@ -84,7 +88,7 @@ namespace codac2 for(std::list::iterator it = _tdomain->_tslices.begin(); it != _tdomain->_tslices.end(); ++it) { - std::shared_ptr> s_ptr = std::make_shared>(x(it)); + std::shared_ptr> s_ptr = std::make_shared>(x(it), *this); it->_slices.insert(std::pair>>(this, s_ptr)); } } @@ -94,6 +98,18 @@ namespace codac2 for(auto& s : _tdomain->_tslices) s._slices.erase(this); } + + Tube& operator=(const Tube& x) + { + if(_tdomain != x._tdomain) + throw std::exception(); // todo: better exception + + for(auto it = _tdomain->_tslices.begin(); + it != _tdomain->_tslices.end(); ++it) + (*this)(it).set(x(it).codomain()); + + return *this; + } virtual Interval t0_tf() const { @@ -210,6 +226,12 @@ namespace codac2 return codomain; } + // Remove this? (direct access with () ) + std::shared_ptr> slice_ptr(const std::list::iterator& it) + { + return std::static_pointer_cast>(it->slices().at(this)); + } + Slice& operator()(const std::list::iterator& it) { return const_cast&>( @@ -244,7 +266,12 @@ namespace codac2 T eval(double t) const { if(!tdomain()->t0_tf().contains(t)) - return T(); // todo: case of dimension to specify? + { + if constexpr(std::is_same::value || std::is_same::value) + return T(); + else + return T(size()); + } std::list::iterator it_t = _tdomain->iterator_tslice(t); assert(it_t != _tdomain->_tslices.end()); T x = std::static_pointer_cast>(it_t->_slices.at(this))->codomain(); @@ -256,15 +283,22 @@ namespace codac2 T eval(const Interval& t) const { if(!tdomain()->t0_tf().is_superset(t)) - return T(size()); // todo: case of dimension to specify? + { + if constexpr(std::is_same::value || std::is_same::value) + return T(); + else + return T(size()); + } + if(t.is_degenerated()) return eval(t.lb()); std::list::iterator it = _tdomain->iterator_tslice(t.lb()); T codomain = std::static_pointer_cast>(it->_slices.at(this))->codomain(); - while(it != _tdomain->iterator_tslice(t.ub())) + while(it != std::next(_tdomain->iterator_tslice(t.ub()))) { + if(it->t0_tf().lb() == t.ub()) break; codomain |= std::static_pointer_cast>(it->_slices.at(this))->codomain(); it++; } @@ -274,8 +308,9 @@ namespace codac2 void set(const T& codomain) { - if constexpr(!std::is_same::value) + if constexpr(!std::is_same::value) { assert((size_t)codomain.size() == size()); + } for(auto& s : *this) if(!s.is_gate()) s.set(codomain); @@ -286,8 +321,9 @@ namespace codac2 void set(const T& codomain, double t) { - if constexpr(!std::is_same::value) + if constexpr(!std::is_same::value) { assert((size_t)codomain.size() == size()); + } std::list::iterator it = _tdomain->sample(t,true); (*this)(it).set(codomain); } diff --git a/tests/core/tests_codac2_tubes.cpp b/tests/core/tests_codac2_tubes.cpp index 4399f585b..18bc6659d 100644 --- a/tests/core/tests_codac2_tubes.cpp +++ b/tests/core/tests_codac2_tubes.cpp @@ -166,6 +166,15 @@ TEST_CASE("Test codac2::tubes") CHECK(v[4]->t0_tf() == Interval(3,oo)); CHECK(v[4]->codomain() == IntervalVector({{-oo,oo}})); + CHECK(tdomain->iterator_tslice(-1.)->t0_tf() == Interval(-oo,0)); + CHECK(tdomain->iterator_tslice(0.)->t0_tf() == Interval(0,1)); + CHECK(tdomain->iterator_tslice(0.01)->t0_tf() == Interval(0,1)); + CHECK(tdomain->iterator_tslice(1)->t0_tf() == Interval(1,2)); + CHECK(tdomain->iterator_tslice(2)->t0_tf() == Interval(2,3)); + CHECK(tdomain->iterator_tslice(ibex::previous_float(3.))->t0_tf() == Interval(2,3)); + CHECK(tdomain->iterator_tslice(3)->t0_tf() == Interval(3,oo)); + CHECK(tdomain->iterator_tslice(ibex::next_float(3.))->t0_tf() == Interval(3,oo)); + CHECK(tdomain->nb_tslices() == 5); // with [-oo,0] and [3,oo] CHECK(static_cast(x(Interval(0,3))) == IntervalVector({{1,9}})); CHECK(static_cast(x(-1)) == IntervalVector(1)); @@ -386,7 +395,7 @@ TEST_CASE("Test codac2::tubes") SECTION("Test again 1") { - auto tdomain = create_tdomain(Interval(0,10), 0.01, true); // last argument creates "gates" (degenerated slices at scalar timesteps) + auto tdomain = create_tdomain(Interval(1,10), 0.01, true); // last argument creates "gates" (degenerated slices at scalar timesteps) Tube x(tdomain, codac::TFunction("(sin(sqrt(t)+((t-5)^2)*[-0.01,0.01]) ; cos(t)+sin(t/0.2)*[-0.1,0.1])")); Tube u(tdomain, IntervalVector(2)); @@ -551,4 +560,50 @@ TEST_CASE("Test codac2::tubes") CHECK(x(std::next(std::next(it))).codomain() == Interval(-10,10)); CHECK(x.codomain() == Interval(-10,10)); } + + SECTION("Testing validity of copy of tubes") + { + auto tdomain = create_tdomain(Interval(0,5), 0.01, true); + + Tube y(tdomain, Interval(2.)), x1(tdomain, Interval(-1,1)), x2(tdomain, Interval(1)); + Tube cx1(x1), cx2(x2); // copy + + for(std::list::iterator it = cx1.tdomain()->tslices().begin(); + it != cx1.tdomain()->tslices().end(); ++it) + { + Interval ix1 = cx1(it).codomain(), ix2 = cx2(it).codomain(); + ibex::bwd_add(y(it).codomain(), ix1, ix2); + cx1(it).set(ix1); + cx2(it).set(ix2); + } + + CHECK(cx1.codomain() == Interval(1)); + CHECK(cx2.codomain() == Interval(1)); + CHECK(y.codomain() == Interval(2)); + } + + SECTION("Tube not empty if built from a Function") + { + auto tdomain = create_tdomain(Interval(0,5), 0.01, true); + Tube aa1(tdomain, TFunction("5*sin(2*t)+t")); + CHECK(!aa1.is_empty()); + } + + SECTION("Testing tube evaluation") + { + auto tdomain = create_tdomain(Interval(0,5), 0.1, true); + Tube a(tdomain, TFunction("10*cos(t)+t")); + codac::Tube a_codac1 = codac2::to_codac1(a); + //vibes::beginDrawing(); + //codac::VIBesFigTube fig("Tube"); + //fig.set_properties(100, 100, 600, 300); + //fig.add_tube(&a_codac1, "a_codac1", "blue[#7EC8FF88]"); + //fig.draw_box({{1,2},a.eval(Interval(1,2))}); + //fig.draw_box({{1,2},a_codac1(Interval(1,2))}, "red"); + //fig.show(); + //vibes::endDrawing(); + + CHECK(ApproxIntv(tdomain->iterator_tslice(2.)->t0_tf()) == Interval(1.900000000000001, 2.000000000000002)); + CHECK(ApproxIntv(a.eval(Interval(1,2))) == Interval(-2.26146836547144, 7.216099682706644)); + } } \ No newline at end of file From da1d743a41ab0d0138fb0853e94b26d07284675f Mon Sep 17 00:00:00 2001 From: SimonRohou Date: Thu, 30 Mar 2023 16:34:30 +0200 Subject: [PATCH 067/256] [doc] typo --- doc/doc/faq.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/doc/faq.rst b/doc/doc/faq.rst index 225eb5503..5196d529e 100644 --- a/doc/doc/faq.rst +++ b/doc/doc/faq.rst @@ -204,7 +204,7 @@ A contractor is an operator that *contracts* (reduces) a domain (a box, for inst When it is used together with other contractors, there may be interactions between the contractors: a contraction from one contractor may *activate* another one. It becomes necessary to call all the contractors several times in order to converge to the best contraction of the domains. This number of contracting iterations cannot be known in advance. It depends on the contractors at stake, their efficiency and their sequencing. -One can implement a loop of contractions in order to process the contractors as long as their is a contraction on one of the domains. The iteration stops when a fixed point has been reached: when nothing can be contracted anymore. +One can implement a loop of contractions in order to process the contractors as long as there is a contraction on one of the domains. The iteration stops when a fixed point has been reached: when nothing can be contracted anymore. Because a computer computes with floating point numbers, the fixed point will be reached in a finite number of steps. In practice, we may stop the iteration as soon as the contractions are not significant anymore. From c2e0edb9144b09844cb612dd35cb1346ac722437 Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Sat, 25 Mar 2023 22:11:59 +0100 Subject: [PATCH 068/256] Documentation updates --- doc/doc/install/01-installation-full-linux.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/doc/install/01-installation-full-linux.rst b/doc/doc/install/01-installation-full-linux.rst index e030d73cd..3b23d128b 100644 --- a/doc/doc/install/01-installation-full-linux.rst +++ b/doc/doc/install/01-installation-full-linux.rst @@ -105,7 +105,7 @@ For instance: mkdir build -p ; cd build ; cmake .. ; make # cmake compilation ./codac_basics_01 # running example -Do not forget to launch the VIBes viewer before running your program. +Do not forget to launch the `VIBes viewer <01-installation.html#graphical-tools>`_ before running your program. (for experts) Additional installation options From 34e7e75d1f04c9b551bdf8d49a9d8f4e5a321dbf Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Sat, 25 Mar 2023 22:21:24 +0100 Subject: [PATCH 069/256] Updated workflows --- .github/workflows/macosmatrix.yml | 2 ++ .github/workflows/tests.yml | 2 +- .github/workflows/unixmatrix.yml | 16 +++++++--------- .github/workflows/vcmatrix.yml | 22 ++++++++++++---------- 4 files changed, 22 insertions(+), 20 deletions(-) diff --git a/.github/workflows/macosmatrix.yml b/.github/workflows/macosmatrix.yml index 0d176f562..7b2b1c309 100644 --- a/.github/workflows/macosmatrix.yml +++ b/.github/workflows/macosmatrix.yml @@ -19,11 +19,13 @@ jobs: fail-fast: false matrix: cfg: + - { os: macos-11, shell: bash, arch: arm64, runtime: bigsur, cmake_flags: '-fPIC', ilib: 'filib', trgt: '11.0', cpcfg: '-macosx_11_0_arm64', py_v_maj: 3, py_v_min: 11, desc: 'macOS Big Sur Python 3.11 arm64' } - { os: macos-11, shell: bash, arch: arm64, runtime: bigsur, cmake_flags: '-fPIC', ilib: 'filib', trgt: '11.0', cpcfg: '-macosx_11_0_arm64', py_v_maj: 3, py_v_min: 10, desc: 'macOS Big Sur Python 3.10 arm64' } - { os: macos-11, shell: bash, arch: arm64, runtime: bigsur, cmake_flags: '-fPIC', ilib: 'filib', trgt: '11.0', cpcfg: '-macosx_11_0_arm64', py_v_maj: 3, py_v_min: 9, desc: 'macOS Big Sur Python 3.9 arm64' } - { os: macos-11, shell: bash, arch: arm64, runtime: bigsur, cmake_flags: '-fPIC', ilib: 'filib', trgt: '11.0', cpcfg: '-macosx_11_0_arm64', py_v_maj: 3, py_v_min: 8, desc: 'macOS Big Sur Python 3.8 arm64' } - { os: macos-11, shell: bash, arch: arm64, runtime: bigsur, cmake_flags: '-fPIC', ilib: 'filib', trgt: '11.0', cpcfg: '-macosx_11_0_arm64', py_v_maj: 3, py_v_min: 7, desc: 'macOS Big Sur Python 3.7 arm64' } - { os: macos-11, shell: bash, arch: arm64, runtime: bigsur, cmake_flags: '-fPIC', ilib: 'filib', trgt: '11.0', cpcfg: '-macosx_11_0_arm64', py_v_maj: 3, py_v_min: 6, desc: 'macOS Big Sur Python 3.6 arm64' } + - { os: macos-11, shell: bash, arch: x86_64, runtime: bigsur, cmake_flags: '-fPIC', ilib: 'gaol', trgt: '10.9', cpcfg: '-macosx_10_9_x86_64', py_v_maj: 3, py_v_min: 11, desc: 'macOS Big Sur Python 3.11 x86_64' } - { os: macos-11, shell: bash, arch: x86_64, runtime: bigsur, cmake_flags: '-fPIC', ilib: 'gaol', trgt: '10.9', cpcfg: '-macosx_10_9_x86_64', py_v_maj: 3, py_v_min: 10, desc: 'macOS Big Sur Python 3.10 x86_64' } - { os: macos-11, shell: bash, arch: x86_64, runtime: bigsur, cmake_flags: '-fPIC', ilib: 'gaol', trgt: '10.9', cpcfg: '-macosx_10_9_x86_64', py_v_maj: 3, py_v_min: 9, desc: 'macOS Big Sur Python 3.9 x86_64' } - { os: macos-11, shell: bash, arch: x86_64, runtime: bigsur, cmake_flags: '-fPIC', ilib: 'gaol', trgt: '10.9', cpcfg: '-macosx_10_9_x86_64', py_v_maj: 3, py_v_min: 8, desc: 'macOS Big Sur Python 3.8 x86_64' } diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 76ea535d5..9d34233b7 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -12,7 +12,7 @@ jobs: fail-fast: false matrix: cfg: - - { os: ubuntu-18.04, gcc_v: 8, py_v_maj: 3, py_v_min: 6, desc: 'Ubuntu 18.04 GCC 8 Python 3.6 tests' } + - { os: ubuntu-20.04, gcc_v: 8, py_v_maj: 3, py_v_min: 6, desc: 'Ubuntu 20.04 GCC 8 Python 3.6 tests' } name: ${{ matrix.cfg.desc }} steps: - uses: actions/checkout@v3 diff --git a/.github/workflows/unixmatrix.yml b/.github/workflows/unixmatrix.yml index 0db85075e..8a0d4fcf6 100644 --- a/.github/workflows/unixmatrix.yml +++ b/.github/workflows/unixmatrix.yml @@ -10,14 +10,13 @@ jobs: deploy: runs-on: ubuntu-latest steps: - - uses: actions/create-release@v1 + - uses: softprops/action-gh-release@v1 id: create_release env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: draft: true tag_name: autotagname-${{ github.sha }} - release_name: autotagname-${{ github.sha }} if: (github.event_name != 'pull_request')&&(github.ref_name == 'master') unixmatrix: @@ -29,12 +28,12 @@ jobs: fail-fast: false matrix: cfg: - - { os: windows-2022, shell: cmd, arch: x64, runtime: vc17, cmake_params: '-G "Visual Studio 17" -A x64', cmake_flags: ' /wd4267 /wd4244 /wd4305 /wd4996', test_config: 'Release/', desc: 'Windows Visual Studio 2022 x64' } - - { os: windows-2022, shell: cmd, arch: x86, runtime: vc17, cmake_params: '-G "Visual Studio 17" -A Win32', choco_flags: '--x86', cmake_flags: ' /wd4267 /wd4244 /wd4305 /wd4996', test_config: 'Release/', desc: 'Windows Visual Studio 2022 x86' } - - { os: windows-2019, shell: cmd, arch: x64, runtime: vc16, cmake_params: '-G "Visual Studio 16" -A x64', cmake_flags: ' /wd4267 /wd4244 /wd4305 /wd4996', test_config: 'Release/', desc: 'Windows Visual Studio 2019 x64' } - - { os: windows-2019, shell: cmd, arch: x86, runtime: vc16, cmake_params: '-G "Visual Studio 16" -A Win32', choco_flags: '--x86', cmake_flags: ' /wd4267 /wd4244 /wd4305 /wd4996', test_config: 'Release/', desc: 'Windows Visual Studio 2019 x86' } - - { os: windows-2019, shell: cmd, arch: x64, runtime: vc15, cmake_params: '-G "Visual Studio 16" -T v141 -A x64', cmake_flags: ' /wd4267 /wd4244 /wd4305 /wd4996', test_config: 'Release/', desc: 'Windows Visual Studio 2017 x64' } - - { os: windows-2019, shell: cmd, arch: x86, runtime: vc15, cmake_params: '-G "Visual Studio 16" -T v141 -A Win32', choco_flags: '--x86', cmake_flags: ' /wd4267 /wd4244 /wd4305 /wd4996', test_config: 'Release/', desc: 'Windows Visual Studio 2017 x86' } + - { os: windows-2022, shell: cmd, arch: x64, runtime: vc17, cmake_params: '-G "Visual Studio 17" -T v143 -A x64', cmake_flags: ' /wd4267 /wd4244 /wd4305 /wd4996', test_config: 'Release/', desc: 'Windows Visual Studio 2022 x64' } + - { os: windows-2022, shell: cmd, arch: x86, runtime: vc17, cmake_params: '-G "Visual Studio 17" -T v143 -A Win32', choco_flags: '--x86', cmake_flags: ' /wd4267 /wd4244 /wd4305 /wd4996', test_config: 'Release/', desc: 'Windows Visual Studio 2022 x86' } + - { os: windows-2022, shell: cmd, arch: x64, runtime: vc16, cmake_params: '-G "Visual Studio 17" -T v142 -A x64', cmake_flags: ' /wd4267 /wd4244 /wd4305 /wd4996', test_config: 'Release/', desc: 'Windows Visual Studio 2019 x64' } + - { os: windows-2022, shell: cmd, arch: x86, runtime: vc16, cmake_params: '-G "Visual Studio 17" -T v142 -A Win32', choco_flags: '--x86', cmake_flags: ' /wd4267 /wd4244 /wd4305 /wd4996', test_config: 'Release/', desc: 'Windows Visual Studio 2019 x86' } + - { os: windows-2022, shell: cmd, arch: x64, runtime: vc15, cmake_params: '-G "Visual Studio 17" -T v141 -A x64', cmake_flags: ' /wd4267 /wd4244 /wd4305 /wd4996', test_config: 'Release/', desc: 'Windows Visual Studio 2017 x64' } + - { os: windows-2022, shell: cmd, arch: x86, runtime: vc15, cmake_params: '-G "Visual Studio 17" -T v141 -A Win32', choco_flags: '--x86', cmake_flags: ' /wd4267 /wd4244 /wd4305 /wd4996', test_config: 'Release/', desc: 'Windows Visual Studio 2017 x86' } - { os: windows-2022, shell: cmd, arch: x64, runtime: mingw11, cmake_params: '-G "MSYS Makefiles"', cmake_flags: '-fPIC', desc: 'Windows MinGW 11.2.0 x64' } - { os: windows-2022, shell: cmd, arch: x86, runtime: mingw11, cmake_params: '-G "MSYS Makefiles"', cmake_flags: '-fPIC', choco_flags: '--x86', desc: 'Windows MinGW 11.2.0 x86' } - { os: windows-2019, shell: cmd, arch: x64, runtime: mingw8, cmake_params: '-G "MSYS Makefiles"', cmake_flags: '-fPIC', desc: 'Windows MinGW 8.1.0 x64' } @@ -43,7 +42,6 @@ jobs: - { os: windows-2019, shell: cmd, arch: x86, runtime: mingw7, cmake_params: '-G "MSYS Makefiles"', cmake_flags: '-fPIC', choco_flags: '--x86', desc: 'Windows MinGW 7.3.0 x86' } - { os: ubuntu-22.04, shell: bash, arch: amd64, runtime: jammy, cmake_flags: '-fPIC', desc: 'Ubuntu 22.04' } - { os: ubuntu-20.04, shell: bash, arch: amd64, runtime: focal, cmake_flags: '-fPIC', desc: 'Ubuntu 20.04' } - - { os: ubuntu-18.04, shell: bash, arch: amd64, runtime: bionic, cmake_flags: '-fPIC', desc: 'Ubuntu 18.04' } - { os: macos-12, shell: bash, arch: arm64, runtime: monterey, cmake_params: '-D CMAKE_SYSTEM_NAME=Darwin -D CMAKE_OSX_ARCHITECTURES=arm64', cmake_flags: '-fPIC', desc: 'macOS Monterey arm64' } - { os: macos-12, shell: bash, arch: x86_64, runtime: monterey, cmake_params: '-D CMAKE_SYSTEM_NAME=Darwin -D CMAKE_OSX_ARCHITECTURES=x86_64', cmake_flags: '-fPIC', desc: 'macOS Monterey x86_64' } - { os: macos-11, shell: bash, arch: arm64, runtime: bigsur, cmake_params: '-D CMAKE_SYSTEM_NAME=Darwin -D CMAKE_OSX_ARCHITECTURES=arm64', cmake_flags: '-fPIC', desc: 'macOS Big Sur arm64' } diff --git a/.github/workflows/vcmatrix.yml b/.github/workflows/vcmatrix.yml index 255eba5ef..1d59f2e56 100644 --- a/.github/workflows/vcmatrix.yml +++ b/.github/workflows/vcmatrix.yml @@ -16,17 +16,19 @@ jobs: fail-fast: false matrix: cfg: - - { os: windows-2019, shell: cmd, arch: x86, runtime: vc16, cmake_params: '-G "Visual Studio 16" -A Win32', choco_flags: '--x86', cpcfg: '-win32', py_v_maj: 3, py_v_min: 10, desc: 'Windows Visual Studio 2019 x86 Python 3.10' } - - { os: windows-2019, shell: cmd, arch: x86, runtime: vc16, cmake_params: '-G "Visual Studio 16" -A Win32', choco_flags: '--x86', cpcfg: '-win32', py_v_maj: 3, py_v_min: 9, desc: 'Windows Visual Studio 2019 x86 Python 3.9' } - - { os: windows-2019, shell: cmd, arch: x86, runtime: vc16, cmake_params: '-G "Visual Studio 16" -A Win32', choco_flags: '--x86', cpcfg: '-win32', py_v_maj: 3, py_v_min: 8, desc: 'Windows Visual Studio 2019 x86 Python 3.8' } - - { os: windows-2019, shell: cmd, arch: x64, runtime: vc16, cmake_params: '-G "Visual Studio 16" -A x64', cpcfg: '-win_amd64', py_v_maj: 3, py_v_min: 10, desc: 'Windows Visual Studio 2019 x64 Python 3.10' } - - { os: windows-2019, shell: cmd, arch: x64, runtime: vc16, cmake_params: '-G "Visual Studio 16" -A x64', cpcfg: '-win_amd64', py_v_maj: 3, py_v_min: 9, desc: 'Windows Visual Studio 2019 x64 Python 3.9' } - - { os: windows-2019, shell: cmd, arch: x64, runtime: vc16, cmake_params: '-G "Visual Studio 16" -A x64', cpcfg: '-win_amd64', py_v_maj: 3, py_v_min: 8, desc: 'Windows Visual Studio 2019 x64 Python 3.8' } + - { os: windows-2022, shell: cmd, arch: x86, runtime: vc17, cmake_params: '-G "Visual Studio 17" -T v143 -A Win32', choco_flags: '--x86', cpcfg: '-win32', py_v_maj: 3, py_v_min: 11, desc: 'Windows Visual Studio 2022 x86 Python 3.11' } + - { os: windows-2022, shell: cmd, arch: x86, runtime: vc16, cmake_params: '-G "Visual Studio 17" -T v142 -A Win32', choco_flags: '--x86', cpcfg: '-win32', py_v_maj: 3, py_v_min: 10, desc: 'Windows Visual Studio 2019 x86 Python 3.10' } + - { os: windows-2022, shell: cmd, arch: x86, runtime: vc16, cmake_params: '-G "Visual Studio 17" -T v142 -A Win32', choco_flags: '--x86', cpcfg: '-win32', py_v_maj: 3, py_v_min: 9, desc: 'Windows Visual Studio 2019 x86 Python 3.9' } + - { os: windows-2022, shell: cmd, arch: x86, runtime: vc16, cmake_params: '-G "Visual Studio 17" -T v142 -A Win32', choco_flags: '--x86', cpcfg: '-win32', py_v_maj: 3, py_v_min: 8, desc: 'Windows Visual Studio 2019 x86 Python 3.8' } + - { os: windows-2022, shell: cmd, arch: x64, runtime: vc17, cmake_params: '-G "Visual Studio 17" -T v143 -A x64', cpcfg: '-win_amd64', py_v_maj: 3, py_v_min: 11, desc: 'Windows Visual Studio 2022 x64 Python 3.11' } + - { os: windows-2022, shell: cmd, arch: x64, runtime: vc16, cmake_params: '-G "Visual Studio 17" -T v142 -A x64', cpcfg: '-win_amd64', py_v_maj: 3, py_v_min: 10, desc: 'Windows Visual Studio 2019 x64 Python 3.10' } + - { os: windows-2022, shell: cmd, arch: x64, runtime: vc16, cmake_params: '-G "Visual Studio 17" -T v142 -A x64', cpcfg: '-win_amd64', py_v_maj: 3, py_v_min: 9, desc: 'Windows Visual Studio 2019 x64 Python 3.9' } + - { os: windows-2022, shell: cmd, arch: x64, runtime: vc16, cmake_params: '-G "Visual Studio 17" -T v142 -A x64', cpcfg: '-win_amd64', py_v_maj: 3, py_v_min: 8, desc: 'Windows Visual Studio 2019 x64 Python 3.8' } # Should be Visual Studio 2015 for Python 3.5-3.7, but need Visual Studio 2017 for C++17 compatibility...? - - { os: windows-2019, shell: cmd, arch: x86, runtime: vc15, cmake_params: '-G "Visual Studio 16" -T v141 -A Win32', choco_flags: '--x86', cpcfg: 'm-win32', py_v_maj: 3, py_v_min: 7, desc: 'Windows Visual Studio 2017 x86 Python 3.7' } - - { os: windows-2019, shell: cmd, arch: x86, runtime: vc15, cmake_params: '-G "Visual Studio 16" -T v141 -A Win32', choco_flags: '--x86', cpcfg: 'm-win32', py_v_maj: 3, py_v_min: 6, desc: 'Windows Visual Studio 2017 x86 Python 3.6' } - - { os: windows-2019, shell: cmd, arch: x64, runtime: vc15, cmake_params: '-G "Visual Studio 16" -T v141 -A x64', cpcfg: 'm-win_amd64', py_v_maj: 3, py_v_min: 7, desc: 'Windows Visual Studio 2017 x64 Python 3.7' } - - { os: windows-2019, shell: cmd, arch: x64, runtime: vc15, cmake_params: '-G "Visual Studio 16" -T v141 -A x64', cpcfg: 'm-win_amd64', py_v_maj: 3, py_v_min: 6, desc: 'Windows Visual Studio 2017 x64 Python 3.6' } + - { os: windows-2022, shell: cmd, arch: x86, runtime: vc15, cmake_params: '-G "Visual Studio 17" -T v141 -A Win32', choco_flags: '--x86', cpcfg: 'm-win32', py_v_maj: 3, py_v_min: 7, desc: 'Windows Visual Studio 2017 x86 Python 3.7' } + - { os: windows-2022, shell: cmd, arch: x86, runtime: vc15, cmake_params: '-G "Visual Studio 17" -T v141 -A Win32', choco_flags: '--x86', cpcfg: 'm-win32', py_v_maj: 3, py_v_min: 6, desc: 'Windows Visual Studio 2017 x86 Python 3.6' } + - { os: windows-2022, shell: cmd, arch: x64, runtime: vc15, cmake_params: '-G "Visual Studio 17" -T v141 -A x64', cpcfg: 'm-win_amd64', py_v_maj: 3, py_v_min: 7, desc: 'Windows Visual Studio 2017 x64 Python 3.7' } + - { os: windows-2022, shell: cmd, arch: x64, runtime: vc15, cmake_params: '-G "Visual Studio 17" -T v141 -A x64', cpcfg: 'm-win_amd64', py_v_maj: 3, py_v_min: 6, desc: 'Windows Visual Studio 2017 x64 Python 3.6' } name: ${{ matrix.cfg.desc }} steps: - uses: actions/checkout@v3 From a478a23c03e47be93349cb8d4d674e5fbba0f928 Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Tue, 17 Jan 2023 17:25:57 +0100 Subject: [PATCH 070/256] Fixed warnings "'delete' applied to a pointer that was allocated with 'new[]'" on macOS --- src/core/contractors/dyn/codac_CtcStatic.cpp | 14 ++++++------ .../contractors/static/codac_CtcFunction.cpp | 22 +++++++++---------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/core/contractors/dyn/codac_CtcStatic.cpp b/src/core/contractors/dyn/codac_CtcStatic.cpp index bd7693903..b899b45c0 100644 --- a/src/core/contractors/dyn/codac_CtcStatic.cpp +++ b/src/core/contractors/dyn/codac_CtcStatic.cpp @@ -54,7 +54,7 @@ namespace codac v_x_slices[i] = x[i].first_slice(); contract(v_x_slices, x.size()); - delete v_x_slices; + delete[] v_x_slices; } void CtcStatic::contract(Tube& x1) @@ -66,7 +66,7 @@ namespace codac v_x_slices[0] = x1.first_slice(); contract(v_x_slices, n); - delete v_x_slices; + delete[] v_x_slices; } void CtcStatic::contract(Tube& x1, Tube& x2) @@ -79,7 +79,7 @@ namespace codac v_x_slices[1] = x2.first_slice(); contract(v_x_slices, n); - delete v_x_slices; + delete[] v_x_slices; } void CtcStatic::contract(Tube& x1, Tube& x2, Tube& x3) @@ -93,7 +93,7 @@ namespace codac v_x_slices[2] = x3.first_slice(); contract(v_x_slices, n); - delete v_x_slices; + delete[] v_x_slices; } void CtcStatic::contract(Tube& x1, Tube& x2, Tube& x3, Tube& x4) @@ -108,7 +108,7 @@ namespace codac v_x_slices[3] = x4.first_slice(); contract(v_x_slices, n); - delete v_x_slices; + delete[] v_x_slices; } void CtcStatic::contract(Tube& x1, Tube& x2, Tube& x3, Tube& x4, Tube& x5) @@ -124,7 +124,7 @@ namespace codac v_x_slices[4] = x5.first_slice(); contract(v_x_slices, n); - delete v_x_slices; + delete[] v_x_slices; } void CtcStatic::contract(Tube& x1, Tube& x2, Tube& x3, Tube& x4, Tube& x5, Tube& x6) @@ -141,7 +141,7 @@ namespace codac v_x_slices[5] = x6.first_slice(); contract(v_x_slices, n); - delete v_x_slices; + delete[] v_x_slices; } void CtcStatic::contract(Slice **v_x_slices, int n) diff --git a/src/core/contractors/static/codac_CtcFunction.cpp b/src/core/contractors/static/codac_CtcFunction.cpp index f1adde21f..93c880dc4 100644 --- a/src/core/contractors/static/codac_CtcFunction.cpp +++ b/src/core/contractors/static/codac_CtcFunction.cpp @@ -54,7 +54,7 @@ namespace codac v_x_slices[i] = x[i].first_slice(); contract(v_x_slices); - delete v_x_slices; + delete[] v_x_slices; } void CtcFunction::contract(Tube& x1) @@ -65,7 +65,7 @@ namespace codac v_x_slices[0] = x1.first_slice(); contract(v_x_slices); - delete v_x_slices; + delete[] v_x_slices; } void CtcFunction::contract(Tube& x1, Tube& x2) @@ -77,7 +77,7 @@ namespace codac v_x_slices[1] = x2.first_slice(); contract(v_x_slices); - delete v_x_slices; + delete[] v_x_slices; } void CtcFunction::contract(Tube& x1, Tube& x2, Tube& x3) @@ -90,7 +90,7 @@ namespace codac v_x_slices[2] = x3.first_slice(); contract(v_x_slices); - delete v_x_slices; + delete[] v_x_slices; } void CtcFunction::contract(Tube& x1, Tube& x2, Tube& x3, Tube& x4) @@ -104,7 +104,7 @@ namespace codac v_x_slices[3] = x4.first_slice(); contract(v_x_slices); - delete v_x_slices; + delete[] v_x_slices; } void CtcFunction::contract(Tube& x1, Tube& x2, Tube& x3, Tube& x4, Tube& x5) @@ -119,7 +119,7 @@ namespace codac v_x_slices[4] = x5.first_slice(); contract(v_x_slices); - delete v_x_slices; + delete[] v_x_slices; } void CtcFunction::contract(Tube& x1, Tube& x2, Tube& x3, Tube& x4, Tube& x5, Tube& x6) @@ -135,7 +135,7 @@ namespace codac v_x_slices[5] = x6.first_slice(); contract(v_x_slices); - delete v_x_slices; + delete[] v_x_slices; } void CtcFunction::contract(Tube& x1, Tube& x2, Tube& x3, Tube& x4, Tube& x5, Tube& x6, Tube& x7) @@ -152,7 +152,7 @@ namespace codac v_x_slices[6] = x7.first_slice(); contract(v_x_slices); - delete v_x_slices; + delete[] v_x_slices; } void CtcFunction::contract(Tube& x1, Tube& x2, Tube& x3, Tube& x4, Tube& x5, Tube& x6, Tube& x7, Tube& x8) @@ -170,7 +170,7 @@ namespace codac v_x_slices[7] = x8.first_slice(); contract(v_x_slices); - delete v_x_slices; + delete[] v_x_slices; } void CtcFunction::contract(Tube& x1, Tube& x2, Tube& x3, Tube& x4, Tube& x5, Tube& x6, Tube& x7, Tube& x8, Tube& x9) @@ -189,7 +189,7 @@ namespace codac v_x_slices[8] = x9.first_slice(); contract(v_x_slices); - delete v_x_slices; + delete[] v_x_slices; } void CtcFunction::contract(Tube& x1, Tube& x2, Tube& x3, Tube& x4, Tube& x5, Tube& x6, Tube& x7, Tube& x8, Tube& x9, Tube& x10) @@ -209,7 +209,7 @@ namespace codac v_x_slices[9] = x10.first_slice(); contract(v_x_slices); - delete v_x_slices; + delete[] v_x_slices; } void CtcFunction::contract(Slice **v_x_slices) From 2d706ff29883e5f6b066547824d6ffdfeee944f1 Mon Sep 17 00:00:00 2001 From: SimonRohou Date: Fri, 14 Apr 2023 10:23:01 +0200 Subject: [PATCH 071/256] [sep] corrected bug in SepProj --- src/core/separators/codac_SepProj.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/separators/codac_SepProj.cpp b/src/core/separators/codac_SepProj.cpp index cf3f2df49..30eecd8f8 100644 --- a/src/core/separators/codac_SepProj.cpp +++ b/src/core/separators/codac_SepProj.cpp @@ -98,11 +98,11 @@ bool SepProj::process(IntervalVector& x_in, IntervalVector& x_out, IntervalVecto x_out.set_empty(); if( use_point == false) impact.setCoutFlags(x_out, x); - y.set_empty(); + //y.set_empty(); return true; } else { x_out = XoutFull.subvector(0, x_out.size()-1); - y = XoutFull.subvector(x_out.size(), XoutFull.size()-1); + //y = XoutFull.subvector(x_out.size(), XoutFull.size()-1); if( use_point == false) impact.setCoutFlags(x_out, x); } From 5158ea302fd6eee3f6efa599cd5e9490bfce46d6 Mon Sep 17 00:00:00 2001 From: SimonRohou Date: Tue, 9 May 2023 15:24:25 +0200 Subject: [PATCH 072/256] [py] moving Ctc/Sep-Transform to core --- python/src/core/separators/codac_py_Sep.cpp | 10 ++++++++-- .../separators/codac_py_Sep_unsupported.cpp | 7 ------- src/core/CMakeLists.txt | 4 ++++ .../separators/codac_CtcTransform.cpp | 0 .../separators/codac_CtcTransform.h | 0 .../separators/codac_SepTransform.cpp | 0 .../separators/codac_SepTransform.h | 0 src/unsupported/CMakeLists.txt | 4 ---- 8 files changed, 12 insertions(+), 13 deletions(-) rename src/{unsupported => core}/separators/codac_CtcTransform.cpp (100%) rename src/{unsupported => core}/separators/codac_CtcTransform.h (100%) rename src/{unsupported => core}/separators/codac_SepTransform.cpp (100%) rename src/{unsupported => core}/separators/codac_SepTransform.h (100%) diff --git a/python/src/core/separators/codac_py_Sep.cpp b/python/src/core/separators/codac_py_Sep.cpp index bd128e539..806a6374b 100644 --- a/python/src/core/separators/codac_py_Sep.cpp +++ b/python/src/core/separators/codac_py_Sep.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -124,10 +125,10 @@ py::class_ export_Sep(py::module& m) "f"_a.noconvert()) .def(py::init(), SEPFUNCTION_SEPFUNCTION_FUNCTION_INTERVAL, - "f"_a.noconvert(), "y"_a.noconvert()) + "f"_a.noconvert(), "y"_a) .def(py::init(), SEPFUNCTION_SEPFUNCTION_FUNCTION_INTERVALVECTOR, - "f"_a.noconvert(), "y"_a.noconvert()) + "f"_a.noconvert(), "y"_a) .def("separate", &SepFunction::separate, SEPFUNCTION_VOID_SEPARATE_INTERVALVECTOR_INTERVALVECTOR, "x_in"_a.noconvert(), "x_out"_a.noconvert()) @@ -165,6 +166,11 @@ py::class_ export_Sep(py::module& m) .def_property("q", py::cpp_function(&SepQInterProjF::get_q), py::cpp_function(&SepQInterProjF::set_q)) ; + // Export SepTransform + py::class_(m, "SepTransform", sep, "todo") + .def(py::init(), py::keep_alive<1,2>(), py::keep_alive<1,3>(), py::keep_alive<1,4>()) + .def("separate", &SepTransform::separate, py::call_guard()) + ; // Export SepProj py::class_(m, "SepProj", sep, __DOC_SEP_SEPPROJ) diff --git a/python/src/unsupported/separators/codac_py_Sep_unsupported.cpp b/python/src/unsupported/separators/codac_py_Sep_unsupported.cpp index f53429258..ef1aa2501 100644 --- a/python/src/unsupported/separators/codac_py_Sep_unsupported.cpp +++ b/python/src/unsupported/separators/codac_py_Sep_unsupported.cpp @@ -26,7 +26,6 @@ using namespace pybind11::literals; #include "ibex_Sep.h" #include "codac_QInterProjF.h" -#include "codac_SepTransform.h" #include "codac_SepFixPoint.h" #include "codac_SepProj.h" #include "codac_SepCtcPairProj.h" @@ -93,12 +92,6 @@ void export_unsupported_sep(py::module& m, py::class_& sep){ // .def("separate", &SepCtcPairProj::separate, py::call_guard()) // ; - // Export SepTransform - py::class_(m, "SepTransform", sep) - .def(py::init(), py::keep_alive<1,2>(), py::keep_alive<1,3>(), py::keep_alive<1,4>()) - .def("separate", &SepTransform::separate, py::call_guard()) - ; - // // Export SepFixPoint // py::class_(m, "SepFixPoint", sep) // .def(py::init(), py::keep_alive<1,2>(), "sep"_a, "ratio"_a=0.01) diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 8ddaba0b0..065f3009c 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -165,6 +165,10 @@ ${CMAKE_CURRENT_SOURCE_DIR}/separators/codac_SepFixPoint.cpp ${CMAKE_CURRENT_SOURCE_DIR}/separators/codac_SepProj.h ${CMAKE_CURRENT_SOURCE_DIR}/separators/codac_SepProj.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/separators/codac_CtcTransform.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/separators/codac_CtcTransform.h + ${CMAKE_CURRENT_SOURCE_DIR}/separators/codac_SepTransform.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/separators/codac_SepTransform.h ${CMAKE_CURRENT_SOURCE_DIR}/tools/codac_Tools.cpp ${CMAKE_CURRENT_SOURCE_DIR}/tools/codac_Tools.h ${CMAKE_CURRENT_SOURCE_DIR}/tools/codac_Eigen.cpp diff --git a/src/unsupported/separators/codac_CtcTransform.cpp b/src/core/separators/codac_CtcTransform.cpp similarity index 100% rename from src/unsupported/separators/codac_CtcTransform.cpp rename to src/core/separators/codac_CtcTransform.cpp diff --git a/src/unsupported/separators/codac_CtcTransform.h b/src/core/separators/codac_CtcTransform.h similarity index 100% rename from src/unsupported/separators/codac_CtcTransform.h rename to src/core/separators/codac_CtcTransform.h diff --git a/src/unsupported/separators/codac_SepTransform.cpp b/src/core/separators/codac_SepTransform.cpp similarity index 100% rename from src/unsupported/separators/codac_SepTransform.cpp rename to src/core/separators/codac_SepTransform.cpp diff --git a/src/unsupported/separators/codac_SepTransform.h b/src/core/separators/codac_SepTransform.h similarity index 100% rename from src/unsupported/separators/codac_SepTransform.h rename to src/core/separators/codac_SepTransform.h diff --git a/src/unsupported/CMakeLists.txt b/src/unsupported/CMakeLists.txt index 758fa4604..aab7fda0f 100644 --- a/src/unsupported/CMakeLists.txt +++ b/src/unsupported/CMakeLists.txt @@ -26,8 +26,6 @@ # Separators ${CMAKE_CURRENT_SOURCE_DIR}/separators/codac_CtcHull.cpp ${CMAKE_CURRENT_SOURCE_DIR}/separators/codac_CtcHull.h - ${CMAKE_CURRENT_SOURCE_DIR}/separators/codac_CtcTransform.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/separators/codac_CtcTransform.h # ${CMAKE_CURRENT_SOURCE_DIR}/separators/codac_QInterProjF.cpp # ${CMAKE_CURRENT_SOURCE_DIR}/separators/codac_QInterProjF.h # ${CMAKE_CURRENT_SOURCE_DIR}/separators/codac_SepCtcPairProj.cpp @@ -36,8 +34,6 @@ # ${CMAKE_CURRENT_SOURCE_DIR}/separators/codac_SepFixPoint.h # ${CMAKE_CURRENT_SOURCE_DIR}/separators/codac_SepProj.cpp # ${CMAKE_CURRENT_SOURCE_DIR}/separators/codac_SepProj.h - ${CMAKE_CURRENT_SOURCE_DIR}/separators/codac_SepTransform.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/separators/codac_SepTransform.h ${CMAKE_CURRENT_SOURCE_DIR}/separators/codac_SepUnionBbox.cpp ${CMAKE_CURRENT_SOURCE_DIR}/separators/codac_SepUnionBbox.h From 80ca1e12ba9a6c436bd93189ff57de2b85efc5f3 Mon Sep 17 00:00:00 2001 From: SimonRohou Date: Tue, 9 May 2023 15:25:28 +0200 Subject: [PATCH 073/256] [py] implicit conversion from list to Interval[Vector] objects --- doc/doc/manual/11-separators/index.rst | 12 +++--------- .../src/core/domains/interval/codac_py_Interval.cpp | 5 ++++- .../domains/interval/codac_py_IntervalVector.cpp | 3 +++ python/src/core/sivia/codac_py_sivia.cpp | 4 ++-- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/doc/doc/manual/11-separators/index.rst b/doc/doc/manual/11-separators/index.rst index 1a8c0b547..58e8a1f18 100644 --- a/doc/doc/manual/11-separators/index.rst +++ b/doc/doc/manual/11-separators/index.rst @@ -26,16 +26,13 @@ Separators can be combined and involved in an algorithm for set-inversion such a f = Function('x', 'y', 'x*cos(x-y)+y') # Build the separator associated to the constraint f(x,y) < 0 - sep = SepFunction(f, Interval(-oo,0)) - - # Setup the initial box - box = IntervalVector(2, [-10, 10]) + sep = SepFunction(f, [-oo,0]) # Graphics vibes.beginDrawing() vibes.newFigure("Set inversion") vibes.setFigureProperties({"x":100, "y":100, "width":500, "height":500}) - SIVIA(box, sep, 0.21, fig_name="Set inversion") + SIVIA([[-10, 10],[-10, 10]], sep, 0.21, fig_name="Set inversion") vibes.endDrawing() .. code-tab:: c++ @@ -52,14 +49,11 @@ Separators can be combined and involved in an algorithm for set-inversion such a // Build the separator associated to the constraint f(x,y) < 0 SepFunction sep(f, Interval(-oo,0)); - // Setup the initial box - IntervalVector box(2, {-10, 10}); - // Graphics vibes::beginDrawing(); vibes::newFigure("Set inversion"); vibes::setFigureProperties(vibesParams("x",100, "y",100, "width",500, "height",500)); - SIVIA(box, sep, 0.21, "Set inversion"); + SIVIA({{-10,10},{-10,10}}, sep, 0.21, "Set inversion"); vibes::endDrawing(); return EXIT_SUCCESS; diff --git a/python/src/core/domains/interval/codac_py_Interval.cpp b/python/src/core/domains/interval/codac_py_Interval.cpp index c82b00f54..3903f4970 100644 --- a/python/src/core/domains/interval/codac_py_Interval.cpp +++ b/python/src/core/domains/interval/codac_py_Interval.cpp @@ -87,7 +87,7 @@ void export_Interval(py::module& m) .def(py::init(), "build a copy of an interval", "x"_a.noconvert()) .def(py::init(), "build the interval [lb,ub]", "lb"_a, "ub"_a) .def(py::init(), "build singleton [x,x]", "x"_a) - //.def(py::init(&build_from_list)) + .def(py::init(&build_from_list)) //.def(py::init([](array& bounds) {return new Interval(bounds[0], bounds[1]);})) //.def(py::init([](array& bounds) {return new Interval(bounds[0], bounds[1]);})) //.def(py::init([](pair& bounds) {return new Interval(bounds.first, bounds.second);})) @@ -293,4 +293,7 @@ void export_Interval(py::module& m) // sbool (*bwd_pow_2)(const Interval&, int , Interval&) = &ibex::bwd_pow; m.attr("oo") = POS_INFINITY; + + // Automatic cast from lists to IntervalVectors (used for instance in SIVIA calls) + py::implicitly_convertible(); }; \ No newline at end of file diff --git a/python/src/core/domains/interval/codac_py_IntervalVector.cpp b/python/src/core/domains/interval/codac_py_IntervalVector.cpp index 01b6ca28e..15b0eacc1 100644 --- a/python/src/core/domains/interval/codac_py_IntervalVector.cpp +++ b/python/src/core/domains/interval/codac_py_IntervalVector.cpp @@ -336,4 +336,7 @@ void export_IntervalVector(py::module& m) m.def("bwd_mul", (bool (*) (const Interval&, IntervalVector&, IntervalVector&)) &ibex::bwd_mul); m.def("max", (IntervalVector(*) (const IntervalVector&, const IntervalVector&)) &max_IntevalVector); + + // Automatic cast from lists to IntervalVectors (used for instance in SIVIA calls) + py::implicitly_convertible(); }; \ No newline at end of file diff --git a/python/src/core/sivia/codac_py_sivia.cpp b/python/src/core/sivia/codac_py_sivia.cpp index b51ffb560..9d7b34b60 100644 --- a/python/src/core/sivia/codac_py_sivia.cpp +++ b/python/src/core/sivia/codac_py_sivia.cpp @@ -34,7 +34,7 @@ void export_sivia(py::module& m, py::class_& ctc, py::class_& ctc, py::class_ Date: Tue, 9 May 2023 15:26:33 +0200 Subject: [PATCH 074/256] [sep] cancel: corrected bug in SepProj --- src/core/separators/codac_SepProj.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/separators/codac_SepProj.cpp b/src/core/separators/codac_SepProj.cpp index 30eecd8f8..cf3f2df49 100644 --- a/src/core/separators/codac_SepProj.cpp +++ b/src/core/separators/codac_SepProj.cpp @@ -98,11 +98,11 @@ bool SepProj::process(IntervalVector& x_in, IntervalVector& x_out, IntervalVecto x_out.set_empty(); if( use_point == false) impact.setCoutFlags(x_out, x); - //y.set_empty(); + y.set_empty(); return true; } else { x_out = XoutFull.subvector(0, x_out.size()-1); - //y = XoutFull.subvector(x_out.size(), XoutFull.size()-1); + y = XoutFull.subvector(x_out.size(), XoutFull.size()-1); if( use_point == false) impact.setCoutFlags(x_out, x); } From cb38a2d879994abd1453c1be0769b298358d3bbc Mon Sep 17 00:00:00 2001 From: SimonRohou Date: Tue, 9 May 2023 16:32:21 +0200 Subject: [PATCH 075/256] [sivia] creating figure if a name is specified --- src/core/sivia/codac_sivia.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/core/sivia/codac_sivia.cpp b/src/core/sivia/codac_sivia.cpp index 587ae74c5..4f03dd182 100644 --- a/src/core/sivia/codac_sivia.cpp +++ b/src/core/sivia/codac_sivia.cpp @@ -46,6 +46,9 @@ namespace codac // will not be ended in case the init has been done outside this SIVIA function } + if(!fig_name.empty()) + vibes::newFigure(fig_name); + vibes::drawBox(x0, vibesParams("figure", fig_name)); vibes::axisAuto(); } @@ -154,7 +157,7 @@ namespace codac } if(!fig_name.empty()) - vibes::selectFigure(fig_name); + vibes::newFigure(fig_name); vibes::drawBox(x0); vibes::axisAuto(); From fe445566f2f86e8af9cebbefaecb525093e0bc97 Mon Sep 17 00:00:00 2001 From: SimonRohou Date: Tue, 9 May 2023 17:04:17 +0200 Subject: [PATCH 076/256] [capd] cancelling tests --- .github/workflows/tests.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 9d34233b7..55033ff9d 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -56,7 +56,7 @@ jobs: bash scripts/dependencies/install_ibex.sh # CAPD - bash scripts/dependencies/install_capd.sh + # cancelled on 2023/05/09: bash scripts/dependencies/install_capd.sh # Environment variables export CMAKE_PREFIX_PATH=$CMAKE_PREFIX_PATH:$HOME/codac/build_install @@ -76,7 +76,7 @@ jobs: # Without synthesis tree # Building lib + tests - cmake -DCMAKE_INSTALL_PREFIX=$HOME/codac/build_install -DCMAKE_PREFIX_PATH=$HOME/ibex-lib/build_install -DWITH_PYTHON=OFF -DBUILD_TESTS=ON -DWITH_TUBE_TREE=OFF -DWITH_CAPD=ON -DTEST_EXAMPLES=ON .. + cmake -DCMAKE_INSTALL_PREFIX=$HOME/codac/build_install -DCMAKE_PREFIX_PATH=$HOME/ibex-lib/build_install -DWITH_PYTHON=OFF -DBUILD_TESTS=ON -DWITH_TUBE_TREE=OFF -DWITH_CAPD=OFF -DTEST_EXAMPLES=ON .. ##-DPYTHON_EXECUTABLE=/usr/bin/python3.5 .. make #make doc # todo @@ -113,7 +113,7 @@ jobs: # With synthesis tree for all created tubes # Building lib + tests - cmake -DCMAKE_INSTALL_PREFIX=$HOME/codac/build_install -DCMAKE_PREFIX_PATH=$HOME/ibex-lib/build_install -DWITH_PYTHON=OFF -DBUILD_TESTS=ON -DWITH_TUBE_TREE=ON -DWITH_CAPD=ON -DTEST_EXAMPLES=ON .. + cmake -DCMAKE_INSTALL_PREFIX=$HOME/codac/build_install -DCMAKE_PREFIX_PATH=$HOME/ibex-lib/build_install -DWITH_PYTHON=OFF -DBUILD_TESTS=ON -DWITH_TUBE_TREE=ON -DWITH_CAPD=OFF -DTEST_EXAMPLES=ON .. ##-DPYTHON_EXECUTABLE=/usr/bin/python3.5 .. make #make doc From a4f5d84f064b097988b25cc0494b8b2f09186629 Mon Sep 17 00:00:00 2001 From: SimonRohou Date: Wed, 10 May 2023 10:21:23 +0200 Subject: [PATCH 077/256] [doc] updated python versions --- doc/doc/install/01-installation-python.rst | 2 ++ doc/doc/install/01-installation.rst | 2 ++ 2 files changed, 4 insertions(+) diff --git a/doc/doc/install/01-installation-python.rst b/doc/doc/install/01-installation-python.rst index 355fb0565..c36bb316e 100644 --- a/doc/doc/install/01-installation-python.rst +++ b/doc/doc/install/01-installation-python.rst @@ -47,6 +47,8 @@ The :gbg:`✓` configurations are officially supported at the moment: |Python 3.9 |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` | | +---------------+----------------+-----------------+-----------------+----------------+----------------+ + |Python 3.10 |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` | | ++---------------+----------------+-----------------+-----------------+----------------+----------------+ + +|Python 3.11 | |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` | | +---------------+----------------+-----------------+-----------------+----------------+----------------+----------------+ If a configuration in this table does not work, please `contact us `_. diff --git a/doc/doc/install/01-installation.rst b/doc/doc/install/01-installation.rst index 8bd7db009..bf68fe8ee 100644 --- a/doc/doc/install/01-installation.rst +++ b/doc/doc/install/01-installation.rst @@ -51,6 +51,8 @@ The :gbg:`✓` configurations are officially supported at the moment: |Python 3.9 ||linux-py|_ ||win-py|_ ||win-py|_ ||macos-py|_ ||macos-py|_ | | +---------------+----------------+-----------------+-----------------+----------------+----------------+ + |Python 3.10 ||linux-py|_ ||win-py|_ ||win-py|_ ||macos-py|_ ||macos-py|_ | | ++---------------+----------------+-----------------+-----------------+----------------+----------------+ + +|Python 3.11 | ||win-py|_ ||win-py|_ ||macos-py|_ ||macos-py|_ | | +---------------+----------------+-----------------+-----------------+----------------+----------------+----------------+ | **Click on the links in the table to access the related installation procedures.** From 9e2baabcdb52e45f1c4582ed86f1c1bf30034274 Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Thu, 11 May 2023 21:36:56 +0200 Subject: [PATCH 078/256] Fixed warnings "'delete' applied to a pointer that was allocated with 'new[]'" on macOS --- python/src/core/domains/interval/codac_py_IntervalVector.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/src/core/domains/interval/codac_py_IntervalVector.cpp b/python/src/core/domains/interval/codac_py_IntervalVector.cpp index 15b0eacc1..d9336906d 100644 --- a/python/src/core/domains/interval/codac_py_IntervalVector.cpp +++ b/python/src/core/domains/interval/codac_py_IntervalVector.cpp @@ -39,7 +39,7 @@ IntervalVector* create_from_pylist(const vector& lst) { if(lst[i].size() != 2) { - delete tmp; + delete[] tmp; throw invalid_argument("sub list must contain only two elements"); } From 7984c078c0543b9dbc92ff2ab76d4c440039b4d5 Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Wed, 10 May 2023 00:05:57 +0200 Subject: [PATCH 079/256] Updated workflows --- .github/workflows/dockercentos.yml | 2 +- .github/workflows/dockerpi.yml | 2 +- .github/workflows/macosmatrix.yml | 26 ++++++++-------- .github/workflows/tests.yml | 2 +- .github/workflows/unixmatrix.yml | 49 ++++++++++++++++-------------- .github/workflows/vcmatrix.yml | 4 +-- scripts/docker/build_pybinding.sh | 5 +++ 7 files changed, 50 insertions(+), 40 deletions(-) diff --git a/.github/workflows/dockercentos.yml b/.github/workflows/dockercentos.yml index 0833cd8d8..bd5933c43 100644 --- a/.github/workflows/dockercentos.yml +++ b/.github/workflows/dockercentos.yml @@ -17,7 +17,7 @@ jobs: clean: false - run: | chmod a+x scripts/docker/build_pybinding.sh - docker run --rm -v `pwd`:/io lebarsfa/manylinux2010_x86_64-ibex /io/scripts/docker/build_pybinding.sh + docker run --rm -v `pwd`:/io lebarsfa/manylinux2010_x86_64-for-codac /io/scripts/docker/build_pybinding.sh ls wheelhouse - uses: xresloader/upload-to-github-release@v1 env: diff --git a/.github/workflows/dockerpi.yml b/.github/workflows/dockerpi.yml index bc536896a..df2e8ea3e 100644 --- a/.github/workflows/dockerpi.yml +++ b/.github/workflows/dockerpi.yml @@ -16,7 +16,7 @@ jobs: fetch-depth: 0 clean: false - run: docker run --rm --privileged multiarch/qemu-user-static:register --reset - - run: docker run -i -v "${PWD}/..:${PWD}/.." lebarsfa/pi:buster-ibex /bin/bash -c "uname -a && cat /etc/os-release && cd ${PWD} && lsb_release -a && sudo apt-get -q update --allow-releaseinfo-change ; sudo apt-get -y install libeigen3-dev zip ; mkdir build ; cd build && cmake -D CMAKE_INSTALL_PREFIX=../codac .. && cmake --build . --target install && cd .. && mkdir -p codac_standalone/example ; cd codac_standalone ; wget https://community.chocolatey.org/api/v2/package/eigen/3.4.0 --no-check-certificate -nv ; unzip -q 3.4.0 -d eigen ; rm -Rf 3.4.0 eigen/*.xml eigen/*.nuspec eigen/_* eigen/package eigen/tools ; mkdir -p ibex/include ; mkdir -p ibex/lib ; mkdir -p ibex/share ; mkdir -p ibex/bin ; cp -Rf /usr/local/include/ibex* ibex/include/ ; cp -Rf /usr/local/lib/*ibex* ibex/lib/ ; cp -Rf /usr/local/share/*ibex* ibex/share/ ; cp -Rf /usr/local/share/pkgconfig ibex/share/ ; cp -Rf /usr/local/bin/ibex* ibex/bin/ ; cp -Rf ../codac . ; cp -Rf ../tests/test_codac/* ./example/ ; cd .. ; zip -q -r codac_standalone_armv6hf_buster.zip codac_standalone ; cd codac_standalone/example && cmake . && cmake --build . && ./my_project" + - run: docker run -i -v "${PWD}/..:${PWD}/.." lebarsfa/pi:buster-for-codac /bin/bash -c "uname -a && cat /etc/os-release && cd ${PWD} && pwd && lsb_release -a && sudo apt-get -q update --allow-releaseinfo-change ; sudo apt-get -y install libeigen3-dev ; wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20220812/ibex_armv6hf_buster.zip --no-check-certificate -nv ; unzip -q ibex_armv6hf_buster.zip && rm -Rf ibex_armv6hf_buster.zip && sudo cp -Rf ibex/* /usr/local/ && mkdir build ; cd build && cmake -E env CXXFLAGS="-fPIC" CFLAGS="-fPIC" cmake -D CMAKE_INSTALL_PREFIX=../codac .. && cmake --build . --target install && cd .. && mkdir -p codac_standalone/example ; cd codac_standalone ; wget https://community.chocolatey.org/api/v2/package/eigen/3.4.0 --no-check-certificate -nv ; unzip -q 3.4.0 -d eigen ; rm -Rf 3.4.0 eigen/*.xml eigen/*.nuspec eigen/_* eigen/package eigen/tools ; mkdir -p ibex/include ; mkdir -p ibex/lib ; mkdir -p ibex/share ; mkdir -p ibex/bin ; cp -Rf /usr/local/include/ibex* ibex/include/ ; cp -Rf /usr/local/lib/*ibex* ibex/lib/ ; cp -Rf /usr/local/share/*ibex* ibex/share/ ; cp -Rf /usr/local/share/pkgconfig ibex/share/ ; cp -Rf /usr/local/bin/ibex* ibex/bin/ ; cp -Rf ../codac . ; cp -Rf ../tests/test_codac/* ./example/ ; cd .. ; zip -q -r codac_standalone_armv6hf_buster.zip codac_standalone ; cd codac_standalone/example && cmake . && cmake --build . && ./my_project" - uses: xresloader/upload-to-github-release@v1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/macosmatrix.yml b/.github/workflows/macosmatrix.yml index 7b2b1c309..1b076647c 100644 --- a/.github/workflows/macosmatrix.yml +++ b/.github/workflows/macosmatrix.yml @@ -19,18 +19,18 @@ jobs: fail-fast: false matrix: cfg: - - { os: macos-11, shell: bash, arch: arm64, runtime: bigsur, cmake_flags: '-fPIC', ilib: 'filib', trgt: '11.0', cpcfg: '-macosx_11_0_arm64', py_v_maj: 3, py_v_min: 11, desc: 'macOS Big Sur Python 3.11 arm64' } - - { os: macos-11, shell: bash, arch: arm64, runtime: bigsur, cmake_flags: '-fPIC', ilib: 'filib', trgt: '11.0', cpcfg: '-macosx_11_0_arm64', py_v_maj: 3, py_v_min: 10, desc: 'macOS Big Sur Python 3.10 arm64' } - - { os: macos-11, shell: bash, arch: arm64, runtime: bigsur, cmake_flags: '-fPIC', ilib: 'filib', trgt: '11.0', cpcfg: '-macosx_11_0_arm64', py_v_maj: 3, py_v_min: 9, desc: 'macOS Big Sur Python 3.9 arm64' } - - { os: macos-11, shell: bash, arch: arm64, runtime: bigsur, cmake_flags: '-fPIC', ilib: 'filib', trgt: '11.0', cpcfg: '-macosx_11_0_arm64', py_v_maj: 3, py_v_min: 8, desc: 'macOS Big Sur Python 3.8 arm64' } - - { os: macos-11, shell: bash, arch: arm64, runtime: bigsur, cmake_flags: '-fPIC', ilib: 'filib', trgt: '11.0', cpcfg: '-macosx_11_0_arm64', py_v_maj: 3, py_v_min: 7, desc: 'macOS Big Sur Python 3.7 arm64' } - - { os: macos-11, shell: bash, arch: arm64, runtime: bigsur, cmake_flags: '-fPIC', ilib: 'filib', trgt: '11.0', cpcfg: '-macosx_11_0_arm64', py_v_maj: 3, py_v_min: 6, desc: 'macOS Big Sur Python 3.6 arm64' } - - { os: macos-11, shell: bash, arch: x86_64, runtime: bigsur, cmake_flags: '-fPIC', ilib: 'gaol', trgt: '10.9', cpcfg: '-macosx_10_9_x86_64', py_v_maj: 3, py_v_min: 11, desc: 'macOS Big Sur Python 3.11 x86_64' } - - { os: macos-11, shell: bash, arch: x86_64, runtime: bigsur, cmake_flags: '-fPIC', ilib: 'gaol', trgt: '10.9', cpcfg: '-macosx_10_9_x86_64', py_v_maj: 3, py_v_min: 10, desc: 'macOS Big Sur Python 3.10 x86_64' } - - { os: macos-11, shell: bash, arch: x86_64, runtime: bigsur, cmake_flags: '-fPIC', ilib: 'gaol', trgt: '10.9', cpcfg: '-macosx_10_9_x86_64', py_v_maj: 3, py_v_min: 9, desc: 'macOS Big Sur Python 3.9 x86_64' } - - { os: macos-11, shell: bash, arch: x86_64, runtime: bigsur, cmake_flags: '-fPIC', ilib: 'gaol', trgt: '10.9', cpcfg: '-macosx_10_9_x86_64', py_v_maj: 3, py_v_min: 8, desc: 'macOS Big Sur Python 3.8 x86_64' } - - { os: macos-11, shell: bash, arch: x86_64, runtime: bigsur, cmake_flags: '-fPIC', ilib: 'gaol', trgt: '10.9', cpcfg: '-macosx_10_9_x86_64', py_v_maj: 3, py_v_min: 7, desc: 'macOS Big Sur Python 3.7 x86_64' } - - { os: macos-11, shell: bash, arch: x86_64, runtime: bigsur, cmake_flags: '-fPIC', ilib: 'gaol', trgt: '10.14', cpcfg: '-macosx_10_14_x86_64', py_v_maj: 3, py_v_min: 6, desc: 'macOS Big Sur Python 3.6 x86_64' } # 10.14 because of error $MACOSX_DEPLOYMENT_TARGET mismatch: now "10.9" but "10.14" during configure. + - { os: macos-11, shell: bash, arch: arm64, runtime: bigsur, cmake_flags: '-fPIC', trgt: '11.0', cpcfg: '-macosx_11_0_arm64', py_v_maj: 3, py_v_min: 11, desc: 'macOS Big Sur Python 3.11 arm64' } + - { os: macos-11, shell: bash, arch: arm64, runtime: bigsur, cmake_flags: '-fPIC', trgt: '11.0', cpcfg: '-macosx_11_0_arm64', py_v_maj: 3, py_v_min: 10, desc: 'macOS Big Sur Python 3.10 arm64' } + - { os: macos-11, shell: bash, arch: arm64, runtime: bigsur, cmake_flags: '-fPIC', trgt: '11.0', cpcfg: '-macosx_11_0_arm64', py_v_maj: 3, py_v_min: 9, desc: 'macOS Big Sur Python 3.9 arm64' } + - { os: macos-11, shell: bash, arch: arm64, runtime: bigsur, cmake_flags: '-fPIC', trgt: '11.0', cpcfg: '-macosx_11_0_arm64', py_v_maj: 3, py_v_min: 8, desc: 'macOS Big Sur Python 3.8 arm64' } + - { os: macos-11, shell: bash, arch: arm64, runtime: bigsur, cmake_flags: '-fPIC', trgt: '11.0', cpcfg: '-macosx_11_0_arm64', py_v_maj: 3, py_v_min: 7, desc: 'macOS Big Sur Python 3.7 arm64' } + - { os: macos-11, shell: bash, arch: arm64, runtime: bigsur, cmake_flags: '-fPIC', trgt: '11.0', cpcfg: '-macosx_11_0_arm64', py_v_maj: 3, py_v_min: 6, desc: 'macOS Big Sur Python 3.6 arm64' } + - { os: macos-11, shell: bash, arch: x86_64, runtime: bigsur, cmake_flags: '-fPIC', trgt: '10.9', cpcfg: '-macosx_10_9_x86_64', py_v_maj: 3, py_v_min: 11, desc: 'macOS Big Sur Python 3.11 x86_64' } + - { os: macos-11, shell: bash, arch: x86_64, runtime: bigsur, cmake_flags: '-fPIC', trgt: '10.9', cpcfg: '-macosx_10_9_x86_64', py_v_maj: 3, py_v_min: 10, desc: 'macOS Big Sur Python 3.10 x86_64' } + - { os: macos-11, shell: bash, arch: x86_64, runtime: bigsur, cmake_flags: '-fPIC', trgt: '10.9', cpcfg: '-macosx_10_9_x86_64', py_v_maj: 3, py_v_min: 9, desc: 'macOS Big Sur Python 3.9 x86_64' } + - { os: macos-11, shell: bash, arch: x86_64, runtime: bigsur, cmake_flags: '-fPIC', trgt: '10.9', cpcfg: '-macosx_10_9_x86_64', py_v_maj: 3, py_v_min: 8, desc: 'macOS Big Sur Python 3.8 x86_64' } + - { os: macos-11, shell: bash, arch: x86_64, runtime: bigsur, cmake_flags: '-fPIC', trgt: '10.9', cpcfg: '-macosx_10_9_x86_64', py_v_maj: 3, py_v_min: 7, desc: 'macOS Big Sur Python 3.7 x86_64' } + - { os: macos-11, shell: bash, arch: x86_64, runtime: bigsur, cmake_flags: '-fPIC', trgt: '10.14', cpcfg: '-macosx_10_14_x86_64', py_v_maj: 3, py_v_min: 6, desc: 'macOS Big Sur Python 3.6 x86_64' } # 10.14 because of error $MACOSX_DEPLOYMENT_TARGET mismatch: now "10.9" but "10.14" during configure. name: ${{ matrix.cfg.desc }} steps: - uses: actions/checkout@v3 @@ -47,6 +47,8 @@ jobs: python -c "import sys; print(sys.version)" echo ${{ matrix.cfg.py_v_maj }}.${{ matrix.cfg.py_v_min }} if: matrix.cfg.py_v_maj!='' + - run: echo "VERBOSE=1" >> $GITHUB_ENV + shell: bash - run: brew install eigen if: runner.os=='macOS' - run: brew install doxygen ; brew install graphviz ; python -m pip install --upgrade pip ; pip install --upgrade wheel setuptools sphinx breathe sphinx_rtd_theme sphinx-tabs sphinx-issues sphinx-reredirects diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 55033ff9d..b713797c7 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -20,7 +20,7 @@ jobs: submodules: true fetch-depth: 0 clean: false - - uses: actions/setup-python@v2 + - uses: actions/setup-python@v4 with: python-version: ${{ matrix.cfg.py_v_maj }}.${{ matrix.cfg.py_v_min }} - run: | diff --git a/.github/workflows/unixmatrix.yml b/.github/workflows/unixmatrix.yml index 8a0d4fcf6..9ebb0bf3b 100644 --- a/.github/workflows/unixmatrix.yml +++ b/.github/workflows/unixmatrix.yml @@ -29,17 +29,17 @@ jobs: matrix: cfg: - { os: windows-2022, shell: cmd, arch: x64, runtime: vc17, cmake_params: '-G "Visual Studio 17" -T v143 -A x64', cmake_flags: ' /wd4267 /wd4244 /wd4305 /wd4996', test_config: 'Release/', desc: 'Windows Visual Studio 2022 x64' } - - { os: windows-2022, shell: cmd, arch: x86, runtime: vc17, cmake_params: '-G "Visual Studio 17" -T v143 -A Win32', choco_flags: '--x86', cmake_flags: ' /wd4267 /wd4244 /wd4305 /wd4996', test_config: 'Release/', desc: 'Windows Visual Studio 2022 x86' } + - { os: windows-2022, shell: cmd, arch: x86, runtime: vc17, cmake_params: '-G "Visual Studio 17" -T v143 -A Win32', cmake_flags: ' /wd4267 /wd4244 /wd4305 /wd4996', test_config: 'Release/', choco_flags: '--x86', desc: 'Windows Visual Studio 2022 x86' } - { os: windows-2022, shell: cmd, arch: x64, runtime: vc16, cmake_params: '-G "Visual Studio 17" -T v142 -A x64', cmake_flags: ' /wd4267 /wd4244 /wd4305 /wd4996', test_config: 'Release/', desc: 'Windows Visual Studio 2019 x64' } - - { os: windows-2022, shell: cmd, arch: x86, runtime: vc16, cmake_params: '-G "Visual Studio 17" -T v142 -A Win32', choco_flags: '--x86', cmake_flags: ' /wd4267 /wd4244 /wd4305 /wd4996', test_config: 'Release/', desc: 'Windows Visual Studio 2019 x86' } + - { os: windows-2022, shell: cmd, arch: x86, runtime: vc16, cmake_params: '-G "Visual Studio 17" -T v142 -A Win32', cmake_flags: ' /wd4267 /wd4244 /wd4305 /wd4996', test_config: 'Release/', choco_flags: '--x86', desc: 'Windows Visual Studio 2019 x86' } - { os: windows-2022, shell: cmd, arch: x64, runtime: vc15, cmake_params: '-G "Visual Studio 17" -T v141 -A x64', cmake_flags: ' /wd4267 /wd4244 /wd4305 /wd4996', test_config: 'Release/', desc: 'Windows Visual Studio 2017 x64' } - - { os: windows-2022, shell: cmd, arch: x86, runtime: vc15, cmake_params: '-G "Visual Studio 17" -T v141 -A Win32', choco_flags: '--x86', cmake_flags: ' /wd4267 /wd4244 /wd4305 /wd4996', test_config: 'Release/', desc: 'Windows Visual Studio 2017 x86' } - - { os: windows-2022, shell: cmd, arch: x64, runtime: mingw11, cmake_params: '-G "MSYS Makefiles"', cmake_flags: '-fPIC', desc: 'Windows MinGW 11.2.0 x64' } - - { os: windows-2022, shell: cmd, arch: x86, runtime: mingw11, cmake_params: '-G "MSYS Makefiles"', cmake_flags: '-fPIC', choco_flags: '--x86', desc: 'Windows MinGW 11.2.0 x86' } - - { os: windows-2019, shell: cmd, arch: x64, runtime: mingw8, cmake_params: '-G "MSYS Makefiles"', cmake_flags: '-fPIC', desc: 'Windows MinGW 8.1.0 x64' } - - { os: windows-2019, shell: cmd, arch: x86, runtime: mingw8, cmake_params: '-G "MSYS Makefiles"', cmake_flags: '-fPIC', choco_flags: '--x86', desc: 'Windows MinGW 8.1.0 x86' } - - { os: windows-2019, shell: cmd, arch: x64, runtime: mingw7, cmake_params: '-G "MSYS Makefiles"', cmake_flags: '-fPIC', desc: 'Windows MinGW 7.3.0 x64' } - - { os: windows-2019, shell: cmd, arch: x86, runtime: mingw7, cmake_params: '-G "MSYS Makefiles"', cmake_flags: '-fPIC', choco_flags: '--x86', desc: 'Windows MinGW 7.3.0 x86' } + - { os: windows-2022, shell: cmd, arch: x86, runtime: vc15, cmake_params: '-G "Visual Studio 17" -T v141 -A Win32', cmake_flags: ' /wd4267 /wd4244 /wd4305 /wd4996', test_config: 'Release/', choco_flags: '--x86', desc: 'Windows Visual Studio 2017 x86' } + - { os: windows-2022, shell: cmd, arch: x64, runtime: mingw11, cmake_params: '-G "MinGW Makefiles"', cmake_flags: '-fPIC', desc: 'Windows MinGW 11.2.0 x64' } + - { os: windows-2022, shell: cmd, arch: x86, runtime: mingw11, cmake_params: '-G "MinGW Makefiles"', cmake_flags: '-fPIC', choco_flags: '--x86', desc: 'Windows MinGW 11.2.0 x86' } + - { os: windows-2019, shell: cmd, arch: x64, runtime: mingw8, cmake_params: '-G "MinGW Makefiles"', cmake_flags: '-fPIC', desc: 'Windows MinGW 8.1.0 x64' } + - { os: windows-2019, shell: cmd, arch: x86, runtime: mingw8, cmake_params: '-G "MinGW Makefiles"', cmake_flags: '-fPIC', choco_flags: '--x86', desc: 'Windows MinGW 8.1.0 x86' } + - { os: windows-2019, shell: cmd, arch: x64, runtime: mingw7, cmake_params: '-G "MinGW Makefiles"', cmake_flags: '-fPIC', desc: 'Windows MinGW 7.3.0 x64' } + - { os: windows-2019, shell: cmd, arch: x86, runtime: mingw7, cmake_params: '-G "MinGW Makefiles"', cmake_flags: '-fPIC', choco_flags: '--x86', desc: 'Windows MinGW 7.3.0 x86' } - { os: ubuntu-22.04, shell: bash, arch: amd64, runtime: jammy, cmake_flags: '-fPIC', desc: 'Ubuntu 22.04' } - { os: ubuntu-20.04, shell: bash, arch: amd64, runtime: focal, cmake_flags: '-fPIC', desc: 'Ubuntu 20.04' } - { os: macos-12, shell: bash, arch: arm64, runtime: monterey, cmake_params: '-D CMAKE_SYSTEM_NAME=Darwin -D CMAKE_OSX_ARCHITECTURES=arm64', cmake_flags: '-fPIC', desc: 'macOS Monterey arm64' } @@ -70,28 +70,31 @@ jobs: - run: echo "PACKAGE_VERSION=$SOFTWARE_VERSION-${DEBIAN_PACKAGE_REV}${{ matrix.cfg.runtime }}$PACKAGE_REV" >> $GITHUB_ENV shell: bash if: runner.os=='Linux' - - run: | - choco install -y -r --no-progress mingw --version=7.3.0 --force ${{ matrix.cfg.choco_flags }} - echo C:\ProgramData\chocolatey\lib\mingw\tools\install\mingw32\bin>>%GITHUB_PATH% + - run: choco install -y -r --no-progress mingw --version=7.3.0 --force ${{ matrix.cfg.choco_flags }} if: (runner.os=='Windows')&&(matrix.cfg.runtime=='mingw7') - - run: | - choco install -y -r --no-progress mingw --version=8.1.0 --force ${{ matrix.cfg.choco_flags }} - echo C:\ProgramData\chocolatey\lib\mingw\tools\install\mingw32\bin>>%GITHUB_PATH% + - run: choco install -y -r --no-progress mingw --version=8.1.0 --force ${{ matrix.cfg.choco_flags }} if: (runner.os=='Windows')&&(matrix.cfg.runtime=='mingw8')&&(matrix.cfg.arch=='x86') - - run: | - choco install -y -r --no-progress mingw --version=11.2.0 --force ${{ matrix.cfg.choco_flags }} - echo C:\ProgramData\chocolatey\lib\mingw\tools\install\mingw32\bin>>%GITHUB_PATH% + - run: choco install -y -r --no-progress mingw --version=11.2.0.07112021 --force ${{ matrix.cfg.choco_flags }} if: (runner.os=='Windows')&&(matrix.cfg.runtime=='mingw11')&&(matrix.cfg.arch=='x86') + - run: echo C:\ProgramData\chocolatey\lib\mingw\tools\install\mingw32\bin>>%GITHUB_PATH% + if: startsWith(matrix.cfg.runtime, 'mingw')&&(matrix.cfg.arch=='x86') + - run: echo C:\ProgramData\chocolatey\lib\mingw\tools\install\mingw64\bin>>%GITHUB_PATH% + if: startsWith(matrix.cfg.runtime, 'mingw')&&(matrix.cfg.arch=='x64') - run: | - choco install -y -r --no-progress checksum wget winflexbison make patch zip ${{ matrix.cfg.choco_flags }} + choco install -y -r --no-progress checksum wget zip choco install -y -r --no-progress eigen --version=3.4.0 ${{ matrix.cfg.choco_flags }} wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20220812/ibex.2.8.9.20220812.nupkg --no-check-certificate -nv choco install -y -r --no-progress --ignore-dependencies -s . ibex --version=2.8.9.20220812 ${{ matrix.cfg.choco_flags }} --params "'/url:https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20220812/ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip'" del /f /q ibex.2.8.9.20220812.nupkg if: runner.os=='Windows' - run: | - sudo sh -c 'echo "deb [trusted=yes] https://packages.ensta-bretagne.fr/`lsb_release --id -s | tr [:upper:] [:lower:]`/`lsb_release -cs` ./" > /etc/apt/sources.list.d/ensta-bretagne.list' - sudo apt-get -q update ; sudo apt-get -y install libibex-dev libeigen3-dev dpkg-dev || true + # Replace these 2 lines by the next ones to test a specific binary package of IBEX. + #sudo sh -c 'echo "deb [trusted=yes] https://packages.ensta-bretagne.fr/`lsb_release --id -s | tr [:upper:] [:lower:]`/`lsb_release -cs` ./" > /etc/apt/sources.list.d/ensta-bretagne.list' + #sudo apt-get -q update ; sudo apt-get -y install libibex-dev libeigen3-dev dpkg-dev || true + sudo apt-get -q update ; sudo apt-get -y install libeigen3-dev dpkg-dev || true + wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20220812/libibex-dev-2.8.9.20220812-0${{ matrix.cfg.runtime }}0_${{ matrix.cfg.arch }}.deb --no-check-certificate -nv + sudo dpkg -i libibex-dev-2.8.9.20220812-0${{ matrix.cfg.runtime }}0_${{ matrix.cfg.arch }}.deb + rm -Rf libibex-dev-2.8.9.20220812-0${{ matrix.cfg.runtime }}0_${{ matrix.cfg.arch }}.deb shell: bash if: runner.os=='Linux' - run: | @@ -99,7 +102,7 @@ jobs: wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20220812/ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip --no-check-certificate -nv unzip -q ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip rm -Rf ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip - cp -Rf ibex/* /usr/local/ + sudo cp -Rf ibex/* /usr/local/ if: runner.os=='macOS' # - run: git clone --depth 1 -b actions https://github.com/lebarsfa/ibex-lib.git ; cd ibex-lib ; mkdir build ; cd build ; cmake -E env CXXFLAGS="${{ matrix.cfg.cmake_flags }}" CFLAGS="${{ matrix.cfg.cmake_flags }}" cmake ${{ matrix.cfg.cmake_params }} -D CMAKE_INSTALL_PREFIX="../../ibex" .. ; cmake --build . --config Release --target install ; cd ../.. # shell: bash @@ -143,7 +146,7 @@ jobs: cd ../../.. checksum -f=codac.$PACKAGE_VERSION.nupkg -t=sha256 choco install -y -r --no-progress --ignore-dependencies -s . codac --params "'/url:./codac_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip'" - if [ ${{ matrix.cfg.runtime }} != mingw8 ] || [ ${{ matrix.cfg.arch }} != x64 ]; then rm -Rf codac.$PACKAGE_VERSION.nupkg ; fi # To avoid upload conflicts of the same file... + if [ ${{ matrix.cfg.runtime }} != mingw11 ] || [ ${{ matrix.cfg.arch }} != x64 ]; then rm -Rf codac.$PACKAGE_VERSION.nupkg ; fi # To avoid upload conflicts of the same file... checksum -f=codac_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip -t=sha256 shell: bash if: runner.os=='Windows' diff --git a/.github/workflows/vcmatrix.yml b/.github/workflows/vcmatrix.yml index 1d59f2e56..701af0d83 100644 --- a/.github/workflows/vcmatrix.yml +++ b/.github/workflows/vcmatrix.yml @@ -45,13 +45,13 @@ jobs: python -c "import sys; print(sys.version)" echo ${{ matrix.cfg.py_v_maj }}.${{ matrix.cfg.py_v_min }} if: matrix.cfg.py_v_maj!='' + - run: echo "VERBOSE=1" >> $GITHUB_ENV + shell: bash - run: | (New-Object System.Net.WebClient).DownloadFile("http://www.ensta-bretagne.fr/lebars/Share/windows_extra_tools.zip", "C:\Windows\Temp\windows_extra_tools.zip") 7z x C:\Windows\Temp\windows_extra_tools.zip -o"C:\Windows" -y shell: pwsh if: runner.os=='Windows' - - run: choco install -y -r --no-progress winflexbison patch - if: startsWith(matrix.cfg.runtime, 'vc') - run: choco install -y -r --no-progress eigen --version=3.4.0 ${{ matrix.cfg.choco_flags }} if: runner.os=='Windows' - run: choco install -y -r --no-progress doxygen.install graphviz & python -m pip install --upgrade pip & pip install --upgrade wheel setuptools & git clone --depth 1 -b v3.1.1 https://github.com/sphinx-doc/sphinx & cd sphinx & pip install . & pip install --upgrade breathe sphinx-issues sphinx-tabs sphinx_rtd_theme sphinx-reredirects diff --git a/scripts/docker/build_pybinding.sh b/scripts/docker/build_pybinding.sh index 67f806577..6f4f844a1 100755 --- a/scripts/docker/build_pybinding.sh +++ b/scripts/docker/build_pybinding.sh @@ -2,6 +2,11 @@ set -e -x +wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20220812/ibex_x86_64_manylinux2010.zip --no-check-certificate -nv +unzip -q ibex_x86_64_manylinux2010.zip +rm -Rf ibex_x86_64_manylinux2010.zip +sudo cp -Rf ibex/* /usr/local/ + git config --global --add safe.directory /io cd /io for PYBIN in /opt/python/cp3*/bin; do From b25ed276eee2fedf2bcdc2283db95ad07776eb8d Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Wed, 10 May 2023 00:05:40 +0200 Subject: [PATCH 080/256] Updated .nupkg --- packages/choco/codac/codac.nuspec | 12 ++- .../codac/tools/chocolateybeforemodify.ps1 | 17 +++- .../choco/codac/tools/chocolateyinstall.ps1 | 91 ++++++++++++++++--- .../choco/codac/tools/chocolateyuninstall.ps1 | 17 +++- packages/choco/codac/tools/sharedVars.ps1 | 1 + 5 files changed, 118 insertions(+), 20 deletions(-) diff --git a/packages/choco/codac/codac.nuspec b/packages/choco/codac/codac.nuspec index 06e556e79..e2ff48c91 100644 --- a/packages/choco/codac/codac.nuspec +++ b/packages/choco/codac/codac.nuspec @@ -27,17 +27,19 @@ Codac is a library providing tools for constraint programming over reals, trajec ## Package parameters The following package parameters can be set: -- `/url:URL` - Will install the specified binary package, see versions from https://github.com/codac-team/codac/releases (the Windows `PATH` might need to be updated manually, etc.). By default, only the MinGW libraries compatible with the corresponding MinGW Chocolatey package dependency are installed. Use the standard parameter `choco install --ignore-dependencies ...` to avoid installing MinGW Chocolatey package dependency if needed. -- `/NoPath` - Will not try to update Windows PATH. +- `/url:URL` - Will install the specified binary package (e.g. built for Visual Studio), see versions from https://github.com/codac-team/codac/releases (the Windows `PATH` might need to be updated manually with e.g. `C:\ProgramData\chocolatey\lib\ibex\bin`, etc.). By default, only the MinGW libraries compatible with the corresponding MinGW Chocolatey package dependency are installed. Use the standard parameter `choco install --ignore-dependencies ...` to avoid installing the default MinGW and IBEX Chocolatey package dependencies if needed (you might want to install manually [IBEX](https://community.chocolatey.org/packages/ibex) package with the corresponding parameters, as well as the corresponding compiler and the Eigen package). +- `/urlX:URL` - Same as above, with X in [1,99], except this will not disable the installation of the MinGW libraries compatible with the corresponding MinGW Chocolatey package dependency. +- `/InstallDir:INSTALLDIR` - Installation directory. +- `/NoPath` - Will not try to update Windows `PATH`. - `/NoRegistry` - Will not try to update Windows registry. -To pass package parameters, use `--params "''"` (e.g. `choco install codac --params "'/NoPath /NoRegistry'"`), e.g. +To pass package parameters, use `--params "''"` (e.g. `choco install codac --params "'/NoPath /NoRegistry'"`), and to install another binary package, try e.g. ``` -choco install -y -f --ignore-dependencies codac --params "'/url:https://github.com/codac-team/codac/releases/download/codac-2.8.9.20220812/codac_x86_vc15.zip'" +choco install -y --ignore-dependencies codac --params "'/url:https://github.com/codac-team/codac/releases/download/codac-1.2.0/codac_x86_vc15.zip'" ``` https://github.com/codac-team/codac/releases - + diff --git a/packages/choco/codac/tools/chocolateybeforemodify.ps1 b/packages/choco/codac/tools/chocolateybeforemodify.ps1 index 37cecf4ea..2ed694ebb 100644 --- a/packages/choco/codac/tools/chocolateybeforemodify.ps1 +++ b/packages/choco/codac/tools/chocolateybeforemodify.ps1 @@ -3,11 +3,24 @@ # Source variables which are shared between install and uninstall. . $PSScriptRoot\sharedVars.ps1 -#Uninstall-BinFile -Name libcodac-rob.a -#Uninstall-BinFile -Name libcodac.a +$toolsDir = "$(Split-Path -parent $MyInvocation.MyCommand.Definition)" + +$pp = Get-PackageParameters +$packageDir = Join-Path "$toolsDir" ".." -Resolve +$installDir = Join-Path "$packageDir" ".." -Resolve +if ($pp.InstallDir -or $pp.InstallationPath) { + $installDir = $pp.InstallDir + $pp.InstallationPath +} +Write-Host "Codac is going to be uninstalled from '$installDir'" + +$root = Join-Path $installDir "codac" if (Test-Path $CMakeRegistryPath) { if (Test-Path $CMakeSystemRepositoryPath\$CMakePackageName) { Remove-Item "$CMakeSystemRepositoryPath\$CMakePackageName" } } + +if (Test-Path $root) { + Remove-Item -Recurse -Force $root +} diff --git a/packages/choco/codac/tools/chocolateyinstall.ps1 b/packages/choco/codac/tools/chocolateyinstall.ps1 index 02b08784c..ed7a24c5d 100644 --- a/packages/choco/codac/tools/chocolateyinstall.ps1 +++ b/packages/choco/codac/tools/chocolateyinstall.ps1 @@ -7,16 +7,23 @@ $toolsDir = "$(Split-Path -parent $MyInvocation.MyCommand.Definition)" $pp = Get-PackageParameters $packageDir = Join-Path "$toolsDir" ".." -Resolve -$root = "$packageDir\.." +$installDir = Join-Path "$packageDir" ".." -Resolve +if ($pp.InstallDir -or $pp.InstallationPath) { + $installDir = $pp.InstallDir + $pp.InstallationPath +} +Write-Host "Codac is going to be installed in '$installDir'" + +$root = Join-Path $installDir "codac" +New-Item -ItemType Directory -Force -Path $root | Out-Null if (!$pp['url']) { - $url = 'https://github.com/lebarsfa/codac/releases/download/codac-1/codac_x86_mingw8.zip' + $url = 'https://github.com/lebarsfa/codac/releases/download/codac-1/codac_x86_mingw$MinGWMVer.zip' $checksum = 'EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE' - $url64 = 'https://github.com/lebarsfa/codac/releases/download/codac-1/codac_x64_mingw8.zip' + $url64 = 'https://github.com/lebarsfa/codac/releases/download/codac-1/codac_x64_mingw$MinGWMVer.zip' $checksum64 = 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF' $packageArgs = @{ packageName = $env:ChocolateyPackageName - unzipLocation = "$root" + unzipLocation = Join-Path "$root" ".." url = $url url64bit = $url64 checksum = $checksum @@ -28,13 +35,12 @@ if (!$pp['url']) { $runtime = "mingw" } -else -{ +else { $url = $pp['url'] #$checksum = $pp['sha256'] $packageArgs = @{ packageName = $env:ChocolateyPackageName - unzipLocation = "$root" + unzipLocation = Join-Path "$root" ".." url = $url #checksum = $checksum #checksumType = 'sha256' @@ -85,13 +91,76 @@ else if (!$pp['NoRegistry']) { New-Item "$CMakeSystemRepositoryPath\$CMakePackageName" -ItemType directory -Force - New-ItemProperty -Name "CMakePackageDir" -PropertyType String -Value "$packageDir\share\$CMakePackageName\cmake" -Path "$CMakeSystemRepositoryPath\$CMakePackageName" -Force + New-ItemProperty -Name "CMakePackageDir" -PropertyType String -Value "$root\share\$CMakePackageName\cmake" -Path "$CMakeSystemRepositoryPath\$CMakePackageName" -Force } -#$pathtoadd = "$packageDir\bin" +#$pathtoadd = "$root\bin" #if (!($pp['NoPath']) -and !([environment]::GetEnvironmentVariable("Path","Machine") -match [regex]::escape($pathtoadd))) { # $newpath = [environment]::GetEnvironmentVariable("Path","Machine") + ";$pathtoadd" # [environment]::SetEnvironmentVariable("Path",$newpath,"Machine") #} -#Install-BinFile -Name libcodac.a -Path "$packageDir\lib" -#Install-BinFile -Name libcodac-rob.a -Path "$packageDir\lib" +for ($i = 1; $i -le 99; $i++) { + if ($pp['url'+$i]) { + $url = $pp['url'+$i] + #$checksum = $pp['sha256'] + $packageArgs = @{ + packageName = $env:ChocolateyPackageName + unzipLocation = Join-Path "$root" ".." + url = $url + #checksum = $checksum + #checksumType = 'sha256' + } + Install-ChocolateyZipPackage @packageArgs + + # Analyze url to guess what to add to Windows PATH... + if ($url -match "x86") { + $arch = "x86" + } + else { + $arch = "x64" + } + if ($url -match "vc8") { + $runtime = "vc8" + } + if ($url -match "vc9") { + $runtime = "vc9" + } + elseif ($url -match "vc10") { + $runtime = "vc10" + } + elseif ($url -match "vc11") { + $runtime = "vc11" + } + elseif ($url -match "vc12") { + $runtime = "vc12" + } + elseif ($url -match "vc14") { + $runtime = "vc14" + } + elseif ($url -match "vc15") { + $runtime = "vc15" + } + elseif ($url -match "vc16") { + $runtime = "vc16" + } + elseif ($url -match "vc17") { + $runtime = "vc17" + } + elseif ($url -match "vc18") { + $runtime = "vc18" + } + else { + $runtime = "mingw" + } + + if (!$pp['NoRegistry']) { + New-Item "$CMakeSystemRepositoryPath\$CMakePackageName" -ItemType directory -Force + New-ItemProperty -Name "CMakePackageDir" -PropertyType String -Value "$root\share\$CMakePackageName\cmake" -Path "$CMakeSystemRepositoryPath\$CMakePackageName" -Force + } + #$pathtoadd = "$root\bin" + #if (!($pp['NoPath']) -and !([environment]::GetEnvironmentVariable("Path","Machine") -match [regex]::escape($pathtoadd))) { + # $newpath = [environment]::GetEnvironmentVariable("Path","Machine") + ";$pathtoadd" + # [environment]::SetEnvironmentVariable("Path",$newpath,"Machine") + #} + } +} diff --git a/packages/choco/codac/tools/chocolateyuninstall.ps1 b/packages/choco/codac/tools/chocolateyuninstall.ps1 index 37cecf4ea..2ed694ebb 100644 --- a/packages/choco/codac/tools/chocolateyuninstall.ps1 +++ b/packages/choco/codac/tools/chocolateyuninstall.ps1 @@ -3,11 +3,24 @@ # Source variables which are shared between install and uninstall. . $PSScriptRoot\sharedVars.ps1 -#Uninstall-BinFile -Name libcodac-rob.a -#Uninstall-BinFile -Name libcodac.a +$toolsDir = "$(Split-Path -parent $MyInvocation.MyCommand.Definition)" + +$pp = Get-PackageParameters +$packageDir = Join-Path "$toolsDir" ".." -Resolve +$installDir = Join-Path "$packageDir" ".." -Resolve +if ($pp.InstallDir -or $pp.InstallationPath) { + $installDir = $pp.InstallDir + $pp.InstallationPath +} +Write-Host "Codac is going to be uninstalled from '$installDir'" + +$root = Join-Path $installDir "codac" if (Test-Path $CMakeRegistryPath) { if (Test-Path $CMakeSystemRepositoryPath\$CMakePackageName) { Remove-Item "$CMakeSystemRepositoryPath\$CMakePackageName" } } + +if (Test-Path $root) { + Remove-Item -Recurse -Force $root +} diff --git a/packages/choco/codac/tools/sharedVars.ps1 b/packages/choco/codac/tools/sharedVars.ps1 index 4fc220aea..77a595455 100644 --- a/packages/choco/codac/tools/sharedVars.ps1 +++ b/packages/choco/codac/tools/sharedVars.ps1 @@ -1,4 +1,5 @@ if ((Get-ProcessorBits 32) -or $env:ChocolateyForceX86 -eq $true) { $arch = "x86" } else { $arch = "x64" } +$MinGWMVer = "11" $CMakeRegistryPath = "HKCU:\SOFTWARE\Kitware\CMake" $CMakeSystemRepositoryPath = "HKLM:\SOFTWARE\Kitware\CMake\Packages" $CMakePackageName = "Codac" From ae74de3199a7d1451f7b1b6b58005d657be86459 Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Thu, 11 May 2023 22:56:10 +0200 Subject: [PATCH 081/256] Documentation updates --- doc/doc/dev/info_dev.rst | 2 +- doc/doc/install/01-installation-full-linux.rst | 2 +- doc/doc/install/01-installation-full-macos.rst | 2 +- doc/doc/install/01-installation-full-windows.rst | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/doc/dev/info_dev.rst b/doc/doc/dev/info_dev.rst index e26a9463c..6f34a1c45 100644 --- a/doc/doc/dev/info_dev.rst +++ b/doc/doc/dev/info_dev.rst @@ -222,4 +222,4 @@ In the :file:`codac` directory, test the Ubuntu configuration locally using Dock .. code-block:: bash chmod a+x scripts/docker/build_pybinding.sh - docker run --rm -v `pwd`:/io lebarsfa/manylinux2010_x86_64-ibex /io/scripts/docker/build_pybinding.sh \ No newline at end of file + docker run --rm -v `pwd`:/io lebarsfa/manylinux2010_x86_64-for-codac /io/scripts/docker/build_pybinding.sh \ No newline at end of file diff --git a/doc/doc/install/01-installation-full-linux.rst b/doc/doc/install/01-installation-full-linux.rst index 3b23d128b..a164c6fef 100644 --- a/doc/doc/install/01-installation-full-linux.rst +++ b/doc/doc/install/01-installation-full-linux.rst @@ -166,4 +166,4 @@ Do not forget to launch the `VIBes viewer <01-installation.html#graphical-tools> export CMAKE_PREFIX_PATH=$CMAKE_PREFIX_PATH:$HOME/ibex-lib/build_install export CMAKE_PREFIX_PATH=$CMAKE_PREFIX_PATH:$HOME/codac/build_install -See also `Information for developers `_. +See also `Information for developers `_. diff --git a/doc/doc/install/01-installation-full-macos.rst b/doc/doc/install/01-installation-full-macos.rst index bb7ed00f3..eb479e55a 100644 --- a/doc/doc/install/01-installation-full-macos.rst +++ b/doc/doc/install/01-installation-full-macos.rst @@ -38,4 +38,4 @@ You will probably need to install those prerequisites (assuming you installed `H brew install eigen -The logic to follow will then be similar to `Linux <01-installation-full-linux.html>`_. See also `Information for developers `_. +The logic to follow will then be similar to `Linux <01-installation-full-linux.html>`_. See also `Information for developers `_. diff --git a/doc/doc/install/01-installation-full-windows.rst b/doc/doc/install/01-installation-full-windows.rst index 674ba81c3..2771e8610 100644 --- a/doc/doc/install/01-installation-full-windows.rst +++ b/doc/doc/install/01-installation-full-windows.rst @@ -52,4 +52,4 @@ The logic to follow will then be similar to `Linux <01-installation-full-linux.h cmake --build . --config Release --target install -See also `Information for developers `_. +See also `Information for developers `_. From 91bbda32d3dd2137b32dba425737f3cb59b35683 Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Thu, 11 May 2023 19:16:48 +0200 Subject: [PATCH 082/256] Test specific IBEX version --- .github/workflows/dockerpi.yml | 2 +- .github/workflows/macosmatrix.yml | 2 +- .github/workflows/unixmatrix.yml | 14 +++++++------- .github/workflows/vcmatrix.yml | 6 +++--- packages/choco/codac/codac.nuspec | 4 ++-- scripts/dependencies/install_ibex.sh | 3 ++- scripts/docker/build_pybinding.sh | 2 +- 7 files changed, 17 insertions(+), 16 deletions(-) diff --git a/.github/workflows/dockerpi.yml b/.github/workflows/dockerpi.yml index df2e8ea3e..5b4767369 100644 --- a/.github/workflows/dockerpi.yml +++ b/.github/workflows/dockerpi.yml @@ -16,7 +16,7 @@ jobs: fetch-depth: 0 clean: false - run: docker run --rm --privileged multiarch/qemu-user-static:register --reset - - run: docker run -i -v "${PWD}/..:${PWD}/.." lebarsfa/pi:buster-for-codac /bin/bash -c "uname -a && cat /etc/os-release && cd ${PWD} && pwd && lsb_release -a && sudo apt-get -q update --allow-releaseinfo-change ; sudo apt-get -y install libeigen3-dev ; wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20220812/ibex_armv6hf_buster.zip --no-check-certificate -nv ; unzip -q ibex_armv6hf_buster.zip && rm -Rf ibex_armv6hf_buster.zip && sudo cp -Rf ibex/* /usr/local/ && mkdir build ; cd build && cmake -E env CXXFLAGS="-fPIC" CFLAGS="-fPIC" cmake -D CMAKE_INSTALL_PREFIX=../codac .. && cmake --build . --target install && cd .. && mkdir -p codac_standalone/example ; cd codac_standalone ; wget https://community.chocolatey.org/api/v2/package/eigen/3.4.0 --no-check-certificate -nv ; unzip -q 3.4.0 -d eigen ; rm -Rf 3.4.0 eigen/*.xml eigen/*.nuspec eigen/_* eigen/package eigen/tools ; mkdir -p ibex/include ; mkdir -p ibex/lib ; mkdir -p ibex/share ; mkdir -p ibex/bin ; cp -Rf /usr/local/include/ibex* ibex/include/ ; cp -Rf /usr/local/lib/*ibex* ibex/lib/ ; cp -Rf /usr/local/share/*ibex* ibex/share/ ; cp -Rf /usr/local/share/pkgconfig ibex/share/ ; cp -Rf /usr/local/bin/ibex* ibex/bin/ ; cp -Rf ../codac . ; cp -Rf ../tests/test_codac/* ./example/ ; cd .. ; zip -q -r codac_standalone_armv6hf_buster.zip codac_standalone ; cd codac_standalone/example && cmake . && cmake --build . && ./my_project" + - run: docker run -i -v "${PWD}/..:${PWD}/.." lebarsfa/pi:buster-for-codac /bin/bash -c "uname -a && cat /etc/os-release && cd ${PWD} && pwd && lsb_release -a && sudo apt-get -q update --allow-releaseinfo-change ; sudo apt-get -y install libeigen3-dev ; wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20230510/ibex_armv6hf_buster.zip --no-check-certificate -nv ; unzip -q ibex_armv6hf_buster.zip && rm -Rf ibex_armv6hf_buster.zip && sudo cp -Rf ibex/* /usr/local/ && mkdir build ; cd build && cmake -E env CXXFLAGS="-fPIC" CFLAGS="-fPIC" cmake -D CMAKE_INSTALL_PREFIX=../codac .. && cmake --build . --target install && cd .. && mkdir -p codac_standalone/example ; cd codac_standalone ; wget https://community.chocolatey.org/api/v2/package/eigen/3.4.0 --no-check-certificate -nv ; unzip -q 3.4.0 -d eigen ; rm -Rf 3.4.0 eigen/*.xml eigen/*.nuspec eigen/_* eigen/package eigen/tools ; mkdir -p ibex/include ; mkdir -p ibex/lib ; mkdir -p ibex/share ; mkdir -p ibex/bin ; cp -Rf /usr/local/include/ibex* ibex/include/ ; cp -Rf /usr/local/lib/*ibex* ibex/lib/ ; cp -Rf /usr/local/share/*ibex* ibex/share/ ; cp -Rf /usr/local/share/pkgconfig ibex/share/ ; cp -Rf /usr/local/bin/ibex* ibex/bin/ ; cp -Rf ../codac . ; cp -Rf ../tests/test_codac/* ./example/ ; cd .. ; zip -q -r codac_standalone_armv6hf_buster.zip codac_standalone ; cd codac_standalone/example && cmake . && cmake --build . && ./my_project" - uses: xresloader/upload-to-github-release@v1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/macosmatrix.yml b/.github/workflows/macosmatrix.yml index 1b076647c..40931aaad 100644 --- a/.github/workflows/macosmatrix.yml +++ b/.github/workflows/macosmatrix.yml @@ -54,7 +54,7 @@ jobs: - run: brew install doxygen ; brew install graphviz ; python -m pip install --upgrade pip ; pip install --upgrade wheel setuptools sphinx breathe sphinx_rtd_theme sphinx-tabs sphinx-issues sphinx-reredirects if: runner.os=='macOS' - run: | - wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20220812/ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip --no-check-certificate -nv + wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20230510/ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip --no-check-certificate -nv unzip -q ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip rm -Rf ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip cp -Rf ibex/* /usr/local/ diff --git a/.github/workflows/unixmatrix.yml b/.github/workflows/unixmatrix.yml index 9ebb0bf3b..67acafd4b 100644 --- a/.github/workflows/unixmatrix.yml +++ b/.github/workflows/unixmatrix.yml @@ -83,23 +83,23 @@ jobs: - run: | choco install -y -r --no-progress checksum wget zip choco install -y -r --no-progress eigen --version=3.4.0 ${{ matrix.cfg.choco_flags }} - wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20220812/ibex.2.8.9.20220812.nupkg --no-check-certificate -nv - choco install -y -r --no-progress --ignore-dependencies -s . ibex --version=2.8.9.20220812 ${{ matrix.cfg.choco_flags }} --params "'/url:https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20220812/ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip'" - del /f /q ibex.2.8.9.20220812.nupkg + wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20230510/ibex.2.8.9.20230510.nupkg --no-check-certificate -nv + choco install -y -r --no-progress --ignore-dependencies -s . ibex --version=2.8.9.20230510 ${{ matrix.cfg.choco_flags }} --params "'/url:https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20230510/ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip'" + del /f /q ibex.2.8.9.20230510.nupkg if: runner.os=='Windows' - run: | # Replace these 2 lines by the next ones to test a specific binary package of IBEX. #sudo sh -c 'echo "deb [trusted=yes] https://packages.ensta-bretagne.fr/`lsb_release --id -s | tr [:upper:] [:lower:]`/`lsb_release -cs` ./" > /etc/apt/sources.list.d/ensta-bretagne.list' #sudo apt-get -q update ; sudo apt-get -y install libibex-dev libeigen3-dev dpkg-dev || true sudo apt-get -q update ; sudo apt-get -y install libeigen3-dev dpkg-dev || true - wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20220812/libibex-dev-2.8.9.20220812-0${{ matrix.cfg.runtime }}0_${{ matrix.cfg.arch }}.deb --no-check-certificate -nv - sudo dpkg -i libibex-dev-2.8.9.20220812-0${{ matrix.cfg.runtime }}0_${{ matrix.cfg.arch }}.deb - rm -Rf libibex-dev-2.8.9.20220812-0${{ matrix.cfg.runtime }}0_${{ matrix.cfg.arch }}.deb + wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20230510/libibex-dev-2.8.9.20230510-0${{ matrix.cfg.runtime }}0_${{ matrix.cfg.arch }}.deb --no-check-certificate -nv + sudo dpkg -i libibex-dev-2.8.9.20230510-0${{ matrix.cfg.runtime }}0_${{ matrix.cfg.arch }}.deb + rm -Rf libibex-dev-2.8.9.20230510-0${{ matrix.cfg.runtime }}0_${{ matrix.cfg.arch }}.deb shell: bash if: runner.os=='Linux' - run: | brew install eigen - wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20220812/ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip --no-check-certificate -nv + wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20230510/ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip --no-check-certificate -nv unzip -q ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip rm -Rf ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip sudo cp -Rf ibex/* /usr/local/ diff --git a/.github/workflows/vcmatrix.yml b/.github/workflows/vcmatrix.yml index 701af0d83..0c18439e8 100644 --- a/.github/workflows/vcmatrix.yml +++ b/.github/workflows/vcmatrix.yml @@ -57,9 +57,9 @@ jobs: - run: choco install -y -r --no-progress doxygen.install graphviz & python -m pip install --upgrade pip & pip install --upgrade wheel setuptools & git clone --depth 1 -b v3.1.1 https://github.com/sphinx-doc/sphinx & cd sphinx & pip install . & pip install --upgrade breathe sphinx-issues sphinx-tabs sphinx_rtd_theme sphinx-reredirects if: runner.os=='Windows' - run: | - wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20220812/ibex.2.8.9.20220812.nupkg --no-check-certificate -nv - choco install -y -r --no-progress --ignore-dependencies -s . ibex --version=2.8.9.20220812 ${{ matrix.cfg.choco_flags }} --params "'/url:https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20220812/ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip'" - del /f /q ibex.2.8.9.20220812.nupkg + wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20230510/ibex.2.8.9.20230510.nupkg --no-check-certificate -nv + choco install -y -r --no-progress --ignore-dependencies -s . ibex --version=2.8.9.20230510 ${{ matrix.cfg.choco_flags }} --params "'/url:https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20230510/ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip'" + del /f /q ibex.2.8.9.20230510.nupkg - run: | mkdir build ; cd build cmake -E env CXXFLAGS=" /wd4267 /wd4244 /wd4305 /wd4996" CFLAGS=" /wd4267 /wd4244 /wd4305 /wd4996" cmake ${{ matrix.cfg.cmake_params }} -D CMAKE_INSTALL_PREFIX="../codac" -D WITH_PYTHON=ON .. diff --git a/packages/choco/codac/codac.nuspec b/packages/choco/codac/codac.nuspec index e2ff48c91..af1939078 100644 --- a/packages/choco/codac/codac.nuspec +++ b/packages/choco/codac/codac.nuspec @@ -34,14 +34,14 @@ The following package parameters can be set: - `/NoRegistry` - Will not try to update Windows registry. To pass package parameters, use `--params "''"` (e.g. `choco install codac --params "'/NoPath /NoRegistry'"`), and to install another binary package, try e.g. ``` -choco install -y --ignore-dependencies codac --params "'/url:https://github.com/codac-team/codac/releases/download/codac-1.2.0/codac_x86_vc15.zip'" +choco install -y --ignore-dependencies codac --params "'/url:https://github.com/codac-team/codac/releases/download/codac-1.2.2/codac_x86_vc15.zip'" ``` https://github.com/codac-team/codac/releases - + diff --git a/scripts/dependencies/install_ibex.sh b/scripts/dependencies/install_ibex.sh index 186fbbfde..a80eb20f1 100644 --- a/scripts/dependencies/install_ibex.sh +++ b/scripts/dependencies/install_ibex.sh @@ -3,7 +3,8 @@ cd $HOME echo 'Installing IBEX in ' $HOME '...'; if [ ! -e "ibex-lib/README.md" ]; then - git clone -b actions https://github.com/lebarsfa/ibex-lib.git ; + #git clone -b actions https://github.com/lebarsfa/ibex-lib.git ; + git clone -b prerelease https://github.com/lebarsfa/ibex-lib.git ; cd ibex-lib ; mkdir build && cd build ; cmake -E env CXXFLAGS="-fPIC" CFLAGS="-fPIC" cmake -DCMAKE_INSTALL_PREFIX=$HOME/ibex-lib/build_install -DCMAKE_BUILD_TYPE=Debug .. ; diff --git a/scripts/docker/build_pybinding.sh b/scripts/docker/build_pybinding.sh index 6f4f844a1..c40c31d1a 100755 --- a/scripts/docker/build_pybinding.sh +++ b/scripts/docker/build_pybinding.sh @@ -2,7 +2,7 @@ set -e -x -wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20220812/ibex_x86_64_manylinux2010.zip --no-check-certificate -nv +wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20230510/ibex_x86_64_manylinux2010.zip --no-check-certificate -nv unzip -q ibex_x86_64_manylinux2010.zip rm -Rf ibex_x86_64_manylinux2010.zip sudo cp -Rf ibex/* /usr/local/ From e200be32ea9b1479eefc57b1a648df86508ca803 Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Sat, 13 May 2023 14:14:00 +0200 Subject: [PATCH 083/256] Removed -pg compiler flag --- src/CMakeLists.txt | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 518cd8b6b..b1fe449f0 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -72,16 +72,6 @@ set(CODAC_C_FLAGS \"\") set(CODAC_CXX_FLAGS \"\") ") -if((CMAKE_BUILD_TYPE MATCHES Debug) AND (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")) -# If Codac is built in Debug, -pg (added by Ibex at the time of Codac -# compilation) might be also necessary to be able to successfully build -# programs in Release (otherwise it would be only added by Ibex if the program -# is built in Debug)... -file(APPEND ${CODAC_CMAKE_CONFIG_FILE} " -set(CMAKE_EXE_LINKER_FLAGS \"${CMAKE_EXE_LINKER_FLAGS} -pg\") -") -endif() - if(WITH_CAPD) file(APPEND ${CODAC_CMAKE_CONFIG_FILE} " From db31b9867ae6859cc48eb4c04a7d338fec267b10 Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Sun, 14 May 2023 02:29:45 +0200 Subject: [PATCH 084/256] Attempt to fix memory corruption in 06_loops_proofs --- src/robotics/loops/codac_TPlane.cpp | 27 ++++++++++++++++++++++----- src/robotics/loops/codac_TPlane.h | 4 ++++ 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/src/robotics/loops/codac_TPlane.cpp b/src/robotics/loops/codac_TPlane.cpp index c2a63ef45..6e5aa4a85 100644 --- a/src/robotics/loops/codac_TPlane.cpp +++ b/src/robotics/loops/codac_TPlane.cpp @@ -17,11 +17,18 @@ using namespace ibex; namespace codac { TPlane::TPlane(const Interval& tdomain) - : Paving(IntervalVector(2, tdomain), SetValue::UNKNOWN) + : Paving(IntervalVector(2, tdomain), SetValue::UNKNOWN), m_first_subtplane(nullptr), m_second_subtplane(nullptr) { } + TPlane::TPlane(const TPlane* t, const Paving* p) + : Paving(*p), m_precision(t->m_precision), m_v_detected_loops(t->m_v_detected_loops), m_v_proven_loops(t->m_v_proven_loops), + m_first_subtplane(t->m_first_subtplane), m_second_subtplane(t->m_second_subtplane) + { + m_verbose = t->m_verbose; + } + void TPlane::compute_loops(float precision, const TubeVector& p, const TubeVector& v) { compute_detections(precision, p, v, true, true); @@ -58,8 +65,12 @@ namespace codac else if(!is_leaf()) { - ((TPlane*)m_first_subpaving)->compute_detections(precision, p, v, with_derivative, false); - ((TPlane*)m_second_subpaving)->compute_detections(precision, p, v, with_derivative, false); + //((TPlane*)m_first_subpaving)->compute_detections(precision, p, v, with_derivative, false); + //((TPlane*)m_second_subpaving)->compute_detections(precision, p, v, with_derivative, false); + m_first_subtplane->compute_detections(precision, p, v, with_derivative, false); + m_first_subpaving = m_first_subtplane; + m_second_subtplane->compute_detections(precision, p, v, with_derivative, false); + m_second_subpaving = m_second_subtplane; } else @@ -120,8 +131,14 @@ namespace codac else { bisect(); - ((TPlane*)m_first_subpaving)->compute_detections(precision, p, v, with_derivative, false); - ((TPlane*)m_second_subpaving)->compute_detections(precision, p, v, with_derivative, false); + //((TPlane*)m_first_subpaving)->compute_detections(precision, p, v, with_derivative, false); + //((TPlane*)m_second_subpaving)->compute_detections(precision, p, v, with_derivative, false); + m_first_subtplane=new TPlane(this, m_first_subpaving); + m_first_subtplane->compute_detections(precision, p, v, with_derivative, false); + m_first_subpaving = m_first_subtplane; + m_second_subtplane=new TPlane(this, m_second_subpaving); + m_second_subtplane->compute_detections(precision, p, v, with_derivative, false); + m_second_subpaving = m_second_subtplane; } } diff --git a/src/robotics/loops/codac_TPlane.h b/src/robotics/loops/codac_TPlane.h index fb1833b6a..2257a53db 100644 --- a/src/robotics/loops/codac_TPlane.h +++ b/src/robotics/loops/codac_TPlane.h @@ -194,9 +194,13 @@ namespace codac */ void compute_detections(float precision, const TubeVector& p, const TubeVector& v, bool with_derivative, bool extract_subsets); + TPlane(const TPlane* t, const Paving* p); + float m_precision = 0.; //!< precision of the SIVIA algorithm, used later on in traj_loops_summary() std::vector m_v_detected_loops; //!< set of loops detections std::vector m_v_proven_loops; //!< set of loops proofs + + TPlane *m_first_subtplane, *m_second_subtplane; static bool m_verbose; }; From 5c486a8f704cf4dbc64fca3f21fa08a6f5a7c2ba Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Sun, 14 May 2023 17:59:09 +0200 Subject: [PATCH 085/256] 2nd attempt to fix memory corruption in 06_loops_proofs --- src/core/paving/codac_Paving.cpp | 31 ++++++++++- src/core/paving/codac_Paving.h | 4 ++ src/robotics/loops/codac_TPlane.cpp | 82 ++++++++++++++++++++++++++--- src/robotics/loops/codac_TPlane.h | 13 ++++- 4 files changed, 119 insertions(+), 11 deletions(-) diff --git a/src/core/paving/codac_Paving.cpp b/src/core/paving/codac_Paving.cpp index 7a0f1c72c..292fa81f6 100644 --- a/src/core/paving/codac_Paving.cpp +++ b/src/core/paving/codac_Paving.cpp @@ -21,10 +21,22 @@ namespace codac // Basics Paving::Paving(const IntervalVector& box, SetValue value) - : Set(box, value), m_root(this) + : Set(box, value), m_flag(false), m_root(this), m_first_subpaving(nullptr), m_second_subpaving(nullptr) { } + + Paving::Paving(const Paving& p) + : Set(p), m_flag(p.m_flag), m_root(p.m_root), m_first_subpaving(nullptr), m_second_subpaving(nullptr) + { + if(p.m_first_subpaving) + { + m_first_subpaving = new Paving(p.m_first_subpaving->m_box, p.m_first_subpaving->m_value); + *m_first_subpaving = *p.m_first_subpaving; + m_second_subpaving = new Paving(p.m_second_subpaving->m_box, p.m_second_subpaving->m_value); + *m_second_subpaving = *p.m_second_subpaving; + } + } Paving::~Paving() { @@ -34,6 +46,23 @@ namespace codac delete m_second_subpaving; } } + + Paving& Paving::operator=(const Paving& p) + { + Set::operator = (p); + m_flag = p.m_flag; + m_root = p.m_root; + m_first_subpaving = nullptr; + m_second_subpaving = nullptr; + if(p.m_first_subpaving) + { + m_first_subpaving = new Paving(p.m_first_subpaving->m_box, p.m_first_subpaving->m_value); + *m_first_subpaving = *p.m_first_subpaving; + m_second_subpaving = new Paving(p.m_second_subpaving->m_box, p.m_second_subpaving->m_value); + *m_second_subpaving = *p.m_second_subpaving; + } + return *this; + } // Binary tree structure diff --git a/src/core/paving/codac_Paving.h b/src/core/paving/codac_Paving.h index d1163eabb..6524c3c8f 100644 --- a/src/core/paving/codac_Paving.h +++ b/src/core/paving/codac_Paving.h @@ -39,11 +39,15 @@ namespace codac */ Paving(const IntervalVector& box, SetValue value = SetValue::UNKNOWN); + Paving(const Paving& p); + /** * \brief Paving destructor */ ~Paving(); + Paving& operator=(const Paving& p); + /// @} /// \name Binary tree structure /// @{ diff --git a/src/robotics/loops/codac_TPlane.cpp b/src/robotics/loops/codac_TPlane.cpp index 6e5aa4a85..a24025cdd 100644 --- a/src/robotics/loops/codac_TPlane.cpp +++ b/src/robotics/loops/codac_TPlane.cpp @@ -17,16 +17,76 @@ using namespace ibex; namespace codac { TPlane::TPlane(const Interval& tdomain) - : Paving(IntervalVector(2, tdomain), SetValue::UNKNOWN), m_first_subtplane(nullptr), m_second_subtplane(nullptr) + : Paving(IntervalVector(2, tdomain), SetValue::UNKNOWN), m_precision(0), m_v_detected_loops(), m_v_proven_loops(), + m_first_subtplane(nullptr), m_second_subtplane(nullptr) { } + + TPlane::TPlane(const TPlane& t) + : Paving(t), m_precision(t.m_precision), m_v_detected_loops(t.m_v_detected_loops), m_v_proven_loops(t.m_v_proven_loops), + m_first_subtplane(nullptr), m_second_subtplane(nullptr) + { + if(t.m_first_subtplane) + { + m_first_subtplane = new TPlane(t.m_first_subtplane->m_box[0]); + *m_first_subtplane = *t.m_first_subtplane; + } + if(t.m_second_subtplane) + { + m_second_subtplane = new TPlane(t.m_second_subtplane->m_box[0]); + *m_second_subtplane = *t.m_second_subtplane; + } + } TPlane::TPlane(const TPlane* t, const Paving* p) : Paving(*p), m_precision(t->m_precision), m_v_detected_loops(t->m_v_detected_loops), m_v_proven_loops(t->m_v_proven_loops), - m_first_subtplane(t->m_first_subtplane), m_second_subtplane(t->m_second_subtplane) + m_first_subtplane(nullptr), m_second_subtplane(nullptr) { m_verbose = t->m_verbose; + if(t->m_first_subtplane) + { + m_first_subtplane = new TPlane(t->m_first_subtplane->m_box[0]); + *m_first_subtplane = *t->m_first_subtplane; + } + if(t->m_second_subtplane) + { + m_second_subtplane = new TPlane(t->m_second_subtplane->m_box[0]); + *m_second_subtplane = *t->m_second_subtplane; + } + } + + TPlane::~TPlane() + { + if(m_first_subtplane) + { + delete m_first_subtplane; + } + if(m_second_subtplane) + { + delete m_second_subtplane; + } + } + + TPlane& TPlane::operator=(const TPlane& t) + { + Paving::operator = (t); + m_precision = t.m_precision; + m_v_detected_loops = t.m_v_detected_loops; + m_v_proven_loops = t.m_v_proven_loops; + m_first_subtplane = nullptr; + m_second_subtplane = nullptr; + if(t.m_first_subtplane) + { + m_first_subtplane = new TPlane(t.m_first_subtplane->m_box[0]); + *m_first_subtplane = *t.m_first_subtplane; + } + if(t.m_second_subtplane) + { + m_second_subtplane = new TPlane(t.m_second_subtplane->m_box[0]); + *m_second_subtplane = *t.m_second_subtplane; + } + return *this; } void TPlane::compute_loops(float precision, const TubeVector& p, const TubeVector& v) @@ -67,10 +127,14 @@ namespace codac { //((TPlane*)m_first_subpaving)->compute_detections(precision, p, v, with_derivative, false); //((TPlane*)m_second_subpaving)->compute_detections(precision, p, v, with_derivative, false); + m_first_subtplane = new TPlane(this, m_first_subpaving); m_first_subtplane->compute_detections(precision, p, v, with_derivative, false); - m_first_subpaving = m_first_subtplane; + *m_first_subpaving = *m_first_subtplane; + delete m_first_subtplane; m_first_subtplane = nullptr; + m_second_subtplane = new TPlane(this, m_second_subpaving); m_second_subtplane->compute_detections(precision, p, v, with_derivative, false); - m_second_subpaving = m_second_subtplane; + *m_second_subpaving = *m_second_subtplane; + delete m_second_subtplane; m_second_subtplane = nullptr; } else @@ -133,12 +197,14 @@ namespace codac bisect(); //((TPlane*)m_first_subpaving)->compute_detections(precision, p, v, with_derivative, false); //((TPlane*)m_second_subpaving)->compute_detections(precision, p, v, with_derivative, false); - m_first_subtplane=new TPlane(this, m_first_subpaving); + m_first_subtplane = new TPlane(this, m_first_subpaving); m_first_subtplane->compute_detections(precision, p, v, with_derivative, false); - m_first_subpaving = m_first_subtplane; - m_second_subtplane=new TPlane(this, m_second_subpaving); + *m_first_subpaving = *m_first_subtplane; + delete m_first_subtplane; m_first_subtplane = nullptr; + m_second_subtplane = new TPlane(this, m_second_subpaving); m_second_subtplane->compute_detections(precision, p, v, with_derivative, false); - m_second_subpaving = m_second_subtplane; + *m_second_subpaving = *m_second_subtplane; + delete m_second_subtplane; m_second_subtplane = nullptr; } } diff --git a/src/robotics/loops/codac_TPlane.h b/src/robotics/loops/codac_TPlane.h index 2257a53db..5866b8bb2 100644 --- a/src/robotics/loops/codac_TPlane.h +++ b/src/robotics/loops/codac_TPlane.h @@ -51,6 +51,17 @@ namespace codac */ TPlane(const Interval& tdomain); + TPlane(const TPlane& t); + + TPlane(const TPlane* t, const Paving* p); + + /** + * \brief TPlane destructor + */ + ~TPlane(); + + TPlane& operator=(const TPlane& t); + /** * \brief Computes the loops (detections and proofs) as a subpaving, from the tube of * positions \f$[\mathbf{p}](\cdot)\f$ and the tube of velocities \f$[\mathbf{v}](\cdot)\f$. @@ -194,8 +205,6 @@ namespace codac */ void compute_detections(float precision, const TubeVector& p, const TubeVector& v, bool with_derivative, bool extract_subsets); - TPlane(const TPlane* t, const Paving* p); - float m_precision = 0.; //!< precision of the SIVIA algorithm, used later on in traj_loops_summary() std::vector m_v_detected_loops; //!< set of loops detections std::vector m_v_proven_loops; //!< set of loops proofs From 7dbc4d797ec6a8d7249c1f0d03d04a5079e12f9d Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Sun, 14 May 2023 22:17:37 +0200 Subject: [PATCH 086/256] Added more python tests for macOS --- .github/workflows/macosmatrix.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/macosmatrix.yml b/.github/workflows/macosmatrix.yml index 40931aaad..29ece58e7 100644 --- a/.github/workflows/macosmatrix.yml +++ b/.github/workflows/macosmatrix.yml @@ -66,7 +66,7 @@ jobs: cmake --build . --config Release --target pip_package ; cp `ls *.whl` ../`ls *.whl | sed "s/py3-none-any/cp${{ matrix.cfg.py_v_maj }}${{ matrix.cfg.py_v_min }}-cp${{ matrix.cfg.py_v_maj }}${{ matrix.cfg.py_v_min }}${{ matrix.cfg.cpcfg }}/"` cd .. shell: bash - - run: pip install *.whl ; python -c "import sys; print(sys.version)" ; python examples/tuto/01_getting_started/01_getting_started.py + - run: pip install *.whl ; python -c "import sys; print(sys.version)" ; python examples/tuto/01_getting_started/01_getting_started.py ; python -m unittest discover codac.tests shell: bash if: (runner.os=='macOS')&&(matrix.cfg.arch=='x86_64') - uses: xresloader/upload-to-github-release@v1 From b7316f5e52f65d29b26bb48db49e6f6c8924e3cc Mon Sep 17 00:00:00 2001 From: SimonRohou Date: Tue, 16 May 2023 09:56:16 +0200 Subject: [PATCH 087/256] [actions] added Actions --- src/core/2/actions/codac2_Action.cpp | 42 ++++++++++++++++ src/core/2/actions/codac2_Action.h | 56 +++++++++++++++++++++ src/core/2/contractors/codac2_CtcAction.cpp | 30 +++++++++++ src/core/2/contractors/codac2_CtcAction.h | 36 +++++++++++++ src/core/CMakeLists.txt | 8 ++- src/core/sivia/codac_sivia.cpp | 9 ++-- 6 files changed, 175 insertions(+), 6 deletions(-) create mode 100644 src/core/2/actions/codac2_Action.cpp create mode 100644 src/core/2/actions/codac2_Action.h create mode 100644 src/core/2/contractors/codac2_CtcAction.cpp create mode 100644 src/core/2/contractors/codac2_CtcAction.h diff --git a/src/core/2/actions/codac2_Action.cpp b/src/core/2/actions/codac2_Action.cpp new file mode 100644 index 000000000..8c259e148 --- /dev/null +++ b/src/core/2/actions/codac2_Action.cpp @@ -0,0 +1,42 @@ +/** + * Action class + * ---------------------------------------------------------------------------- + * \date 2023 + * \author Simon Rohou, Luc Jaulin + * \copyright Copyright 2021 Codac Team + * \license This program is distributed under the terms of + * the GNU Lesser General Public License (LGPL). + */ + +#include "codac2_Action.h" +#include "codac2_CtcAction.h" + +using namespace std; +using namespace codac; + +namespace codac2 +{ + Action::Action(const OctaSym& s) : _s(s), __s(s) + { + + } + + Action::Action(const OctaSym& s, const OctaSym& _s) : _s(s), __s(_s) + { + + } + + IntervalVector Action::operator()(const IntervalVector& x) const + { + assert((size_t)x.size() == _s.size()); + IntervalVector xs(_s.size()); + for(size_t i = 0 ; i < _s.size() ; i++) + xs[i] = sign(_s[i])*x[abs(_s[i])-1]; + return xs; + } + + CtcAction Action::operator()(Ctc& ctc) const + { + return CtcAction(ctc, _s, __s); + } +} \ No newline at end of file diff --git a/src/core/2/actions/codac2_Action.h b/src/core/2/actions/codac2_Action.h new file mode 100644 index 000000000..9c561dd86 --- /dev/null +++ b/src/core/2/actions/codac2_Action.h @@ -0,0 +1,56 @@ +/** + * \file + * + * ---------------------------------------------------------------------------- + * \date 2023 + * \author Simon Rohou, Luc Jaulin + * \copyright Copyright 2022 Codac Team + * \license This program is distributed under the terms of + * the GNU Lesser General Public License (LGPL). + */ + +#ifndef __CODAC2_ACTION_H__ +#define __CODAC2_ACTION_H__ + +#include +#include "codac_IntervalVector.h" +#include "codac_Ctc.h" + +namespace codac2 +{ + class CtcAction; + + /** + * \class OctaSym + */ + class OctaSym : public std::vector + { + public: + + OctaSym(std::initializer_list s) : std::vector(s) {} + }; + + /** + * \class CtcAction + */ + class Action + { + public: + + Action(const OctaSym& s); + Action(const OctaSym& s, const OctaSym& _s); + codac::IntervalVector operator()(const codac::IntervalVector& x) const; + CtcAction operator()(codac::Ctc& ctc) const; + + protected: + + static codac::Interval sign(int a) + { + return (a > 0) ? 1 : ((a < 0) ? -1 : 0); + } + + const OctaSym _s, __s; + }; +} + +#endif \ No newline at end of file diff --git a/src/core/2/contractors/codac2_CtcAction.cpp b/src/core/2/contractors/codac2_CtcAction.cpp new file mode 100644 index 000000000..95d0d0017 --- /dev/null +++ b/src/core/2/contractors/codac2_CtcAction.cpp @@ -0,0 +1,30 @@ +/** + * CtcAction class + * ---------------------------------------------------------------------------- + * \date 2023 + * \author Simon Rohou, Luc Jaulin + * \copyright Copyright 2021 Codac Team + * \license This program is distributed under the terms of + * the GNU Lesser General Public License (LGPL). + */ + +#include "codac2_CtcAction.h" + +using namespace std; +using namespace codac; + +namespace codac2 +{ + CtcAction::CtcAction(Ctc& ctc, const Action& s, const Action& _s) + : Ctc(2), _ctc(ctc), _s(s), __s(_s) + { + + } + + void CtcAction::contract(IntervalVector& x) + { + IntervalVector _x(_s(x)); + _ctc.contract(_x); + x &= __s(_x); + } +} \ No newline at end of file diff --git a/src/core/2/contractors/codac2_CtcAction.h b/src/core/2/contractors/codac2_CtcAction.h new file mode 100644 index 000000000..dbf273925 --- /dev/null +++ b/src/core/2/contractors/codac2_CtcAction.h @@ -0,0 +1,36 @@ +/** + * \file + * + * ---------------------------------------------------------------------------- + * \date 2023 + * \author Simon Rohou, Luc Jaulin + * \copyright Copyright 2022 Codac Team + * \license This program is distributed under the terms of + * the GNU Lesser General Public License (LGPL). + */ + +#ifndef __CODAC2_CTCACTION_H__ +#define __CODAC2_CTCACTION_H__ + +#include "codac2_Action.h" + +namespace codac2 +{ + /** + * \class CtcAction + */ + class CtcAction : public codac::Ctc + { + public: + + CtcAction(Ctc& ctc, const Action& s, const Action& _s); + void contract(codac::IntervalVector& x); + + protected: + + codac::Ctc& _ctc; + const Action _s, __s; + }; +} + +#endif \ No newline at end of file diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 49317ffef..f5c589895 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -195,6 +195,11 @@ ${CMAKE_CURRENT_SOURCE_DIR}/2/contractors/codac2_CtcLinobs.h ${CMAKE_CURRENT_SOURCE_DIR}/2/integration/codac2_AbstractDomain.cpp ${CMAKE_CURRENT_SOURCE_DIR}/2/integration/codac2_AbstractDomain.h + # Actions + ${CMAKE_CURRENT_SOURCE_DIR}/2/actions/codac2_Action.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/2/actions/codac2_Action.h + ${CMAKE_CURRENT_SOURCE_DIR}/2/contractors/codac2_CtcAction.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/2/contractors/codac2_CtcAction.h ) @@ -225,7 +230,8 @@ ${CMAKE_CURRENT_SOURCE_DIR}/sivia ${CMAKE_CURRENT_SOURCE_DIR}/2/contractors ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/tube - ${CMAKE_CURRENT_SOURCE_DIR}/2/integration/) + ${CMAKE_CURRENT_SOURCE_DIR}/2/integration/ + ${CMAKE_CURRENT_SOURCE_DIR}/2/actions/) target_link_libraries(codac PUBLIC Ibex::ibex) diff --git a/src/core/sivia/codac_sivia.cpp b/src/core/sivia/codac_sivia.cpp index b97591f84..b8515a7f3 100644 --- a/src/core/sivia/codac_sivia.cpp +++ b/src/core/sivia/codac_sivia.cpp @@ -58,7 +58,6 @@ namespace codac { _vibes_initialized = true; vibes::beginDrawing(); - vibes::axisAuto(); // will not be ended in case the init has been done outside this SIVIA function } @@ -68,6 +67,7 @@ namespace codac vibes::drawBox(x0); vibes::newGroup("boxes_out", cm.at(SetValue::OUT)); vibes::newGroup("boxes_unknown", cm.at(SetValue::UNKNOWN)); + vibes::axisAuto(); } map n_boxes; @@ -86,10 +86,9 @@ namespace codac while(!stack.empty()) { k++; - IntervalVector *xx = new IntervalVector(stack.front()); - IntervalVector &x = *xx; + IntervalVector x_before_ctc = stack.front(); stack.pop_front(); - IntervalVector x_before_ctc(x); + IntervalVector x(x_before_ctc); ctc.contract(x); @@ -188,7 +187,6 @@ namespace codac { _vibes_initialized = true; vibes::beginDrawing(); - vibes::axisAuto(); // will not be ended in case the init has been done outside this SIVIA function } @@ -199,6 +197,7 @@ namespace codac vibes::newGroup("boxes_out", cm.at(SetValue::OUT)); vibes::newGroup("boxes_unknown", cm.at(SetValue::UNKNOWN)); vibes::newGroup("boxes_in", cm.at(SetValue::IN)); + vibes::axisAuto(); } map n_boxes; From ecb49e7b3c4f2b197d856254ef4cd5bd52cdc731 Mon Sep 17 00:00:00 2001 From: SimonRohou Date: Tue, 16 May 2023 11:21:15 +0200 Subject: [PATCH 088/256] [py] created codac2 module --- python/CMakeLists.txt | 23 ++++++ python/codac_py_codac2.cpp | 32 +++++++++ python/setup.py.in | 1 + .../src/core/2/actions/codac2_py_Action.cpp | 72 +++++++++++++++++++ .../2/contractors/codac2_py_CtcAction.cpp | 41 +++++++++++ .../interval/codac_py_IntervalVector.cpp | 4 ++ src/core/2/actions/codac2_Action.h | 2 +- 7 files changed, 174 insertions(+), 1 deletion(-) create mode 100644 python/codac_py_codac2.cpp create mode 100644 python/src/core/2/actions/codac2_py_Action.cpp create mode 100644 python/src/core/2/contractors/codac2_py_CtcAction.cpp diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt index 12d3baafb..d663dc78d 100644 --- a/python/CMakeLists.txt +++ b/python/CMakeLists.txt @@ -159,6 +159,29 @@ COMMAND ${CMAKE_COMMAND} -E copy "$" "${PYTHON_PACKAGE_DIR}/${PYTHON_PACKAGE_NAME}" ) + # Generating a library codac2.so containing the python binding: + + pybind11_add_module(codac2 SHARED + codac_py_codac2.cpp + src/core/2/actions/codac2_py_Action.cpp + src/core/2/contractors/codac2_py_CtcAction.cpp + ) + + target_include_directories(codac2 + PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/../include + PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/ + ${CMAKE_CURRENT_BINARY_DIR}/docstring + ) + + target_link_libraries(codac2 + PRIVATE core codac core ${LIBS} core + ) + + # Copy the generated library in the package folder + add_custom_command(TARGET codac2 POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy "$" "${PYTHON_PACKAGE_DIR}/${PYTHON_PACKAGE_NAME}" + ) + #pybind11_add_module(graphics SHARED # codac_py_VIBesFig.cpp diff --git a/python/codac_py_codac2.cpp b/python/codac_py_codac2.cpp new file mode 100644 index 000000000..c49aea982 --- /dev/null +++ b/python/codac_py_codac2.cpp @@ -0,0 +1,32 @@ +/** + * \file + * Codac binding (codac2) + * ---------------------------------------------------------------------------- + * \date 2023 + * \author Simon Rohou + * \copyright Copyright 2023 Codac Team + * \license This program is distributed under the terms of + * the GNU Lesser General Public License (LGPL). + */ + +#include +#include +#include "src/core/contractors/static/codac_py_Ctc.h" + +#include + +using namespace codac; +namespace py = pybind11; + +void export_Action(py::module& m); +void export_CtcAction(py::module& m, py::class_& ctc); + +PYBIND11_MODULE(codac2, m) +{ + m.doc() = "Python binding of Codac (codac2)"; + + py::class_ ctc = (py::class_)py::module::import("codac").attr("Ctc"); + + export_Action(m); + export_CtcAction(m, ctc); +} \ No newline at end of file diff --git a/python/setup.py.in b/python/setup.py.in index 21f491477..49da05ea6 100644 --- a/python/setup.py.in +++ b/python/setup.py.in @@ -25,6 +25,7 @@ setup( '${PYTHON_PACKAGE_NAME}': [ 'core${PYTHON_MODULE_EXTENSION}', 'unsupported${PYTHON_MODULE_EXTENSION}', + 'codac2${PYTHON_MODULE_EXTENSION}', #'graphics${PYTHON_MODULE_EXTENSION}', ], }, diff --git a/python/src/core/2/actions/codac2_py_Action.cpp b/python/src/core/2/actions/codac2_py_Action.cpp new file mode 100644 index 000000000..9e418486e --- /dev/null +++ b/python/src/core/2/actions/codac2_py_Action.cpp @@ -0,0 +1,72 @@ +/** + * \file + * Action Python binding + * ---------------------------------------------------------------------------- + * \date 2020 + * \author Simon Rohou, Benoît Desrochers + * \copyright Copyright 2021 Codac Team + * \license This program is distributed under the terms of + * the GNU Lesser General Public License (LGPL). + */ + +#include +#include +#include +#include +#include +#include "codac_type_caster.h" + +#include "codac2_Action.h" +#include "codac2_CtcAction.h" + +using namespace std; +using namespace codac; +using namespace codac2; +namespace py = pybind11; +using namespace pybind11::literals; + + +void export_Action(py::module& m) +{ + py::class_ octasym(m, "OctaSym", "todo"); + octasym + + .def(py::init([](py::array_t s) + { + // Request a buffer descriptor from Python + py::buffer_info info = s.request(); + + // Some sanity checks... + if(info.format != py::format_descriptor::format()) + throw std::runtime_error("Incompatible format: expected a int array"); + + //if(info.ndim == 1 || // e.g.: a=np.array([1,0,0]) + // (info.ndim == 2 && (int)info.shape[1] == 1)) // e.g.: a=np.array([[1],[0],[0]]) + { + //ibex::Vector m(, static_cast(info.ptr)); + + int len = (int)info.shape[0]; + int* w = static_cast(info.ptr); + + vector s; + s.assign(w, w + len); + return OctaSym(s); + } + })) + ; + + py::class_ action(m, "Action", "todo"); + action + + .def(py::init(), "todo", "s"_a) + .def(py::init(), "todo", "s"_a, "_s"_a) + + .def("__call__", [](Action& s,const codac::IntervalVector& x) { return s(x); }, + "todo", + py::return_value_policy::reference_internal) + + .def("__call__", [](Action& s,codac::Ctc& ctc) { return s(ctc); }, + "todo", + py::return_value_policy::reference_internal) + ; +} \ No newline at end of file diff --git a/python/src/core/2/contractors/codac2_py_CtcAction.cpp b/python/src/core/2/contractors/codac2_py_CtcAction.cpp new file mode 100644 index 000000000..fbabf3a6c --- /dev/null +++ b/python/src/core/2/contractors/codac2_py_CtcAction.cpp @@ -0,0 +1,41 @@ +/** + * \file + * CtcBox Python binding + * ---------------------------------------------------------------------------- + * \date 2020 + * \author Simon Rohou, Benoît Desrochers + * \copyright Copyright 2021 Codac Team + * \license This program is distributed under the terms of + * the GNU Lesser General Public License (LGPL). + */ + +#include +#include +#include +#include +#include "codac_type_caster.h" + +#include "../../contractors/static/codac_py_Ctc.h" +#include "codac2_CtcAction.h" + +using namespace std; +using namespace codac; +using namespace codac2; +namespace py = pybind11; +using namespace pybind11::literals; + + +void export_CtcAction(py::module& m, py::class_& ctc) +{ + py::class_ ctc_action(m, "CtcAction", ctc, "todo"); + ctc_action + + .def(py::init(), + "todo", + "ctc"_a, "s"_a, "_s"_a) + + .def("contract", &CtcAction::contract, + "todo", + "x"_a.noconvert()) + ; +} \ No newline at end of file diff --git a/python/src/core/domains/interval/codac_py_IntervalVector.cpp b/python/src/core/domains/interval/codac_py_IntervalVector.cpp index 01b6ca28e..2e668ccf4 100644 --- a/python/src/core/domains/interval/codac_py_IntervalVector.cpp +++ b/python/src/core/domains/interval/codac_py_IntervalVector.cpp @@ -336,4 +336,8 @@ void export_IntervalVector(py::module& m) m.def("bwd_mul", (bool (*) (const Interval&, IntervalVector&, IntervalVector&)) &ibex::bwd_mul); m.def("max", (IntervalVector(*) (const IntervalVector&, const IntervalVector&)) &max_IntevalVector); + + // Automatic cast from lists to IntervalVectors (used for instance in SIVIA calls) + py::implicitly_convertible(); + }; \ No newline at end of file diff --git a/src/core/2/actions/codac2_Action.h b/src/core/2/actions/codac2_Action.h index 9c561dd86..5c575e0ca 100644 --- a/src/core/2/actions/codac2_Action.h +++ b/src/core/2/actions/codac2_Action.h @@ -27,7 +27,7 @@ namespace codac2 { public: - OctaSym(std::initializer_list s) : std::vector(s) {} + OctaSym(const std::vector& s) : std::vector(s) {} }; /** From 59439a07f45466d55315a2d7839c0f149c1edf22 Mon Sep 17 00:00:00 2001 From: SimonRohou Date: Tue, 16 May 2023 14:47:46 +0200 Subject: [PATCH 089/256] [action] auto cast list -> OctaSym --- python/src/core/2/actions/codac2_py_Action.cpp | 3 +++ src/core/graphics/codac_VIBesFig.h | 1 + 2 files changed, 4 insertions(+) diff --git a/python/src/core/2/actions/codac2_py_Action.cpp b/python/src/core/2/actions/codac2_py_Action.cpp index 9e418486e..be7110617 100644 --- a/python/src/core/2/actions/codac2_py_Action.cpp +++ b/python/src/core/2/actions/codac2_py_Action.cpp @@ -55,6 +55,9 @@ void export_Action(py::module& m) })) ; + // Automatic cast from lists to OctaSym + py::implicitly_convertible(); + py::class_ action(m, "Action", "todo"); action diff --git a/src/core/graphics/codac_VIBesFig.h b/src/core/graphics/codac_VIBesFig.h index 82697a76c..21be6ce36 100644 --- a/src/core/graphics/codac_VIBesFig.h +++ b/src/core/graphics/codac_VIBesFig.h @@ -19,6 +19,7 @@ #include "codac_ConvexPolygon.h" #include "codac_ColorMap.h" #include "codac_ConvexPolygon.h" +#include "codac_polygon_arithmetic.h" #include "vibes.h" namespace codac2 From c06a41f3980de47e7906aabb43fde6e3018effca Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Tue, 16 May 2023 17:33:40 +0200 Subject: [PATCH 090/256] Correct python tests for macOS --- .github/workflows/macosmatrix.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/macosmatrix.yml b/.github/workflows/macosmatrix.yml index 29ece58e7..fa842a1ed 100644 --- a/.github/workflows/macosmatrix.yml +++ b/.github/workflows/macosmatrix.yml @@ -66,7 +66,7 @@ jobs: cmake --build . --config Release --target pip_package ; cp `ls *.whl` ../`ls *.whl | sed "s/py3-none-any/cp${{ matrix.cfg.py_v_maj }}${{ matrix.cfg.py_v_min }}-cp${{ matrix.cfg.py_v_maj }}${{ matrix.cfg.py_v_min }}${{ matrix.cfg.cpcfg }}/"` cd .. shell: bash - - run: pip install *.whl ; python -c "import sys; print(sys.version)" ; python examples/tuto/01_getting_started/01_getting_started.py ; python -m unittest discover codac.tests + - run: pip install *.whl ; python -c "import sys; print(sys.version)" ; python examples/tuto/01_getting_started/01_getting_started.py ; pip install numpy ; python -m unittest discover codac.tests shell: bash if: (runner.os=='macOS')&&(matrix.cfg.arch=='x86_64') - uses: xresloader/upload-to-github-release@v1 From 5c6b40d2a729412153fa6a6caaefb286c7ba02d7 Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Wed, 17 May 2023 00:08:11 +0200 Subject: [PATCH 091/256] Correct Eigen3 in codac_standalone --- .github/workflows/unixmatrix.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/unixmatrix.yml b/.github/workflows/unixmatrix.yml index 67acafd4b..8c05f072d 100644 --- a/.github/workflows/unixmatrix.yml +++ b/.github/workflows/unixmatrix.yml @@ -117,7 +117,7 @@ jobs: shell: bash - run: | mkdir -p codac_standalone/example ; cd codac_standalone - wget https://community.chocolatey.org/api/v2/package/eigen/3.4.0 --no-check-certificate -nv ; unzip -q 3.4.0 -d eigen ; rm -Rf 3.4.0 eigen/*.xml eigen/*.nuspec eigen/_* eigen/package eigen/tools + wget https://community.chocolatey.org/api/v2/package/eigen/3.4.0 --no-check-certificate -nv ; unzip -q 3.4.0 -d eigen ; rm -Rf 3.4.0 eigen/*.xml eigen/*.nuspec eigen/_* eigen/package eigen/tools ; cd eigen/share/cmake ; if [ ${{ runner.os }} = macOS ]; then sed -i "" "s/\\/..\\/../\\/..\\/..\\/../" *.cmake ; else sed -i "s/\\/..\\/../\\/..\\/..\\/../" *.cmake ; fi ; cd .. ; mkdir eigen3 ; mv cmake eigen3/ ; cd ../.. if [ ${{ runner.os }} = Windows ]; then cp -Rf /C/ProgramData/chocolatey/lib/ibex . ; rm -Rf ibex/tools ibex/ibex.* elif [ ${{ runner.os }} = Linux ]; then mkdir -p ibex/include ; mkdir -p ibex/lib ; mkdir -p ibex/share ; mkdir -p ibex/bin ; cp -Rf /usr/include/ibex* ibex/include/ ; cp -Rf /usr/lib/*ibex* ibex/lib/ ; cp -Rf /usr/share/*ibex* ibex/share/ ; cp -Rf /usr/share/pkgconfig ibex/share/ ; cp -Rf /usr/bin/ibex* ibex/bin/ elif [ ${{ runner.os }} = macOS ]; then cp -Rf ../ibex . From 45e86e0cb69b917f402707c293439edbb80022f3 Mon Sep 17 00:00:00 2001 From: SimonRohou Date: Wed, 17 May 2023 15:14:20 +0200 Subject: [PATCH 092/256] [actions] OctaSym and invert --- doc/doc/manual/11-separators/index.rst | 2 ++ .../src/core/2/actions/codac2_py_Action.cpp | 20 +++++------- .../2/contractors/codac2_py_CtcAction.cpp | 4 +-- src/core/2/actions/codac2_Action.cpp | 32 +++++++++++-------- src/core/2/actions/codac2_Action.h | 31 ++++++++---------- src/core/2/contractors/codac2_CtcAction.cpp | 4 +-- src/core/2/contractors/codac2_CtcAction.h | 4 +-- 7 files changed, 48 insertions(+), 49 deletions(-) diff --git a/doc/doc/manual/11-separators/index.rst b/doc/doc/manual/11-separators/index.rst index 58e8a1f18..7b6f16820 100644 --- a/doc/doc/manual/11-separators/index.rst +++ b/doc/doc/manual/11-separators/index.rst @@ -76,6 +76,7 @@ The ``SIVIA(..)`` function allows several parameters for specifying result outpu SIVIA(box, # initial domain to be bisected sep, # related separator (or contractor if provided) 0.21, # precision parameter (stopping condition) + regular_paving=False, # keep a regular paving (bisection rule) display_result=True, # displaying boxes in a VIBes figure fig_name="SIVIA", # name of the VIBes figure in case of display return_result=True, # returning a map of lists of boxes @@ -87,6 +88,7 @@ The ``SIVIA(..)`` function allows several parameters for specifying result outpu SIVIA(box, // initial domain to be bisected sep, // related separator (or contractor if provided) 0.21, // precision parameter (stopping condition) + false, // keep a regular paving (bisection rule) true, // boolean for displaying boxes in a VIBes figure "SIVIA", // name of the VIBes figure in case of display true, // boolean for returning a map of lists of boxes diff --git a/python/src/core/2/actions/codac2_py_Action.cpp b/python/src/core/2/actions/codac2_py_Action.cpp index be7110617..ddc4a8405 100644 --- a/python/src/core/2/actions/codac2_py_Action.cpp +++ b/python/src/core/2/actions/codac2_py_Action.cpp @@ -53,23 +53,19 @@ void export_Action(py::module& m) return OctaSym(s); } })) - ; - - // Automatic cast from lists to OctaSym - py::implicitly_convertible(); - - py::class_ action(m, "Action", "todo"); - action - .def(py::init(), "todo", "s"_a) - .def(py::init(), "todo", "s"_a, "_s"_a) - - .def("__call__", [](Action& s,const codac::IntervalVector& x) { return s(x); }, + .def("__call__", [](OctaSym& s,const codac::IntervalVector& x) { return s(x); }, "todo", py::return_value_policy::reference_internal) - .def("__call__", [](Action& s,codac::Ctc& ctc) { return s(ctc); }, + .def("__call__", [](OctaSym& s,codac::Ctc& ctc) { return s(ctc); }, "todo", py::return_value_policy::reference_internal) + + .def("invert", &OctaSym::invert, + "todo") ; + + // Automatic cast from lists to OctaSym + py::implicitly_convertible(); } \ No newline at end of file diff --git a/python/src/core/2/contractors/codac2_py_CtcAction.cpp b/python/src/core/2/contractors/codac2_py_CtcAction.cpp index fbabf3a6c..de98fba76 100644 --- a/python/src/core/2/contractors/codac2_py_CtcAction.cpp +++ b/python/src/core/2/contractors/codac2_py_CtcAction.cpp @@ -30,9 +30,9 @@ void export_CtcAction(py::module& m, py::class_& ctc) py::class_ ctc_action(m, "CtcAction", ctc, "todo"); ctc_action - .def(py::init(), + .def(py::init(), "todo", - "ctc"_a, "s"_a, "_s"_a) + "ctc"_a, "s"_a) .def("contract", &CtcAction::contract, "todo", diff --git a/src/core/2/actions/codac2_Action.cpp b/src/core/2/actions/codac2_Action.cpp index 8c259e148..90468ab59 100644 --- a/src/core/2/actions/codac2_Action.cpp +++ b/src/core/2/actions/codac2_Action.cpp @@ -1,5 +1,5 @@ /** - * Action class + * OctaSym class * ---------------------------------------------------------------------------- * \date 2023 * \author Simon Rohou, Luc Jaulin @@ -16,27 +16,33 @@ using namespace codac; namespace codac2 { - Action::Action(const OctaSym& s) : _s(s), __s(s) + OctaSym::OctaSym(const std::vector& s) : std::vector(s) { - + for(const auto& i : s) + { + assert((size_t)abs(i) > 0 && (size_t)abs(i) <= size()); + } } - Action::Action(const OctaSym& s, const OctaSym& _s) : _s(s), __s(_s) + IntervalVector OctaSym::operator()(const IntervalVector& x) const { - + assert((size_t)x.size() == size()); + IntervalVector xs(size()); + for(size_t i = 0 ; i < size() ; i++) + xs[i] = sign((*this)[i])*x[abs((*this)[i])-1]; + return xs; } - IntervalVector Action::operator()(const IntervalVector& x) const + OctaSym OctaSym::invert() const { - assert((size_t)x.size() == _s.size()); - IntervalVector xs(_s.size()); - for(size_t i = 0 ; i < _s.size() ; i++) - xs[i] = sign(_s[i])*x[abs(_s[i])-1]; - return xs; + vector inv(size()); + for(size_t i = 0 ; i < size() ; i++) + inv[abs((int)(*this)[i])-1] = (abs((int)i)+1)*((*this)[i] >= 0 ? 1. : -1.); + return OctaSym(inv); } - CtcAction Action::operator()(Ctc& ctc) const + CtcAction OctaSym::operator()(Ctc& ctc) const { - return CtcAction(ctc, _s, __s); + return CtcAction(ctc, *this); } } \ No newline at end of file diff --git a/src/core/2/actions/codac2_Action.h b/src/core/2/actions/codac2_Action.h index 5c575e0ca..895e4e8b2 100644 --- a/src/core/2/actions/codac2_Action.h +++ b/src/core/2/actions/codac2_Action.h @@ -21,26 +21,10 @@ namespace codac2 class CtcAction; /** - * \class OctaSym - */ - class OctaSym : public std::vector - { - public: - - OctaSym(const std::vector& s) : std::vector(s) {} - }; - - /** - * \class CtcAction + * \class Action */ class Action { - public: - - Action(const OctaSym& s); - Action(const OctaSym& s, const OctaSym& _s); - codac::IntervalVector operator()(const codac::IntervalVector& x) const; - CtcAction operator()(codac::Ctc& ctc) const; protected: @@ -48,8 +32,19 @@ namespace codac2 { return (a > 0) ? 1 : ((a < 0) ? -1 : 0); } + }; + + /** + * \class OctaSym + */ + class OctaSym : public std::vector, public Action + { + public: - const OctaSym _s, __s; + OctaSym(const std::vector& s); + CtcAction operator()(codac::Ctc& ctc) const; + codac::IntervalVector operator()(const codac::IntervalVector& x) const; + OctaSym invert() const; }; } diff --git a/src/core/2/contractors/codac2_CtcAction.cpp b/src/core/2/contractors/codac2_CtcAction.cpp index 95d0d0017..c6311768f 100644 --- a/src/core/2/contractors/codac2_CtcAction.cpp +++ b/src/core/2/contractors/codac2_CtcAction.cpp @@ -15,8 +15,8 @@ using namespace codac; namespace codac2 { - CtcAction::CtcAction(Ctc& ctc, const Action& s, const Action& _s) - : Ctc(2), _ctc(ctc), _s(s), __s(_s) + CtcAction::CtcAction(Ctc& ctc, const OctaSym& s) + : Ctc(2), _ctc(ctc), _s(s), __s(s.invert()) { } diff --git a/src/core/2/contractors/codac2_CtcAction.h b/src/core/2/contractors/codac2_CtcAction.h index dbf273925..f6816c332 100644 --- a/src/core/2/contractors/codac2_CtcAction.h +++ b/src/core/2/contractors/codac2_CtcAction.h @@ -23,13 +23,13 @@ namespace codac2 { public: - CtcAction(Ctc& ctc, const Action& s, const Action& _s); + CtcAction(Ctc& ctc, const OctaSym& s); void contract(codac::IntervalVector& x); protected: codac::Ctc& _ctc; - const Action _s, __s; + const OctaSym _s, __s; }; } From d25410f0a618162b7dab80a6ebf089ffd44c59c9 Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Sat, 20 May 2023 23:10:49 +0200 Subject: [PATCH 093/256] Remove sphinx specific handling in vcmatrix.yml --- .github/workflows/vcmatrix.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/vcmatrix.yml b/.github/workflows/vcmatrix.yml index 0c18439e8..e3765ac9d 100644 --- a/.github/workflows/vcmatrix.yml +++ b/.github/workflows/vcmatrix.yml @@ -54,7 +54,7 @@ jobs: if: runner.os=='Windows' - run: choco install -y -r --no-progress eigen --version=3.4.0 ${{ matrix.cfg.choco_flags }} if: runner.os=='Windows' - - run: choco install -y -r --no-progress doxygen.install graphviz & python -m pip install --upgrade pip & pip install --upgrade wheel setuptools & git clone --depth 1 -b v3.1.1 https://github.com/sphinx-doc/sphinx & cd sphinx & pip install . & pip install --upgrade breathe sphinx-issues sphinx-tabs sphinx_rtd_theme sphinx-reredirects + - run: choco install -y -r --no-progress doxygen.install graphviz & pip install --upgrade wheel setuptools sphinx breathe sphinx-issues sphinx-tabs sphinx_rtd_theme sphinx-reredirects if: runner.os=='Windows' - run: | wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20230510/ibex.2.8.9.20230510.nupkg --no-check-certificate -nv From f25e2cad989549a41f7fc9c1de134fa304a55b21 Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Sat, 20 May 2023 23:12:27 +0200 Subject: [PATCH 094/256] Added more python tests for Windows --- .github/workflows/vcmatrix.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/vcmatrix.yml b/.github/workflows/vcmatrix.yml index e3765ac9d..10015d8a5 100644 --- a/.github/workflows/vcmatrix.yml +++ b/.github/workflows/vcmatrix.yml @@ -64,9 +64,11 @@ jobs: mkdir build ; cd build cmake -E env CXXFLAGS=" /wd4267 /wd4244 /wd4305 /wd4996" CFLAGS=" /wd4267 /wd4244 /wd4305 /wd4996" cmake ${{ matrix.cfg.cmake_params }} -D CMAKE_INSTALL_PREFIX="../codac" -D WITH_PYTHON=ON .. cmake --build . --config Release --target install - cmake --build . --config Release --target pip_package ; cp `ls *.whl` ../`ls *.whl | sed "s/py3-none-any/cp${{ matrix.cfg.py_v_maj }}${{ matrix.cfg.py_v_min }}-cp${{ matrix.cfg.py_v_maj }}${{ matrix.cfg.py_v_min }}${{ matrix.cfg.cpcfg }}/"` ; pip install ../*.whl ; python -c "import sys; print(sys.version)" ; python ../examples/tuto/01_getting_started/01_getting_started.py + cmake --build . --config Release --target pip_package ; cp `ls *.whl` ../`ls *.whl | sed "s/py3-none-any/cp${{ matrix.cfg.py_v_maj }}${{ matrix.cfg.py_v_min }}-cp${{ matrix.cfg.py_v_maj }}${{ matrix.cfg.py_v_min }}${{ matrix.cfg.cpcfg }}/"` cd .. shell: bash + - run: pip install *.whl ; python -c "import sys; print(sys.version)" ; python examples/tuto/01_getting_started/01_getting_started.py ; pip install numpy ; python -m unittest discover codac.tests + shell: bash - uses: xresloader/upload-to-github-release@v1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 66bba9f6adb20a87922fc2623ed2c3a32c20515e Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Sun, 21 May 2023 14:05:47 +0200 Subject: [PATCH 095/256] Improve portability of Python tests --- python/codac/tests/test_definition.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/python/codac/tests/test_definition.py b/python/codac/tests/test_definition.py index 0ac06d30a..d001dbe69 100644 --- a/python/codac/tests/test_definition.py +++ b/python/codac/tests/test_definition.py @@ -8,6 +8,8 @@ # \license This program is distributed under the terms of # the GNU Lesser General Public License (LGPL). +import os +from tempfile import gettempdir import unittest from codac import * import codac as codac @@ -24,7 +26,7 @@ def test_Init(self): #self.assertTrue(cos(self.tube).codomain() == cos(Interval(10.,11.))) def test_Serialize(self): - self.tube.serialize("/tmp/x.tube") + self.tube.serialize(os.path.join(gettempdir(), "x.tube")) def test_Slice_class(self): From b253b0d7bf957a0511f624f1d15bd6ddaed144e6 Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Sun, 21 May 2023 15:27:01 +0200 Subject: [PATCH 096/256] Test specific IBEX version --- .github/workflows/unixmatrix.yml | 2 +- doc/doc/install/01-installation-full-linux.rst | 4 ++-- scripts/dependencies/install_ibex.sh | 3 +-- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/.github/workflows/unixmatrix.yml b/.github/workflows/unixmatrix.yml index 8c05f072d..376f17497 100644 --- a/.github/workflows/unixmatrix.yml +++ b/.github/workflows/unixmatrix.yml @@ -104,7 +104,7 @@ jobs: rm -Rf ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip sudo cp -Rf ibex/* /usr/local/ if: runner.os=='macOS' -# - run: git clone --depth 1 -b actions https://github.com/lebarsfa/ibex-lib.git ; cd ibex-lib ; mkdir build ; cd build ; cmake -E env CXXFLAGS="${{ matrix.cfg.cmake_flags }}" CFLAGS="${{ matrix.cfg.cmake_flags }}" cmake ${{ matrix.cfg.cmake_params }} -D CMAKE_INSTALL_PREFIX="../../ibex" .. ; cmake --build . --config Release --target install ; cd ../.. +# - run: git clone --depth 1 -b master https://github.com/lebarsfa/ibex-lib.git ; cd ibex-lib ; mkdir build ; cd build ; cmake -E env CXXFLAGS="${{ matrix.cfg.cmake_flags }}" CFLAGS="${{ matrix.cfg.cmake_flags }}" cmake ${{ matrix.cfg.cmake_params }} -D CMAKE_INSTALL_PREFIX="../../ibex" .. ; cmake --build . --config Release --target install ; cd ../.. # shell: bash - run: | mkdir build ; cd build diff --git a/doc/doc/install/01-installation-full-linux.rst b/doc/doc/install/01-installation-full-linux.rst index a164c6fef..88375c110 100644 --- a/doc/doc/install/01-installation-full-linux.rst +++ b/doc/doc/install/01-installation-full-linux.rst @@ -46,7 +46,7 @@ In case you prefer the latest development version, Codac can be installed by com Requirements ^^^^^^^^^^^^ -Codac uses several features of the `IBEX library `_ that you have to install first. The last version of IBEX is maintained on `this unofficial development repository `_: +Codac uses several features of the `IBEX library `_ that you have to install first. The last version of IBEX is maintained on `this unofficial development repository `_: .. code-block:: bash @@ -54,7 +54,7 @@ Codac uses several features of the `IBEX library Date: Sun, 21 May 2023 16:07:23 +0200 Subject: [PATCH 097/256] Documentation updates --- doc/doc/install/01-installation-python.rst | 9 +++++---- doc/doc/install/01-installation.rst | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/doc/doc/install/01-installation-python.rst b/doc/doc/install/01-installation-python.rst index c36bb316e..5313a1790 100644 --- a/doc/doc/install/01-installation-python.rst +++ b/doc/doc/install/01-installation-python.rst @@ -48,7 +48,7 @@ The :gbg:`✓` configurations are officially supported at the moment: +---------------+----------------+-----------------+-----------------+----------------+----------------+ + |Python 3.10 |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` | | +---------------+----------------+-----------------+-----------------+----------------+----------------+ + -|Python 3.11 | |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` | | +|Python 3.11 |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` | | +---------------+----------------+-----------------+-----------------+----------------+----------------+----------------+ If a configuration in this table does not work, please `contact us `_. @@ -56,7 +56,7 @@ If a configuration in this table does not work, please `contact us `_ might be necessary to run `VIBes `_ (not tested). + | `Rosetta `_ might be necessary to run `VIBes `_. .. warning:: @@ -65,9 +65,10 @@ If a configuration in this table does not work, please `contact us Date: Thu, 25 May 2023 10:49:50 +0200 Subject: [PATCH 098/256] Do not try to add degenerated slices when truncating on tdomain, add corresponding test --- src/core/domains/tube/codac_Tube.cpp | 4 ++-- tests/core/tests_slices_structure.cpp | 12 ++++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/core/domains/tube/codac_Tube.cpp b/src/core/domains/tube/codac_Tube.cpp index 725e91be6..0f8f76f5e 100644 --- a/src/core/domains/tube/codac_Tube.cpp +++ b/src/core/domains/tube/codac_Tube.cpp @@ -1251,7 +1251,7 @@ namespace codac assert(tdomain().is_superset(t)); // The first slice is the slice containing t.lb() - while(!m_first_slice->tdomain().contains(t.lb())) + while(!m_first_slice->tdomain().contains(t.lb()) || !valid_tdomain(t & m_first_slice->tdomain())) { Slice *s_next = m_first_slice->next_slice(); delete m_first_slice; @@ -1262,7 +1262,7 @@ namespace codac // After this iteration, the last slice will be the one containing t.ub() Slice *s_last = last_slice(); - while(!s_last->tdomain().contains(t.ub())) + while(!s_last->tdomain().contains(t.ub()) || !valid_tdomain(t & s_last->tdomain())) { Slice *s_prev = s_last->prev_slice(); delete s_last; diff --git a/tests/core/tests_slices_structure.cpp b/tests/core/tests_slices_structure.cpp index cf39e395d..1649458d0 100644 --- a/tests/core/tests_slices_structure.cpp +++ b/tests/core/tests_slices_structure.cpp @@ -402,4 +402,16 @@ TEST_CASE("Tube slices structure") CHECK(tube.nb_slices() == 1); CHECK(tube[0].slice(0)->tdomain() == Interval(8.2,8.3)); } + + SECTION("truncate_tdomain, test 5") + { + TubeVector tube(Interval(0.,10.), 1., 2); + CHECK(tube.nb_slices() == 10); + tube.truncate_tdomain(Interval(3.,6.)); + CHECK(tube.tdomain() == Interval(3.,6.)); + CHECK(tube.nb_slices() == 3); + CHECK(tube[0].slice(0)->tdomain() == Interval(3.,4.)); + CHECK(tube[0].slice(1)->tdomain() == Interval(4.,5.)); + CHECK(tube[0].slice(2)->tdomain() == Interval(5.,6.)); + } } From 2a934d2d1719840422d8f2efc5e40e21ca77eecc Mon Sep 17 00:00:00 2001 From: Joris Date: Thu, 25 May 2023 11:02:13 +0200 Subject: [PATCH 099/256] Just check for degeneration --- src/core/domains/tube/codac_Tube.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/domains/tube/codac_Tube.cpp b/src/core/domains/tube/codac_Tube.cpp index 0f8f76f5e..b897bb7d1 100644 --- a/src/core/domains/tube/codac_Tube.cpp +++ b/src/core/domains/tube/codac_Tube.cpp @@ -1251,7 +1251,7 @@ namespace codac assert(tdomain().is_superset(t)); // The first slice is the slice containing t.lb() - while(!m_first_slice->tdomain().contains(t.lb()) || !valid_tdomain(t & m_first_slice->tdomain())) + while(!m_first_slice->tdomain().contains(t.lb()) || (t & m_first_slice->tdomain()).is_degenerated()) { Slice *s_next = m_first_slice->next_slice(); delete m_first_slice; @@ -1262,7 +1262,7 @@ namespace codac // After this iteration, the last slice will be the one containing t.ub() Slice *s_last = last_slice(); - while(!s_last->tdomain().contains(t.ub()) || !valid_tdomain(t & s_last->tdomain())) + while(!s_last->tdomain().contains(t.ub()) || (t & s_last->tdomain()).is_degenerated()) { Slice *s_prev = s_last->prev_slice(); delete s_last; From 633575fab58c43817531eb7255049e2cbba8de69 Mon Sep 17 00:00:00 2001 From: SimonRohou Date: Thu, 25 May 2023 18:46:32 +0200 Subject: [PATCH 100/256] [actions] python binding and tests --- python/codac/tests/test_actions.py | 38 +++++++++++++++++++ .../src/core/2/actions/codac2_py_Action.cpp | 6 +++ src/core/2/actions/codac2_Action.cpp | 31 ++++++++++++++- src/core/2/actions/codac2_Action.h | 10 ++--- 4 files changed, 77 insertions(+), 8 deletions(-) create mode 100644 python/codac/tests/test_actions.py diff --git a/python/codac/tests/test_actions.py b/python/codac/tests/test_actions.py new file mode 100644 index 000000000..426bc1ac7 --- /dev/null +++ b/python/codac/tests/test_actions.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python + +# Codac tests +# --------------------------------------------------------------------------- +# \date 2023 +# \author Simon Rohou +# \copyright Copyright 2023 Codac Team +# \license This program is distributed under the terms of +# the GNU Lesser General Public License (LGPL). + +import unittest +from codac import * +from codac.codac2 import * +import numpy as np + +class TestActions(unittest.TestCase): + + def tests_action(self): + + x = IntervalVector([[-1,0],[5,6]]) + a = OctaSym([2,-1]) + + print(x) # Result: ([-1, 0] ; [5, 6]) + print(a(x)) # Result: ([5, 6] ; [-0, 1]) + print(a.invert()(a(x))) # Result: ([-1, 0] ; [5, 6]) + + print(a) # Result: (2 -1) + print(a.invert()) # Result: (-2 1) + print(a*a) # Result: (-1 -2) + + self.assertEqual(a(x), IntervalVector([[5,6],[-0,1]])) + self.assertEqual(a.invert()(a(x)), IntervalVector([[-1,0],[5,6]])) + self.assertEqual(a, OctaSym([2,-1])) + self.assertEqual(a.invert(), OctaSym([-2,1])) + self.assertEqual(a*a, OctaSym([-1,-2])) + +if __name__ == '__main__': + unittest.main() \ No newline at end of file diff --git a/python/src/core/2/actions/codac2_py_Action.cpp b/python/src/core/2/actions/codac2_py_Action.cpp index ddc4a8405..f3f68b4ff 100644 --- a/python/src/core/2/actions/codac2_py_Action.cpp +++ b/python/src/core/2/actions/codac2_py_Action.cpp @@ -53,6 +53,8 @@ void export_Action(py::module& m) return OctaSym(s); } })) + + .def(py::self == py::self) .def("__call__", [](OctaSym& s,const codac::IntervalVector& x) { return s(x); }, "todo", @@ -64,6 +66,10 @@ void export_Action(py::module& m) .def("invert", &OctaSym::invert, "todo") + + .def(py::self * py::self) + + .def("__repr__", [](const OctaSym& s) { ostringstream str; str << s; return str.str(); }) ; // Automatic cast from lists to OctaSym diff --git a/src/core/2/actions/codac2_Action.cpp b/src/core/2/actions/codac2_Action.cpp index 90468ab59..fe0eb44e6 100644 --- a/src/core/2/actions/codac2_Action.cpp +++ b/src/core/2/actions/codac2_Action.cpp @@ -16,6 +16,16 @@ using namespace codac; namespace codac2 { + Interval sign(int a) + { + return (a > 0) ? 1 : ((a < 0) ? -1 : 0); + } + + int isign(int a) + { + return (a > 0) ? 1 : ((a < 0) ? -1 : 0); + } + OctaSym::OctaSym(const std::vector& s) : std::vector(s) { for(const auto& i : s) @@ -35,14 +45,31 @@ namespace codac2 OctaSym OctaSym::invert() const { - vector inv(size()); + OctaSym inv(*this); for(size_t i = 0 ; i < size() ; i++) inv[abs((int)(*this)[i])-1] = (abs((int)i)+1)*((*this)[i] >= 0 ? 1. : -1.); - return OctaSym(inv); + return inv; } CtcAction OctaSym::operator()(Ctc& ctc) const { return CtcAction(ctc, *this); } + + OctaSym operator*(const OctaSym& s1, const OctaSym& s2) + { + OctaSym s3(s1); + for(size_t i = 0 ; i < s3.size() ; i++) + s3[i] = isign(s2[i])*s1[abs((int)s2[i])-1]; + return s3; + } + + std::ostream& operator<<(std::ostream& str, const OctaSym& s) + { + str << "("; + for(size_t i = 0 ; i < s.size() ; i++) + str << (i != 0 ? " " : "") << s[i]; + str << ")" << flush; + return str; + } } \ No newline at end of file diff --git a/src/core/2/actions/codac2_Action.h b/src/core/2/actions/codac2_Action.h index 895e4e8b2..163b24806 100644 --- a/src/core/2/actions/codac2_Action.h +++ b/src/core/2/actions/codac2_Action.h @@ -26,12 +26,6 @@ namespace codac2 class Action { - protected: - - static codac::Interval sign(int a) - { - return (a > 0) ? 1 : ((a < 0) ? -1 : 0); - } }; /** @@ -45,7 +39,11 @@ namespace codac2 CtcAction operator()(codac::Ctc& ctc) const; codac::IntervalVector operator()(const codac::IntervalVector& x) const; OctaSym invert() const; + + friend std::ostream& operator<<(std::ostream& str, const OctaSym& s); }; + + OctaSym operator*(const OctaSym& s1, const OctaSym& s2); } #endif \ No newline at end of file From e3549453513cec571a20ac77c89069ce9947a1cc Mon Sep 17 00:00:00 2001 From: SimonRohou Date: Thu, 25 May 2023 18:48:03 +0200 Subject: [PATCH 101/256] [doc] updated gome page --- doc/doc/index.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/doc/index.rst b/doc/doc/index.rst index 4912e1789..6c99cbe43 100644 --- a/doc/doc/index.rst +++ b/doc/doc/index.rst @@ -359,7 +359,7 @@ Contributors * `Peter Franek `_ * `Gilles Trombettoni `_ * Verlein Radwan - * `Mohamed Saad Ibn Seddik `_ + * Joris Tillet Main related publications From 00a2d49f5d62717d5b75ae8cc142c9ef74c9bafb Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Sun, 28 May 2023 18:38:10 +0200 Subject: [PATCH 102/256] Updated .nupkg --- .github/workflows/unixmatrix.yml | 3 ++- packages/choco/codac/codac.nuspec | 2 +- packages/choco/codac/tools/chocolateyinstall.ps1 | 4 ++-- packages/choco/codac/tools/sharedVars.ps1 | 1 + 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/workflows/unixmatrix.yml b/.github/workflows/unixmatrix.yml index 376f17497..86be07424 100644 --- a/.github/workflows/unixmatrix.yml +++ b/.github/workflows/unixmatrix.yml @@ -137,7 +137,8 @@ jobs: cd packages/choco sed_param=s/\1\<\\/version\>/\${PACKAGE_VERSION}\<\\/version\>/ sed -i "$sed_param" codac/codac.nuspec - sed_param=s/codac-1/codac-${PACKAGE_VERSION}/ + sed_param=s/download\\/v1\\/codac/download\\/v${PACKAGE_VERSION}\\/codac/ + sed -i "$sed_param" codac/codac.nuspec sed -i "$sed_param" codac/tools/chocolateyinstall.ps1 mv -f codac codac.$PACKAGE_VERSION cd codac.$PACKAGE_VERSION diff --git a/packages/choco/codac/codac.nuspec b/packages/choco/codac/codac.nuspec index af1939078..b1f861fee 100644 --- a/packages/choco/codac/codac.nuspec +++ b/packages/choco/codac/codac.nuspec @@ -34,7 +34,7 @@ The following package parameters can be set: - `/NoRegistry` - Will not try to update Windows registry. To pass package parameters, use `--params "''"` (e.g. `choco install codac --params "'/NoPath /NoRegistry'"`), and to install another binary package, try e.g. ``` -choco install -y --ignore-dependencies codac --params "'/url:https://github.com/codac-team/codac/releases/download/codac-1.2.2/codac_x86_vc15.zip'" +choco install -y --ignore-dependencies codac --params "'/url:https://github.com/codac-team/codac/releases/download/v1/codac_x64_vc17.zip'" ``` https://github.com/codac-team/codac/releases diff --git a/packages/choco/codac/tools/chocolateyinstall.ps1 b/packages/choco/codac/tools/chocolateyinstall.ps1 index ed7a24c5d..bf3284425 100644 --- a/packages/choco/codac/tools/chocolateyinstall.ps1 +++ b/packages/choco/codac/tools/chocolateyinstall.ps1 @@ -17,9 +17,9 @@ $root = Join-Path $installDir "codac" New-Item -ItemType Directory -Force -Path $root | Out-Null if (!$pp['url']) { - $url = 'https://github.com/lebarsfa/codac/releases/download/codac-1/codac_x86_mingw$MinGWMVer.zip' + $url = 'https://github.com/codac-team/codac/releases/download/v1/codac_x86_mingw11.zip' $checksum = 'EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE' - $url64 = 'https://github.com/lebarsfa/codac/releases/download/codac-1/codac_x64_mingw$MinGWMVer.zip' + $url64 = 'https://github.com/codac-team/codac/releases/download/v1/codac_x64_mingw11.zip' $checksum64 = 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF' $packageArgs = @{ packageName = $env:ChocolateyPackageName diff --git a/packages/choco/codac/tools/sharedVars.ps1 b/packages/choco/codac/tools/sharedVars.ps1 index 77a595455..98e579210 100644 --- a/packages/choco/codac/tools/sharedVars.ps1 +++ b/packages/choco/codac/tools/sharedVars.ps1 @@ -1,3 +1,4 @@ +# Some of these variables might not be used in links to simplify parsing of files... if ((Get-ProcessorBits 32) -or $env:ChocolateyForceX86 -eq $true) { $arch = "x86" } else { $arch = "x64" } $MinGWMVer = "11" $CMakeRegistryPath = "HKCU:\SOFTWARE\Kitware\CMake" From c2e7057fe79c80beb720ab7f7f70297da9888a85 Mon Sep 17 00:00:00 2001 From: SimonRohou Date: Thu, 1 Jun 2023 12:59:07 +0200 Subject: [PATCH 103/256] [py] temporarily removing assertion on action constructor --- python/src/core/2/actions/codac2_py_Action.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/src/core/2/actions/codac2_py_Action.cpp b/python/src/core/2/actions/codac2_py_Action.cpp index f3f68b4ff..73d416882 100644 --- a/python/src/core/2/actions/codac2_py_Action.cpp +++ b/python/src/core/2/actions/codac2_py_Action.cpp @@ -37,8 +37,8 @@ void export_Action(py::module& m) py::buffer_info info = s.request(); // Some sanity checks... - if(info.format != py::format_descriptor::format()) - throw std::runtime_error("Incompatible format: expected a int array"); + // TEMPORARILY REMOVED (not supported on Windows): if(info.format != py::format_descriptor::format()) + // TEMPORARILY REMOVED (not supported on Windows): throw std::runtime_error("Incompatible format: expected a int array"); //if(info.ndim == 1 || // e.g.: a=np.array([1,0,0]) // (info.ndim == 2 && (int)info.shape[1] == 1)) // e.g.: a=np.array([[1],[0],[0]]) From eeb6d3d69770ed0168b159601aef05059bd12721 Mon Sep 17 00:00:00 2001 From: SimonRohou Date: Wed, 21 Jun 2023 16:05:05 +0200 Subject: [PATCH 104/256] [py] binding of get_connected_subsets and related classes --- python/CMakeLists.txt | 2 + python/codac_py_core.cpp | 7 ++- .../core/graphics/codac_py_VIBesFigPaving.cpp | 3 +- .../core/paving/codac_py_ConnectedSubset.cpp | 36 ++++++++++++++ python/src/core/paving/codac_py_Paving.cpp | 10 +++- .../src/core/paving/codac_py_SIVIAPaving.cpp | 47 +++++++++++++++++++ python/src/core/paving/codac_py_Set.cpp | 3 ++ src/core/paving/codac_Paving.cpp | 3 +- src/core/paving/codac_Paving.h | 3 +- 9 files changed, 107 insertions(+), 7 deletions(-) create mode 100644 python/src/core/paving/codac_py_ConnectedSubset.cpp create mode 100644 python/src/core/paving/codac_py_SIVIAPaving.cpp diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt index 184cbc7a4..680ce863e 100644 --- a/python/CMakeLists.txt +++ b/python/CMakeLists.txt @@ -97,7 +97,9 @@ src/core/geometry/codac_py_geometry.cpp src/core/paving/codac_py_Paving.cpp + src/core/paving/codac_py_SIVIAPaving.cpp src/core/paving/codac_py_Set.cpp + src/core/paving/codac_py_ConnectedSubset.cpp src/core/sivia/codac_py_sivia.cpp diff --git a/python/codac_py_core.cpp b/python/codac_py_core.cpp index 91c270bf5..49b111895 100644 --- a/python/codac_py_core.cpp +++ b/python/codac_py_core.cpp @@ -78,9 +78,10 @@ void export_VIBesFigPaving(py::module& m); void export_geometry(py::module& m, py::class_& ctc, py::class_& sep); - -void export_Paving(py::module& m); void export_Set(py::module& m); +void export_Paving(py::module& m); +void export_SIVIAPaving(py::module& m); +void export_ConnectedSubset(py::module& m); void export_DataLoader(py::module& m); void export_TPlane(py::module& m); @@ -144,7 +145,9 @@ PYBIND11_MODULE(core, m) export_geometry(m, ctc, sep); export_Paving(m); + export_SIVIAPaving(m); export_Set(m); + export_ConnectedSubset(m); export_DataLoader(m); export_TPlane(m); diff --git a/python/src/core/graphics/codac_py_VIBesFigPaving.cpp b/python/src/core/graphics/codac_py_VIBesFigPaving.cpp index ec5d566b2..d0bf7e2cf 100644 --- a/python/src/core/graphics/codac_py_VIBesFigPaving.cpp +++ b/python/src/core/graphics/codac_py_VIBesFigPaving.cpp @@ -32,7 +32,8 @@ void export_VIBesFigPaving(py::module& m) .def(py::init(), VIBESFIGPAVING_CONSTPAVING_M_PAVING, - "fig_name"_a, "paving"_a) + "fig_name"_a, "paving"_a.noconvert(), + py::keep_alive<1,3>()) .def("show", (void (VIBesFigPaving::*)())&VIBesFigPaving::show, VIBESFIGPAVING_VOID_SHOW) diff --git a/python/src/core/paving/codac_py_ConnectedSubset.cpp b/python/src/core/paving/codac_py_ConnectedSubset.cpp new file mode 100644 index 000000000..5890fe326 --- /dev/null +++ b/python/src/core/paving/codac_py_ConnectedSubset.cpp @@ -0,0 +1,36 @@ +/** + * \file + * Paving Python binding + * ---------------------------------------------------------------------------- + * \date 2021 + * \author Simon Rohou + * \copyright Copyright 2021 Codac Team + * \license This program is distributed under the terms of + * the GNU Lesser General Public License (LGPL). + */ + +#include +#include +#include +#include + +#include "codac_ConnectedSubset.h" +// Generated file from Doxygen XML (doxygen2docstring.py): +#include "codac_py_ConnectedSubset_docs.h" + +using namespace std; +using namespace codac; +namespace py = pybind11; +using namespace pybind11::literals; + + +void export_ConnectedSubset(py::module& m) +{ + py::class_ connectedsubset(m, "ConnectedSubset", CONNECTEDSUBSET_MAIN); + connectedsubset + + .def("get_boxes", &ConnectedSubset::get_boxes, + CONNECTEDSUBSET_VECTORINTERVALVECTOR_GET_BOXES) + + ; +} \ No newline at end of file diff --git a/python/src/core/paving/codac_py_Paving.cpp b/python/src/core/paving/codac_py_Paving.cpp index f82bf4c9f..49f73642b 100644 --- a/python/src/core/paving/codac_py_Paving.cpp +++ b/python/src/core/paving/codac_py_Paving.cpp @@ -14,6 +14,7 @@ #include #include +#include "codac_Set.h" #include "codac_Paving.h" // Generated file from Doxygen XML (doxygen2docstring.py): #include "codac_py_Paving_docs.h" @@ -26,5 +27,12 @@ using namespace pybind11::literals; void export_Paving(py::module& m) { - py::class_ tplane(m, "Paving", PAVING_MAIN); + py::class_ paving(m, "Paving", PAVING_MAIN); + paving + + .def("get_connected_subsets", &Paving::get_connected_subsets, + PAVING_VECTORCONNECTEDSUBSET_GET_CONNECTED_SUBSETS_BOOL_SETVALUE, + "sort_by_size"_a=false, "val"_a) + + ; } \ No newline at end of file diff --git a/python/src/core/paving/codac_py_SIVIAPaving.cpp b/python/src/core/paving/codac_py_SIVIAPaving.cpp new file mode 100644 index 000000000..cc1526230 --- /dev/null +++ b/python/src/core/paving/codac_py_SIVIAPaving.cpp @@ -0,0 +1,47 @@ +/** + * \file + * Paving Python binding + * ---------------------------------------------------------------------------- + * \date 2021 + * \author Simon Rohou + * \copyright Copyright 2021 Codac Team + * \license This program is distributed under the terms of + * the GNU Lesser General Public License (LGPL). + */ + +#include +#include +#include +#include + +#include "codac_SIVIAPaving.h" +// Generated file from Doxygen XML (doxygen2docstring.py): +#include "codac_py_SIVIAPaving_docs.h" + +using namespace std; +using namespace codac; +namespace py = pybind11; +using namespace pybind11::literals; + + +void export_SIVIAPaving(py::module& m) +{ + py::class_ paving(m, "SIVIAPaving", SIVIAPAVING_MAIN); + paving + + .def(py::init(), + SIVIAPAVING_SIVIAPAVING_INTERVALVECTOR) + + .def("compute", (void (SIVIAPaving::*)(const Function&,const IntervalVector&,float))&SIVIAPaving::compute, + SIVIAPAVING_VOID_COMPUTE_FUNCTION_INTERVALVECTOR_FLOAT, + "f"_a, "y"_a, "precision"_a) + + .def("compute", (void (SIVIAPaving::*)(Ctc&,float))&SIVIAPaving::compute, + SIVIAPAVING_VOID_COMPUTE_CTC_FLOAT, + "ctc"_a, "precision"_a) + + .def("compute", (void (SIVIAPaving::*)(ibex::Sep&,float))&SIVIAPaving::compute, + SIVIAPAVING_VOID_COMPUTE_SEP_FLOAT, + "sep"_a, "precision"_a) + ; +} \ No newline at end of file diff --git a/python/src/core/paving/codac_py_Set.cpp b/python/src/core/paving/codac_py_Set.cpp index d6f99b472..96e862ef9 100644 --- a/python/src/core/paving/codac_py_Set.cpp +++ b/python/src/core/paving/codac_py_Set.cpp @@ -31,5 +31,8 @@ void export_Set(py::module& m) .value("OUT", SetValue::OUT) .value("IN", SetValue::IN) .value("PENUMBRA", SetValue::PENUMBRA) + + .def("__or__", [](SetValue v1, SetValue v2) { return v1|v2; }) + .def("__and__", [](SetValue v1, SetValue v2) { return v1&v2; }) ; } \ No newline at end of file diff --git a/src/core/paving/codac_Paving.cpp b/src/core/paving/codac_Paving.cpp index 0eb054e5f..307cb1e9c 100644 --- a/src/core/paving/codac_Paving.cpp +++ b/src/core/paving/codac_Paving.cpp @@ -237,12 +237,11 @@ namespace codac // todo: return x1->get_items().size() > x2->get_items().size(); // todo: } - vector Paving::get_connected_subsets(bool sort_by_size) const + vector Paving::get_connected_subsets(bool sort_by_size, SetValue val) const { reset_flags(); const Paving *p; - SetValue val = SetValue::UNKNOWN | SetValue::IN; vector v_connected_subsets; while((p = get_first_leaf(val, true))) diff --git a/src/core/paving/codac_Paving.h b/src/core/paving/codac_Paving.h index 5e633f105..42a049ae0 100644 --- a/src/core/paving/codac_Paving.h +++ b/src/core/paving/codac_Paving.h @@ -206,9 +206,10 @@ namespace codac * * \param sort_by_size (optional) if `true` then the subsets will be * sort by the number of boxes they are made of + * \param val the value of the leaves defining the connected items * \return the set of connected subsets */ - std::vector get_connected_subsets(bool sort_by_size = false) const; + std::vector get_connected_subsets(bool sort_by_size = false, SetValue val = SetValue::UNKNOWN | SetValue::IN) const; /// @} From 2912ab5471e9aab954c97c4a96bc5618ef7ddfba Mon Sep 17 00:00:00 2001 From: SimonRohou Date: Wed, 21 Jun 2023 17:27:01 +0200 Subject: [PATCH 105/256] [paving] added ConnectecSubset::contains + binding --- python/src/core/paving/codac_py_ConnectedSubset.cpp | 9 +++++++++ src/core/paving/codac_ConnectedSubset.cpp | 8 ++++++++ src/core/paving/codac_ConnectedSubset.h | 9 +++++++++ 3 files changed, 26 insertions(+) diff --git a/python/src/core/paving/codac_py_ConnectedSubset.cpp b/python/src/core/paving/codac_py_ConnectedSubset.cpp index 5890fe326..a1b0d233d 100644 --- a/python/src/core/paving/codac_py_ConnectedSubset.cpp +++ b/python/src/core/paving/codac_py_ConnectedSubset.cpp @@ -13,7 +13,9 @@ #include #include #include +#include "codac_type_caster.h" +#include "codac_Vector.h" #include "codac_ConnectedSubset.h" // Generated file from Doxygen XML (doxygen2docstring.py): #include "codac_py_ConnectedSubset_docs.h" @@ -32,5 +34,12 @@ void export_ConnectedSubset(py::module& m) .def("get_boxes", &ConnectedSubset::get_boxes, CONNECTEDSUBSET_VECTORINTERVALVECTOR_GET_BOXES) + .def("is_strictly_included_in_paving", &ConnectedSubset::is_strictly_included_in_paving, + CONNECTEDSUBSET_BOOL_IS_STRICTLY_INCLUDED_IN_PAVING) + + .def("contains", &ConnectedSubset::contains, + CONNECTEDSUBSET_BOOL_CONTAINS_VECTOR, + "p"_a.noconvert()) + ; } \ No newline at end of file diff --git a/src/core/paving/codac_ConnectedSubset.cpp b/src/core/paving/codac_ConnectedSubset.cpp index 81799fa53..f1029dd36 100644 --- a/src/core/paving/codac_ConnectedSubset.cpp +++ b/src/core/paving/codac_ConnectedSubset.cpp @@ -36,6 +36,14 @@ namespace codac return box().is_strict_interior_subset(get_paving()->box()); } + bool ConnectedSubset::contains(const Vector& p) const + { + for(const auto& subset_item : m_v_subset_items) + if(subset_item->box().contains(p)) + return true; + return false; + } + const Paving* ConnectedSubset::get_paving() const { assert(!m_v_subset_items.empty() && "no items in ConnectedSubset (empty items vector)"); diff --git a/src/core/paving/codac_ConnectedSubset.h b/src/core/paving/codac_ConnectedSubset.h index c4591b611..6eb6956a9 100644 --- a/src/core/paving/codac_ConnectedSubset.h +++ b/src/core/paving/codac_ConnectedSubset.h @@ -14,6 +14,7 @@ #include #include "codac_IntervalMatrix.h" +#include "codac_Vector.h" #include "codac_Set.h" namespace codac @@ -56,6 +57,14 @@ namespace codac */ bool is_strictly_included_in_paving() const; + /** + * \brief Tests if p is contained in the connected subset + * + * \param p vector to be tested + * \return `true` if p is inside this + */ + bool contains(const Vector& p) const; + /** * \brief Returns a const pointer to the paving structure * From a645ab5d80ba890cda0d9daada78fe10e82e2034 Mon Sep 17 00:00:00 2001 From: SimonRohou Date: Wed, 28 Jun 2023 14:33:58 +0200 Subject: [PATCH 106/256] [paving] corrected bug in SIVIA paving --- src/core/paving/codac_SIVIAPaving.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/paving/codac_SIVIAPaving.cpp b/src/core/paving/codac_SIVIAPaving.cpp index c21af1fa1..bbbe99dd6 100644 --- a/src/core/paving/codac_SIVIAPaving.cpp +++ b/src/core/paving/codac_SIVIAPaving.cpp @@ -47,7 +47,7 @@ namespace codac } } - void SIVIAPaving::compute(Ctc &ctc, float precision) + void SIVIAPaving::compute(Ctc& ctc, float precision) { assert(precision > 0.); assert(ctc.nb_var == box().size()); @@ -58,7 +58,7 @@ namespace codac if(result.is_empty()) set_value(SetValue::OUT); - else if(result.max_diam() < precision) + else if(box().max_diam() < precision) set_value(SetValue::UNKNOWN); else From cb5cff79179d9d2e4f9ed17fe9e5cc58e083f9c7 Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Wed, 28 Jun 2023 19:15:36 +0200 Subject: [PATCH 107/256] Force doxygen v1.9.6 on macOS since latest causes issues --- .github/workflows/macosmatrix.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/macosmatrix.yml b/.github/workflows/macosmatrix.yml index fa842a1ed..ff1d92f38 100644 --- a/.github/workflows/macosmatrix.yml +++ b/.github/workflows/macosmatrix.yml @@ -51,7 +51,8 @@ jobs: shell: bash - run: brew install eigen if: runner.os=='macOS' - - run: brew install doxygen ; brew install graphviz ; python -m pip install --upgrade pip ; pip install --upgrade wheel setuptools sphinx breathe sphinx_rtd_theme sphinx-tabs sphinx-issues sphinx-reredirects + # doxygen v1.9.7 causes issues... + - run: wget https://github.com/Homebrew/homebrew-core/raw/d2267b9f2ad247bc9c8273eb755b39566a474a70/Formula/doxygen.rb ; brew reinstall ./doxygen.rb ; brew pin doxygen ; brew install graphviz ; python -m pip install --upgrade pip ; pip install --upgrade wheel setuptools sphinx breathe sphinx_rtd_theme sphinx-tabs sphinx-issues sphinx-reredirects if: runner.os=='macOS' - run: | wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20230510/ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip --no-check-certificate -nv From a50752c3fd0dfc916d2dd354e32f245b6e4656e8 Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Wed, 28 Jun 2023 21:43:59 +0200 Subject: [PATCH 108/256] Force numpy below v1.25.0 on macOS since latest causes issues --- .github/workflows/macosmatrix.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/macosmatrix.yml b/.github/workflows/macosmatrix.yml index ff1d92f38..272b2999c 100644 --- a/.github/workflows/macosmatrix.yml +++ b/.github/workflows/macosmatrix.yml @@ -67,7 +67,8 @@ jobs: cmake --build . --config Release --target pip_package ; cp `ls *.whl` ../`ls *.whl | sed "s/py3-none-any/cp${{ matrix.cfg.py_v_maj }}${{ matrix.cfg.py_v_min }}-cp${{ matrix.cfg.py_v_maj }}${{ matrix.cfg.py_v_min }}${{ matrix.cfg.cpcfg }}/"` cd .. shell: bash - - run: pip install *.whl ; python -c "import sys; print(sys.version)" ; python examples/tuto/01_getting_started/01_getting_started.py ; pip install numpy ; python -m unittest discover codac.tests + # numpy v1.25.0 causes issues... + - run: pip install *.whl ; python -c "import sys; print(sys.version)" ; python examples/tuto/01_getting_started/01_getting_started.py ; pip install "numpy<1.25" ; python -m unittest discover codac.tests shell: bash if: (runner.os=='macOS')&&(matrix.cfg.arch=='x86_64') - uses: xresloader/upload-to-github-release@v1 From c805a9e65679011cbaef83d2e5f3b0c27693a6ef Mon Sep 17 00:00:00 2001 From: SimonRohou Date: Tue, 11 Jul 2023 15:44:35 +0200 Subject: [PATCH 109/256] [doc] added slam page --- doc/doc/conf.py.in | 2 +- doc/doc/use-cases/slam/index.rst | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 doc/doc/use-cases/slam/index.rst diff --git a/doc/doc/conf.py.in b/doc/doc/conf.py.in index 450062d89..a1f0ed504 100644 --- a/doc/doc/conf.py.in +++ b/doc/doc/conf.py.in @@ -45,7 +45,7 @@ extensions = [ redirects = { "lohner": "manual/05-dynamic-contractors/03-ctc-lohner.html", - "slam": "tutorial/08-rangeonly-slam/index.html", + "slam": "use-cases/slam/index.html", "lie-symmetries": "use-cases/lie-symmetries/index.html", "sivia": "manual/11-separators/index.html", "installation": "install/01-installation.html", diff --git a/doc/doc/use-cases/slam/index.rst b/doc/doc/use-cases/slam/index.rst new file mode 100644 index 000000000..d193e0a88 --- /dev/null +++ b/doc/doc/use-cases/slam/index.rst @@ -0,0 +1,8 @@ +.. _sec-usecases-slam: + +##################################### +Simultaneous Localization And Mapping +##################################### + +* :ref:`Link to the tutorial page (Lesson H) about How to build a SLAM solver with Codac`. +* `Link to the source code of the solution of Lesson H `_. \ No newline at end of file From 488bf29a30ca91209e884dcd17a98ecccae0db0b Mon Sep 17 00:00:00 2001 From: SimonRohou Date: Mon, 17 Jul 2023 17:34:24 +0200 Subject: [PATCH 110/256] [eigen] corrected bug in EigenHelpers --- src/core/tools/codac_Eigen.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/tools/codac_Eigen.cpp b/src/core/tools/codac_Eigen.cpp index 54dc187be..f746c5f77 100644 --- a/src/core/tools/codac_Eigen.cpp +++ b/src/core/tools/codac_Eigen.cpp @@ -15,7 +15,7 @@ Eigen::MatrixXd EigenHelpers::i2e(const Matrix &x) { Eigen::MatrixXd EigenHelpers::i2e(const Vector &x) { Eigen::MatrixXd m(x.size(), 1); for (int i = 0; i < x.size(); ++i) { - m(i, 1) = x[i]; + m(i, 0) = x[i]; } return m; } From a8356a7d9415e7f7977e3cafde9ca68564cefd1b Mon Sep 17 00:00:00 2001 From: SimonRohou Date: Wed, 23 Aug 2023 15:30:26 +0200 Subject: [PATCH 111/256] [core] using Eigen for [Interval]Matrix/Vector --- src/core/2/domains/interval/codac2_Interval.h | 54 ++++ .../domains/interval/codac2_IntervalMatrix.h | 32 +++ .../domains/interval/codac2_IntervalVector.h | 267 ++++++++++++++++++ src/core/2/domains/paving/codac2_Paving.h | 124 ++++++++ src/core/2/domains/tube/codac2_Tube.h | 4 +- src/core/2/variables/codac2_Matrix.h | 53 ++++ src/core/2/variables/codac2_Vector.h | 88 ++++++ src/core/CMakeLists.txt | 11 +- src/core/paving/codac_Paving.h | 2 +- src/core/sivia/codac_sivia.cpp | 45 ++- src/core/sivia/codac_sivia.h | 22 ++ 11 files changed, 688 insertions(+), 14 deletions(-) create mode 100644 src/core/2/domains/interval/codac2_Interval.h create mode 100644 src/core/2/domains/interval/codac2_IntervalMatrix.h create mode 100644 src/core/2/domains/interval/codac2_IntervalVector.h create mode 100644 src/core/2/domains/paving/codac2_Paving.h create mode 100644 src/core/2/variables/codac2_Matrix.h create mode 100644 src/core/2/variables/codac2_Vector.h diff --git a/src/core/2/domains/interval/codac2_Interval.h b/src/core/2/domains/interval/codac2_Interval.h new file mode 100644 index 000000000..35df833d3 --- /dev/null +++ b/src/core/2/domains/interval/codac2_Interval.h @@ -0,0 +1,54 @@ +/** + * \file + * + * ---------------------------------------------------------------------------- + * \date 2023 + * \author Simon Rohou + * \copyright Copyright 2023 Codac Team + * \license This program is distributed under the terms of + * the GNU Lesser General Public License (LGPL). + */ + +#ifndef __CODAC2_INTERVAL_H__ +#define __CODAC2_INTERVAL_H__ + +#include + +namespace codac2 +{ + using codac::Interval; + +} // namespace codac + +namespace Eigen +{ + template<> struct NumTraits + : NumTraits // permits to get the epsilon, dummy_precision, lowest, highest functions + { + typedef codac2::Interval Real; + typedef codac2::Interval NonInteger; + typedef codac2::Interval Nested; + + enum { + IsComplex = 0, + IsInteger = 0, + IsSigned = 1, + RequireInitialization = 1, + ReadCost = 1, + AddCost = 3, + MulCost = 3 + }; + }; +} + +namespace codac2 +{ + inline const Interval& conj(const Interval& x) { return x; } + inline const Interval& real(const Interval& x) { return x; } + inline Interval imag(const Interval&) { return 0.; } + inline Interval abs(const Interval& x) { return ibex::abs(x); } + inline Interval abs2(const Interval& x) { return x*x; } + +} // namespace codac + +#endif \ No newline at end of file diff --git a/src/core/2/domains/interval/codac2_IntervalMatrix.h b/src/core/2/domains/interval/codac2_IntervalMatrix.h new file mode 100644 index 000000000..11ccdeb45 --- /dev/null +++ b/src/core/2/domains/interval/codac2_IntervalMatrix.h @@ -0,0 +1,32 @@ +/** + * \file + * + * ---------------------------------------------------------------------------- + * \date 2023 + * \author Simon Rohou + * \copyright Copyright 2023 Codac Team + * \license This program is distributed under the terms of + * the GNU Lesser General Public License (LGPL). + */ + +#ifndef __CODAC2_INTERVALMATRIX_H__ +#define __CODAC2_INTERVALMATRIX_H__ + +#include +#include +#include +#include +#include + +namespace codac2 +{ + template + class IntervalMatrix : public Eigen::Matrix + { + public: + + }; + +} // namespace codac + +#endif \ No newline at end of file diff --git a/src/core/2/domains/interval/codac2_IntervalVector.h b/src/core/2/domains/interval/codac2_IntervalVector.h new file mode 100644 index 000000000..970251fa8 --- /dev/null +++ b/src/core/2/domains/interval/codac2_IntervalVector.h @@ -0,0 +1,267 @@ +/** + * \file + * + * ---------------------------------------------------------------------------- + * \date 2023 + * \author Simon Rohou + * \copyright Copyright 2023 Codac Team + * \license This program is distributed under the terms of + * the GNU Lesser General Public License (LGPL). + */ + +#ifndef __CODAC2_INTERVALVECTOR_H__ +#define __CODAC2_INTERVALVECTOR_H__ + +#include +#include +#include +#include +#include +#include "codac2_Interval.h" +#include "codac2_IntervalMatrix.h" +#include "codac2_Vector.h" + +namespace codac2 +{ + template + class IntervalVector : public Eigen::Matrix + { + public: + + IntervalVector() + : Eigen::Matrix() + { + + } + + explicit IntervalVector(const Vector& v) + : Eigen::Matrix() + { + for(size_t i = 0 ; i < N ; i++) + (*this)[i] = v[i]; + } + + explicit IntervalVector(const Interval& xi) + : Eigen::Matrix() + { + (*this)(0,0) = xi; + } + + IntervalVector(std::initializer_list l) + : Eigen::Matrix() + { + assert(l.size() == N); + size_t i = 0; + for(const auto& li : l) + (*this)[i++] = li; + } + + template + IntervalVector(const Eigen::MatrixBase& other) + : Eigen::Matrix(other) + { } + + // This method allows you to assign Eigen expressions to IntervalVector + template + IntervalVector& operator=(const Eigen::MatrixBase& other) + { + this->Eigen::Matrix::operator=(other); + return *this; + } + + constexpr size_t size() const + { + return N; + } + + bool is_empty() const + { + for(size_t i = 0 ; i < N ; i++) + if((*this)(i,0).is_empty()) + return true; + return false; + } + + std::pair,IntervalVector> bisect(float ratio) const + { + assert(Interval(0,1).interior_contains(ratio)); + size_t i = largest_dim(); + assert((*this)[i].is_bisectable()); + + auto p = std::make_pair(*this,*this); + auto pi = (*this)[i].bisect(ratio); + p.first[i] = pi.first; + p.second[i] = pi.second; + return p; + } + + size_t largest_dim() const + { + return to_codac1(*this).extr_diam_index(false); + } + + double volume() const + { + if(is_empty()) + return 0.; + double v = 0.; + for(size_t i = 0 ; i < N ; i++) + { + if((*this)[i].is_unbounded()) return POS_INFINITY; + if((*this)[i].is_degenerated()) return 0.; + v += log((*this)[i].diam()); + } + return exp(v); + } + + Vector lb() const + { + assert(!is_empty()); // todo: use nan instead of assert? + Vector lb; + for(size_t i = 0 ; i < N ; i++) + lb[i] = (*this)[i].lb(); + return lb; + } + + Vector ub() const + { + assert(!is_empty()); // todo: use nan instead of assert? + Vector ub; + for(size_t i = 0 ; i < N ; i++) + ub[i] = (*this)[i].ub(); + return ub; + } + + Vector mid() const + { + assert(!is_empty()); // todo: use nan instead of assert? + Vector m; + for(size_t i = 0 ; i < N ; i++) + m[i] = (*this)[i].mid(); + return m; + } + + void set_empty() + { + for(size_t i = 0 ; i < N ; i++) + (*this)[i].set_empty(); + } + + static IntervalVector empty_set() + { + IntervalVector x; + x.set_empty(); + return x; + } + + IntervalVector& operator&=(const IntervalVector& x) + { + if(!is_empty()) + { + if(x.is_empty()) + set_empty(); + + else + for(size_t i = 0 ; i < N ; i++) + (*this)[i] &= x[i]; + } + return *this; + } + + IntervalVector& operator|=(const IntervalVector& x) + { + if(!x.is_empty()) + { + if(is_empty()) + *this = x; + + else + for(size_t i = 0 ; i < N ; i++) + (*this)[i] |= x[i]; + } + return *this; + } + + IntervalVector& operator+=(const Vector& x) + { + (*this).noalias() += x.template cast(); + return *this; + } + + IntervalVector& operator-=(const Vector& x) + { + (*this).noalias() -= x.template cast(); + return *this; + } + + template + IntervalVector subvector() const + { + assert(N1 >= 0 && N1 < N && N2 >= 0 && N2 < N && N1 <= N2); + return this->template block(N1,0); + } + + template + void put(const IntervalVector& x) + { + assert(I >= 0 && I < N && M+I <= N); + this->template block(I,0) << x; + } + }; + + template + codac::IntervalVector to_codac1(const IntervalVector& x) + { + ibex::IntervalVector x_(N); + for(size_t i = 0 ; i < N ; i++) + x_[i] = x[i]; + return x_; + } + + template + IntervalVector to_codac2(const codac::IntervalVector& x) + { + assert(x.size() == N); + IntervalVector x_; + for(size_t i = 0 ; i < N ; i++) + x_[i] = x[i]; + return x_; + } + + template + IntervalVector cart_prod(const IntervalVector& x1, const Interval& x2) + { + IntervalVector x; + x << x1,x2; + return x; + } + + template + IntervalVector cart_prod(const Interval& x1, const IntervalVector& x2) + { + IntervalVector x; + x << x1,x2; + return x; + } + + template + IntervalVector cart_prod(const IntervalVector& x1, const IntervalVector& x2) + { + IntervalVector x; + x << x1,x2; + return x; + } + + template + auto cart_prod(const T1& x1, const T2& x2, const Args&... xi) // recursive variadic function + { + auto x_ = cart_prod(x1, x2); + if constexpr(sizeof...(xi) > 0) + return cart_prod(x_, xi...); + else + return x_; + } + +} // namespace codac + +#endif \ No newline at end of file diff --git a/src/core/2/domains/paving/codac2_Paving.h b/src/core/2/domains/paving/codac2_Paving.h new file mode 100644 index 000000000..a53672391 --- /dev/null +++ b/src/core/2/domains/paving/codac2_Paving.h @@ -0,0 +1,124 @@ +/** + * \file + * + * ---------------------------------------------------------------------------- + * \date 2023 + * \author Simon Rohou + * \copyright Copyright 2023 Codac Team + * \license This program is distributed under the terms of + * the GNU Lesser General Public License (LGPL). + */ + +#ifndef __CODAC2_PAVING_H__ +#define __CODAC2_PAVING_H__ + +#include +#include +#include "codac2_Interval.h" +#include "codac2_IntervalVector.h" + +namespace codac2 +{ + template + class Paving : public std::enable_shared_from_this> + { + public: + + Paving() + : _x(IntervalVector()) + { + + } + + Paving(const IntervalVector& x) + : _x(x) + { + + } + + const IntervalVector& box() const + { + return _x; + } + + bool is_leaf() const + { + return not _left && not _right; + } + + double volume() const + { + if(is_leaf()) + return _x.volume(); + double v = 0.; + if(_left) v += _left->volume(); + if(_right) v += _right->volume(); + return v; + } + + void bisect(float ratio = 0.49) + { + assert(Interval(0.,1.).interior_contains(ratio)); + assert(is_leaf() && "only leaves can be bisected"); + auto p = _x.bisect(ratio); + _left = std::make_shared(p.first); + _right = std::make_shared(p.second); + } + + IntervalVector hull_box() const + { + if(is_leaf()) + return _x; + auto hull = IntervalVector::empty_set(); + if(_left) hull |= _left->hull_box(); + if(_right) hull |= _right->hull_box(); + return hull; + } + + std::list>> boxes_list() const + { + std::list>> l; + boxes_list_push(l); + return l; + } + + std::list*> leaves_list() + { + std::list*> l; + leaves_list_push(l); + return l; + } + + protected: + + void boxes_list_push(std::list>>& l) const + { + if(is_leaf() && !_x.is_empty()) + l.push_back(std::cref(_x)); + else + { + if(_left) _left->boxes_list_push(l); + if(_right) _right->boxes_list_push(l); + } + } + + void leaves_list_push(std::list*>& l) + { + if(is_leaf() && !_x.is_empty()) + l.push_back(this); + else + { + if(_left) _left->leaves_list_push(l); + if(_right) _right->leaves_list_push(l); + } + } + + public: // todo + + IntervalVector _x; + std::shared_ptr _left = nullptr, _right = nullptr; + }; + +} // namespace codac + +#endif \ No newline at end of file diff --git a/src/core/2/domains/tube/codac2_Tube.h b/src/core/2/domains/tube/codac2_Tube.h index a37d04423..3090e2aa2 100644 --- a/src/core/2/domains/tube/codac2_Tube.h +++ b/src/core/2/domains/tube/codac2_Tube.h @@ -479,10 +479,10 @@ namespace codac2 }; codac::Tube to_codac1(const codac2::Tube& x); - codac::TubeVector to_codac1(const codac2::Tube& x); + codac::TubeVector to_codac1(const codac2::Tube& x); codac::TubeVector to_codac1_poly(const codac2::Tube& x); codac2::Tube to_codac2(const codac::Tube& x); - codac2::Tube to_codac2(const codac::TubeVector& x); + codac2::Tube to_codac2(const codac::TubeVector& x); codac2::Tube to_codac2_poly(const codac::TubeVector& x); diff --git a/src/core/2/variables/codac2_Matrix.h b/src/core/2/variables/codac2_Matrix.h new file mode 100644 index 000000000..7dce342a9 --- /dev/null +++ b/src/core/2/variables/codac2_Matrix.h @@ -0,0 +1,53 @@ +/** + * \file + * + * ---------------------------------------------------------------------------- + * \date 2023 + * \author Simon Rohou + * \copyright Copyright 2023 Codac Team + * \license This program is distributed under the terms of + * the GNU Lesser General Public License (LGPL). + */ + +#ifndef __CODAC2_MATRIX_H__ +#define __CODAC2_MATRIX_H__ + +#include +#include +#include + +namespace codac2 +{ + template + class Matrix : public Eigen::Matrix + { + public: + + Matrix() + { + + } + + template + Matrix(const Eigen::MatrixBase& other) + : Eigen::Matrix(other) + { } + + // This method allows you to assign Eigen expressions to MyVectorType + template + Matrix& operator=(const Eigen::MatrixBase& other) + { + this->Eigen::Matrix::operator=(other); + return *this; + } + + static Matrix eye() + { + return Eigen::Matrix::Identity(); + } + + }; + +} // namespace codac + +#endif \ No newline at end of file diff --git a/src/core/2/variables/codac2_Vector.h b/src/core/2/variables/codac2_Vector.h new file mode 100644 index 000000000..e944e24b1 --- /dev/null +++ b/src/core/2/variables/codac2_Vector.h @@ -0,0 +1,88 @@ +/** + * \file + * + * ---------------------------------------------------------------------------- + * \date 2023 + * \author Simon Rohou + * \copyright Copyright 2023 Codac Team + * \license This program is distributed under the terms of + * the GNU Lesser General Public License (LGPL). + */ + +#ifndef __CODAC2_VECTOR_H__ +#define __CODAC2_VECTOR_H__ + +#include +#include +#include +#include +#include + +namespace codac2 +{ + template + class Vector : public Eigen::Matrix + { + public: + + Vector() + { + + } + + Vector(double l) : Vector({ l }) + { + + } + + Vector(std::initializer_list l) + { + size_t i = 0; + for(double li : l) + (*this)(i++,0) = li; + } + + template + Vector(const Eigen::MatrixBase& other) + : Eigen::Matrix(other) + { } + + // This method allows you to assign Eigen expressions to MyVectorType + template + Vector& operator=(const Eigen::MatrixBase& other) + { + this->Eigen::Matrix::operator=(other); + return *this; + } + + Matrix as_diag() const + { + return Matrix(Eigen::Matrix(this->asDiagonal())); + } + + static Vector zeros() + { + Vector v; + return v; + } + }; + + template + Matrix diag(const Vector v) + { + return v.as_diag(); + } + + template + Vector to_codac2(const codac::Vector& x) + { + assert(x.size() == N); + Vector x_; + for(size_t i = 0 ; i < N ; i++) + x_[i] = x[i]; + return x_; + } + +} // namespace codac + +#endif \ No newline at end of file diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 20183ea76..ce02751c0 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -179,6 +179,9 @@ ${CMAKE_CURRENT_SOURCE_DIR}/sivia/codac_sivia.h # Files related to codac2 + ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/interval/codac2_Interval.h + ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/interval/codac2_IntervalMatrix.h + ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/interval/codac2_IntervalVector.h ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/tube/codac2_AbstractConstTube.h ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/tube/codac2_AbstractSlice.cpp ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/tube/codac2_AbstractSlice.h @@ -193,12 +196,15 @@ ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/tube/codac2_Tube.h ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/tube/codac2_TubeComponent.h ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/tube/codac2_TubeEvaluation.h + ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/paving/codac2_Paving.h ${CMAKE_CURRENT_SOURCE_DIR}/2/contractors/codac2_CtcDiffInclusion.cpp ${CMAKE_CURRENT_SOURCE_DIR}/2/contractors/codac2_CtcDiffInclusion.h ${CMAKE_CURRENT_SOURCE_DIR}/2/contractors/codac2_CtcLinobs.cpp ${CMAKE_CURRENT_SOURCE_DIR}/2/contractors/codac2_CtcLinobs.h ${CMAKE_CURRENT_SOURCE_DIR}/2/integration/codac2_AbstractDomain.cpp ${CMAKE_CURRENT_SOURCE_DIR}/2/integration/codac2_AbstractDomain.h + ${CMAKE_CURRENT_SOURCE_DIR}/2/variables/codac2_Matrix.h + ${CMAKE_CURRENT_SOURCE_DIR}/2/variables/codac2_Vector.h # Actions ${CMAKE_CURRENT_SOURCE_DIR}/2/actions/codac2_Action.cpp ${CMAKE_CURRENT_SOURCE_DIR}/2/actions/codac2_Action.h @@ -233,9 +239,12 @@ ${CMAKE_CURRENT_SOURCE_DIR}/tools ${CMAKE_CURRENT_SOURCE_DIR}/sivia ${CMAKE_CURRENT_SOURCE_DIR}/2/contractors + ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/interval ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/tube + ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/paving ${CMAKE_CURRENT_SOURCE_DIR}/2/integration/ - ${CMAKE_CURRENT_SOURCE_DIR}/2/actions/) + ${CMAKE_CURRENT_SOURCE_DIR}/2/actions/ + ${CMAKE_CURRENT_SOURCE_DIR}/2/variables/) target_link_libraries(codac PUBLIC Ibex::ibex) diff --git a/src/core/paving/codac_Paving.h b/src/core/paving/codac_Paving.h index 42a049ae0..674b18c53 100644 --- a/src/core/paving/codac_Paving.h +++ b/src/core/paving/codac_Paving.h @@ -213,7 +213,7 @@ namespace codac /// @} - protected: + public: mutable bool m_flag = false; //!< optional flag, can be used by search algorithms Paving *m_root = nullptr; //!< pointer to the root diff --git a/src/core/sivia/codac_sivia.cpp b/src/core/sivia/codac_sivia.cpp index f486d8ead..ca79ffdd8 100644 --- a/src/core/sivia/codac_sivia.cpp +++ b/src/core/sivia/codac_sivia.cpp @@ -32,8 +32,8 @@ namespace codac return v; } - map> SIVIA( - const IntervalVector& x0, Ctc& ctc, float precision, bool regular_paving, + map> _SIVIA( + const IntervalVector& x0, const IntervalVector* y0, Ctc& ctc, float precision, bool regular_paving, bool display_result, const string& fig_name, bool return_result, const SetColorMap& color_map) { assert(x0.size() >= 2); @@ -64,11 +64,11 @@ namespace codac if(!fig_name.empty()) vibes::newFigure(fig_name); - vibes::drawBox(x0); + vibes::drawBox(x0.subvector(0,1)); vibes::newGroup("boxes_out", cm.at(SetValue::OUT)); vibes::newGroup("boxes_unknown", cm.at(SetValue::UNKNOWN)); - vibes::drawBox(x0, vibesParams("figure", fig_name)); + vibes::drawBox(x0.subvector(0,1), vibesParams("figure", fig_name)); vibes::axisAuto(); } @@ -80,7 +80,7 @@ namespace codac }; ibex::LargestFirst bisector(0.); - deque stack = { x0 }; + deque stack = { y0 ? cart_prod(x0,*y0) : x0 }; int k = 0; clock_t t_start = clock(); @@ -128,7 +128,8 @@ namespace codac { IntervalVector& x_remaining = regular_paving ? x_before_ctc : x; - if(x_remaining.max_diam() < precision) + if((!y0 && x_remaining.max_diam() < precision) || + ( y0 && x_remaining.subvector(0,x0.size()-1).max_diam() < precision)) { if(display_result) { @@ -142,9 +143,19 @@ namespace codac else { - pair p = bisector.bisect(x_remaining); - stack.push_back(p.first); - stack.push_back(p.second); + if(y0) + { + pair p = bisector.bisect(x_remaining.subvector(0,x0.size()-1)); + stack.push_back(cart_prod(p.first,*y0)); + stack.push_back(cart_prod(p.second,*y0)); + } + + else + { + pair p = bisector.bisect(x_remaining); + stack.push_back(p.first); + stack.push_back(p.second); + } } } } @@ -160,6 +171,20 @@ namespace codac return boxes; } + map> SIVIA( + const IntervalVector& x0, const IntervalVector& y0, Ctc& ctc, float precision, bool regular_paving, + bool display_result, const string& fig_name, bool return_result, const SetColorMap& color_map) + { + return _SIVIA(x0, &y0, ctc, precision, regular_paving, display_result, fig_name, return_result, color_map); + } + + map> SIVIA( + const IntervalVector& x0, Ctc& ctc, float precision, bool regular_paving, + bool display_result, const string& fig_name, bool return_result, const SetColorMap& color_map) + { + return _SIVIA(x0, nullptr, ctc, precision, regular_paving, display_result, fig_name, return_result, color_map); + } + map> SIVIA( const IntervalVector& x0, ibex::Sep& sep, float precision, bool regular_paving, bool display_result, const string& fig_name, bool return_result, const SetColorMap& color_map) @@ -195,7 +220,7 @@ namespace codac if(!fig_name.empty()) vibes::newFigure(fig_name); - vibes::drawBox(x0); + vibes::drawBox(x0.subvector(0,1)); vibes::newGroup("boxes_out", cm.at(SetValue::OUT)); vibes::newGroup("boxes_unknown", cm.at(SetValue::UNKNOWN)); vibes::newGroup("boxes_in", cm.at(SetValue::IN)); diff --git a/src/core/sivia/codac_sivia.h b/src/core/sivia/codac_sivia.h index 7e595864f..260a6dd42 100644 --- a/src/core/sivia/codac_sivia.h +++ b/src/core/sivia/codac_sivia.h @@ -24,6 +24,28 @@ namespace codac /// \name SIVIA for contractors /// @{ + /** + * \brief Executes a SIVIA algorithm from a contractor, and displays the result. + * SIVIA: Set Inversion Via Interval Analysis. + * + * The result is displayed in the current VIBes figure. + * + * \param x initial box (part that will be bisected) + * \param y initial box (part that will not be bisected) + * \param ctc Contractor operator for the set inversion + * \param precision accuracy of the paving algorithm + * \param regular_paving regular bisection rule + * \param display_result display information if true + * \param fig_name name of the figure on which boxes are drawn. If empty, default figure is used + * \param return_result if true, boxes will be stored in the returned map + * \param color_map color map used to draw boxes, see SetColorMap + * \return return a map of lists of boxes. Keys of the map are IN/OUT/UNKNOWN. The lists are empty if return_result if false. + */ + std::map> SIVIA(const IntervalVector& x, const IntervalVector& y, Ctc& ctc, float precision, + bool regular_paving = false, bool display_result = true, + const std::string& fig_name = "", bool return_result = false, + const SetColorMap& color_map = DEFAULT_SET_COLOR_MAP); + /** * \brief Executes a SIVIA algorithm from a contractor, and displays the result. * SIVIA: Set Inversion Via Interval Analysis. From 7a64cbfb0beb56bbabebe856e24fb9f782757ca7 Mon Sep 17 00:00:00 2001 From: SimonRohou Date: Mon, 28 Aug 2023 14:42:04 +0200 Subject: [PATCH 112/256] [core] various updates related to Codac2 --- .../2/contractors/codac2_CtcDiffInclusion.h | 4 +- src/core/2/domains/interval/codac2_Interval.h | 2 + .../domains/interval/codac2_IntervalVector.h | 10 ++++- src/core/2/domains/paving/codac2_Paving.h | 8 ++-- .../2/integration/codac2_AbstractDomain.h | 2 +- src/core/CMakeLists.txt | 43 +++++++++++++++---- src/robotics/graphics/codac_VIBesFigMap.cpp | 4 +- 7 files changed, 55 insertions(+), 18 deletions(-) diff --git a/src/core/2/contractors/codac2_CtcDiffInclusion.h b/src/core/2/contractors/codac2_CtcDiffInclusion.h index 8ded524e5..ca1da53e6 100644 --- a/src/core/2/contractors/codac2_CtcDiffInclusion.h +++ b/src/core/2/contractors/codac2_CtcDiffInclusion.h @@ -31,8 +31,8 @@ namespace codac2 public: CtcDiffInclusion(const TFunction& t); - void contract(codac2::Tube& x, const codac2::Tube& u, TimePropag t_propa = TimePropag::FORWARD | TimePropag::BACKWARD); - void contract(Slice& x, const Slice& u, TimePropag t_propa = TimePropag::FORWARD | TimePropag::BACKWARD); + void contract(codac2::Tube& x, const codac2::Tube& u, TimePropag t_propa = TimePropag::FORWARD | TimePropag::BACKWARD); + void contract(Slice& x, const Slice& u, TimePropag t_propa = TimePropag::FORWARD | TimePropag::BACKWARD); const TFunction& f() const; protected: diff --git a/src/core/2/domains/interval/codac2_Interval.h b/src/core/2/domains/interval/codac2_Interval.h index 35df833d3..c87c3e56c 100644 --- a/src/core/2/domains/interval/codac2_Interval.h +++ b/src/core/2/domains/interval/codac2_Interval.h @@ -13,6 +13,8 @@ #define __CODAC2_INTERVAL_H__ #include +#include +#include namespace codac2 { diff --git a/src/core/2/domains/interval/codac2_IntervalVector.h b/src/core/2/domains/interval/codac2_IntervalVector.h index 970251fa8..c547d8d64 100644 --- a/src/core/2/domains/interval/codac2_IntervalVector.h +++ b/src/core/2/domains/interval/codac2_IntervalVector.h @@ -77,11 +77,19 @@ namespace codac2 bool is_empty() const { for(size_t i = 0 ; i < N ; i++) - if((*this)(i,0).is_empty()) + if((*this)[i].is_empty()) return true; return false; } + bool intersects(const IntervalVector& x) const + { + for(size_t i = 0 ; i < N ; i++) + if(!(*this)[i].intersects(x[i])) + return false; + return true; + } + std::pair,IntervalVector> bisect(float ratio) const { assert(Interval(0,1).interior_contains(ratio)); diff --git a/src/core/2/domains/paving/codac2_Paving.h b/src/core/2/domains/paving/codac2_Paving.h index a53672391..d7b11218d 100644 --- a/src/core/2/domains/paving/codac2_Paving.h +++ b/src/core/2/domains/paving/codac2_Paving.h @@ -75,10 +75,10 @@ namespace codac2 return hull; } - std::list>> boxes_list() const + std::list>> boxes_list(const IntervalVector& intersect = IntervalVector<2>()) const { std::list>> l; - boxes_list_push(l); + boxes_list_push(l, intersect); return l; } @@ -91,9 +91,9 @@ namespace codac2 protected: - void boxes_list_push(std::list>>& l) const + void boxes_list_push(std::list>>& l, const IntervalVector& intersect = IntervalVector<2>()) const { - if(is_leaf() && !_x.is_empty()) + if(is_leaf() && !_x.is_empty() && _x.intersects(intersect)) l.push_back(std::cref(_x)); else { diff --git a/src/core/2/integration/codac2_AbstractDomain.h b/src/core/2/integration/codac2_AbstractDomain.h index 60d3dca54..f7b837f7b 100644 --- a/src/core/2/integration/codac2_AbstractDomain.h +++ b/src/core/2/integration/codac2_AbstractDomain.h @@ -27,7 +27,7 @@ namespace codac2 public: AbstractDomain(); - IntervalVector box() const; + codac::IntervalVector box() const; protected: diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index ce02751c0..fdf90f992 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -2,7 +2,8 @@ # Codac - cmake configuration file # ================================================================== - list(APPEND SRC ${CMAKE_CURRENT_SOURCE_DIR}/functions/codac_Function.h + list(APPEND CODAC1_SRC + ${CMAKE_CURRENT_SOURCE_DIR}/functions/codac_Function.h ${CMAKE_CURRENT_SOURCE_DIR}/functions/codac_TFnc.h ${CMAKE_CURRENT_SOURCE_DIR}/functions/codac_TFnc.cpp ${CMAKE_CURRENT_SOURCE_DIR}/functions/codac_TFunction.h @@ -176,9 +177,9 @@ ${CMAKE_CURRENT_SOURCE_DIR}/tools/codac_Eigen.cpp ${CMAKE_CURRENT_SOURCE_DIR}/tools/codac_Eigen.h ${CMAKE_CURRENT_SOURCE_DIR}/sivia/codac_sivia.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/sivia/codac_sivia.h + ${CMAKE_CURRENT_SOURCE_DIR}/sivia/codac_sivia.h) - # Files related to codac2 + list(APPEND CODAC2_SRC # Files related to codac2 ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/interval/codac2_Interval.h ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/interval/codac2_IntervalMatrix.h ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/interval/codac2_IntervalVector.h @@ -212,6 +213,8 @@ ${CMAKE_CURRENT_SOURCE_DIR}/2/contractors/codac2_CtcAction.h ) + set(SRC ${CODAC1_SRC} ${CODAC2_SRC}) + ################################################################################ # Create the target for libcodac @@ -254,9 +257,9 @@ # Getting header files from sources - foreach(srcfile ${SRC}) + foreach(srcfile ${CODAC1_SRC}) if(srcfile MATCHES "\\.h$" OR srcfile MATCHES "\\.hpp$") - list(APPEND HDR ${srcfile}) + list(APPEND CODAC_HDR ${srcfile}) # Copying header files for other Codac libraries compiled in the same time file(COPY ${srcfile} DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/../../include) endif() @@ -268,15 +271,39 @@ file(WRITE ${CODAC_MAIN_HEADER} "/* This file is generated by CMake */\n\n") file(APPEND ${CODAC_MAIN_HEADER} "#ifndef __CODAC_H__\n#define __CODAC_H__\n\n") file(APPEND ${CODAC_MAIN_HEADER} "\n#include \n") # todo: clean this organization - foreach(header_path ${HDR}) + foreach(header_path ${CODAC_HDR}) get_filename_component(header_name ${header_path} NAME) file(APPEND ${CODAC_MAIN_HEADER} "#include <${header_name}>\n") endforeach() file(APPEND ${CODAC_MAIN_HEADER} "\n#endif /* __CODAC_H__ */\n") file(COPY ${CODAC_MAIN_HEADER} DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/../../include) + +# Getting header files from sources + + foreach(srcfile ${CODAC2_SRC}) + if(srcfile MATCHES "\\.h$" OR srcfile MATCHES "\\.hpp$") + list(APPEND CODAC2_HDR ${srcfile}) + # Copying header files for other Codac libraries compiled in the same time + file(COPY ${srcfile} DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/../../include) + endif() + endforeach() + +# Generating a codac.h file + + set(CODAC2_MAIN_HEADER ${CMAKE_CURRENT_BINARY_DIR}/codac2.h) + file(WRITE ${CODAC2_MAIN_HEADER} "/* This file is generated by CMake */\n\n") + file(APPEND ${CODAC2_MAIN_HEADER} "#ifndef __CODAC2_H__\n#define __CODAC2_H__\n\n") + foreach(header_path ${CODAC2_HDR}) + get_filename_component(header_name ${header_path} NAME) + file(APPEND ${CODAC2_MAIN_HEADER} "#include <${header_name}>\n") + endforeach() + file(APPEND ${CODAC2_MAIN_HEADER} "\n#endif /* __CODAC_H__ */\n") + file(COPY ${CODAC2_MAIN_HEADER} DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/../../include) # Install files in system directories install(TARGETS codac DESTINATION ${CMAKE_INSTALL_LIBDIR}) - install(FILES ${HDR} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/codac) - install(FILES ${CODAC_MAIN_HEADER} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/codac) \ No newline at end of file + install(FILES ${CODAC_HDR} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/codac) + install(FILES ${CODAC_MAIN_HEADER} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/codac) + install(FILES ${CODAC2_HDR} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/codac) + install(FILES ${CODAC2_MAIN_HEADER} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/codac) \ No newline at end of file diff --git a/src/robotics/graphics/codac_VIBesFigMap.cpp b/src/robotics/graphics/codac_VIBesFigMap.cpp index b8efd5f98..1bfdd4a73 100644 --- a/src/robotics/graphics/codac_VIBesFigMap.cpp +++ b/src/robotics/graphics/codac_VIBesFigMap.cpp @@ -61,7 +61,7 @@ namespace codac for(it_trajs = m_map_trajs.begin(); it_trajs != m_map_trajs.end(); it_trajs++) m_view_box |= draw_trajectory(it_trajs->first); - axis_limits(m_view_box, true, 0.02); + //axis_limits(m_view_box, true, 0.02); } void VIBesFigMap::show(float robot_size) @@ -710,7 +710,7 @@ namespace codac assert(pose.size() == 2 || pose.size() == 3); float robot_size = size == -1 ? m_robot_size : size; double robot_heading = pose.size() == 3 ? pose[2] : 0.; - axis_limits(m_view_box | pose.subvector(0,1), true); + //axis_limits(m_view_box | pose.subvector(0,1), true); //vibes::drawTank(pose[0], pose[1], robot_heading * 180. / M_PI, robot_size, "black[yellow]", params); vibes::drawAUV(pose[0], pose[1], robot_heading * 180. / M_PI, robot_size, "black[yellow]", params); } From 104b57476ad49b4a212323f6e88e5a9fb257d467 Mon Sep 17 00:00:00 2001 From: SimonRohou Date: Mon, 28 Aug 2023 17:21:42 +0200 Subject: [PATCH 113/256] [core] templated Interval/Vector/Matrix --- .../domains/interval/codac2_IntervalMatrix.h | 2 +- .../domains/interval/codac2_IntervalVector.h | 174 +++++++++++------- src/core/2/domains/paving/codac2_Paving.h | 22 +-- .../2/domains/tube/codac2_AbstractConstTube.h | 1 - .../2/domains/tube/codac2_AbstractSlice.h | 1 - src/core/2/domains/tube/codac2_Slice.h | 1 - .../2/integration/codac2_AbstractDomain.h | 2 - src/core/2/variables/codac2_Matrix.h | 2 +- src/core/2/variables/codac2_Vector.h | 60 ++++-- 9 files changed, 169 insertions(+), 96 deletions(-) diff --git a/src/core/2/domains/interval/codac2_IntervalMatrix.h b/src/core/2/domains/interval/codac2_IntervalMatrix.h index 11ccdeb45..372a40111 100644 --- a/src/core/2/domains/interval/codac2_IntervalMatrix.h +++ b/src/core/2/domains/interval/codac2_IntervalMatrix.h @@ -20,7 +20,7 @@ namespace codac2 { - template + template class IntervalMatrix : public Eigen::Matrix { public: diff --git a/src/core/2/domains/interval/codac2_IntervalVector.h b/src/core/2/domains/interval/codac2_IntervalVector.h index c547d8d64..65bb8167f 100644 --- a/src/core/2/domains/interval/codac2_IntervalVector.h +++ b/src/core/2/domains/interval/codac2_IntervalVector.h @@ -19,51 +19,61 @@ #include #include "codac2_Interval.h" #include "codac2_IntervalMatrix.h" -#include "codac2_Vector.h" namespace codac2 { - template - class IntervalVector : public Eigen::Matrix + template + class Vector_; + + using Eigen::Dynamic; + + template + class IntervalVector_ : public Eigen::Matrix { public: - IntervalVector() + IntervalVector_() : Eigen::Matrix() - { + { } + IntervalVector_(size_t n) + : Eigen::Matrix(n) + { + assert(N == Dynamic || N == n); } - explicit IntervalVector(const Vector& v) - : Eigen::Matrix() + template + explicit IntervalVector_(const Vector_& v) + : Eigen::Matrix(v.size()) { - for(size_t i = 0 ; i < N ; i++) + static_assert(N == M || N == -1 || M == -1); + for(size_t i = 0 ; i < size() ; i++) (*this)[i] = v[i]; } - explicit IntervalVector(const Interval& xi) - : Eigen::Matrix() - { - (*this)(0,0) = xi; - } + //explicit IntervalVector_(const Interval& xi) + // : Eigen::Matrix() + //{ + // (*this)(0,0) = xi; + //} - IntervalVector(std::initializer_list l) + IntervalVector_(std::initializer_list l) : Eigen::Matrix() { - assert(l.size() == N); + assert(l.size() == size()); size_t i = 0; for(const auto& li : l) (*this)[i++] = li; } template - IntervalVector(const Eigen::MatrixBase& other) + IntervalVector_(const Eigen::MatrixBase& other) : Eigen::Matrix(other) { } - // This method allows you to assign Eigen expressions to IntervalVector + // This method allows you to assign Eigen expressions to IntervalVector_ template - IntervalVector& operator=(const Eigen::MatrixBase& other) + IntervalVector_& operator=(const Eigen::MatrixBase& other) { this->Eigen::Matrix::operator=(other); return *this; @@ -71,26 +81,27 @@ namespace codac2 constexpr size_t size() const { - return N; + return this->Eigen::Matrix::size(); } bool is_empty() const { - for(size_t i = 0 ; i < N ; i++) + for(size_t i = 0 ; i < size() ; i++) if((*this)[i].is_empty()) return true; return false; } - bool intersects(const IntervalVector& x) const + bool intersects(const IntervalVector_& x) const { - for(size_t i = 0 ; i < N ; i++) + // todo: case of Dynamic vs Fixed + for(size_t i = 0 ; i < size() ; i++) if(!(*this)[i].intersects(x[i])) return false; return true; } - std::pair,IntervalVector> bisect(float ratio) const + std::pair,IntervalVector_> bisect(float ratio = 0.49) const { assert(Interval(0,1).interior_contains(ratio)); size_t i = largest_dim(); @@ -113,7 +124,7 @@ namespace codac2 if(is_empty()) return 0.; double v = 0.; - for(size_t i = 0 ; i < N ; i++) + for(size_t i = 0 ; i < size() ; i++) { if((*this)[i].is_unbounded()) return POS_INFINITY; if((*this)[i].is_degenerated()) return 0.; @@ -122,47 +133,55 @@ namespace codac2 return exp(v); } - Vector lb() const + Vector_ lb() const { assert(!is_empty()); // todo: use nan instead of assert? - Vector lb; - for(size_t i = 0 ; i < N ; i++) + Vector_ lb(size()); + for(size_t i = 0 ; i < size() ; i++) lb[i] = (*this)[i].lb(); return lb; } - Vector ub() const + Vector_ ub() const { assert(!is_empty()); // todo: use nan instead of assert? - Vector ub; - for(size_t i = 0 ; i < N ; i++) + Vector_ ub(size()); + for(size_t i = 0 ; i < size() ; i++) ub[i] = (*this)[i].ub(); return ub; } - Vector mid() const + Vector_ mid() const { assert(!is_empty()); // todo: use nan instead of assert? - Vector m; - for(size_t i = 0 ; i < N ; i++) + Vector_ m(size()); + for(size_t i = 0 ; i < size() ; i++) m[i] = (*this)[i].mid(); return m; } void set_empty() { - for(size_t i = 0 ; i < N ; i++) + for(size_t i = 0 ; i < size() ; i++) (*this)[i].set_empty(); } - static IntervalVector empty_set() + static IntervalVector_ empty_set(size_t n = N) { - IntervalVector x; + IntervalVector_ x(n); x.set_empty(); return x; } - IntervalVector& operator&=(const IntervalVector& x) + IntervalVector_& inflate(double r) + { + assert(r >= 0.); + for(size_t i = 0 ; i < size() ; i++) + (*this)[i].inflate(r); + return *this; + } + + IntervalVector_& operator&=(const IntervalVector_& x) { if(!is_empty()) { @@ -170,13 +189,13 @@ namespace codac2 set_empty(); else - for(size_t i = 0 ; i < N ; i++) + for(size_t i = 0 ; i < size() ; i++) (*this)[i] &= x[i]; } return *this; } - IntervalVector& operator|=(const IntervalVector& x) + IntervalVector_& operator|=(const IntervalVector_& x) { if(!x.is_empty()) { @@ -184,78 +203,89 @@ namespace codac2 *this = x; else - for(size_t i = 0 ; i < N ; i++) + for(size_t i = 0 ; i < size() ; i++) (*this)[i] |= x[i]; } return *this; } - IntervalVector& operator+=(const Vector& x) + IntervalVector_& operator+=(const Vector_& x) { (*this).noalias() += x.template cast(); return *this; } - IntervalVector& operator-=(const Vector& x) + IntervalVector_& operator-=(const Vector_& x) { (*this).noalias() -= x.template cast(); return *this; } template - IntervalVector subvector() const + IntervalVector_ subvector() const { assert(N1 >= 0 && N1 < N && N2 >= 0 && N2 < N && N1 <= N2); return this->template block(N1,0); } - template - void put(const IntervalVector& x) + IntervalVector_<> subvector(size_t start_index, size_t end_index) const + { + assert(end_index >= 0 && start_index >= 0); + assert(end_index < size() && start_index <= end_index); + + IntervalVector_<> s(end_index-start_index+1); + for(size_t i = 0 ; i < s.size() ; i++) + s[i] = (*this)[i+start_index]; + return s; + } + + template + void put(const IntervalVector_& x) { assert(I >= 0 && I < N && M+I <= N); this->template block(I,0) << x; } }; - template - codac::IntervalVector to_codac1(const IntervalVector& x) + template + codac::IntervalVector to_codac1(const IntervalVector_& x) { - ibex::IntervalVector x_(N); - for(size_t i = 0 ; i < N ; i++) + ibex::IntervalVector x_(x.size()); + for(size_t i = 0 ; i < x.size() ; i++) x_[i] = x[i]; return x_; } - template - IntervalVector to_codac2(const codac::IntervalVector& x) + template + IntervalVector_ to_codac2(const codac::IntervalVector& x) { assert(x.size() == N); - IntervalVector x_; - for(size_t i = 0 ; i < N ; i++) + IntervalVector_ x_(x.size()); + for(size_t i = 0 ; i < x.size() ; i++) x_[i] = x[i]; return x_; } - template - IntervalVector cart_prod(const IntervalVector& x1, const Interval& x2) + template + IntervalVector_ cart_prod(const IntervalVector_& x1, const Interval& x2) { - IntervalVector x; + IntervalVector_ x; x << x1,x2; return x; } - template - IntervalVector cart_prod(const Interval& x1, const IntervalVector& x2) + template + IntervalVector_ cart_prod(const Interval& x1, const IntervalVector_& x2) { - IntervalVector x; + IntervalVector_ x; x << x1,x2; return x; } - template - IntervalVector cart_prod(const IntervalVector& x1, const IntervalVector& x2) + template + IntervalVector_ cart_prod(const IntervalVector_& x1, const IntervalVector_& x2) { - IntervalVector x; + IntervalVector_ x; x << x1,x2; return x; } @@ -270,6 +300,26 @@ namespace codac2 return x_; } + class IntervalVector : public IntervalVector_<> + { + public: + + explicit IntervalVector(int n) + : IntervalVector_<>(n) + { } + + IntervalVector(const IntervalVector_<>& x) + : IntervalVector_<>(x) + { } + + template + explicit IntervalVector(const Vector_& v) + : IntervalVector_<>(v) + { + + } + }; + } // namespace codac #endif \ No newline at end of file diff --git a/src/core/2/domains/paving/codac2_Paving.h b/src/core/2/domains/paving/codac2_Paving.h index d7b11218d..f1450fe35 100644 --- a/src/core/2/domains/paving/codac2_Paving.h +++ b/src/core/2/domains/paving/codac2_Paving.h @@ -19,24 +19,24 @@ namespace codac2 { - template + template class Paving : public std::enable_shared_from_this> { public: - Paving() - : _x(IntervalVector()) + Paving(size_t n) + : _x(IntervalVector_(n)) { } - Paving(const IntervalVector& x) + Paving(const IntervalVector_& x) : _x(x) { } - const IntervalVector& box() const + const IntervalVector_& box() const { return _x; } @@ -65,19 +65,19 @@ namespace codac2 _right = std::make_shared(p.second); } - IntervalVector hull_box() const + IntervalVector_ hull_box() const { if(is_leaf()) return _x; - auto hull = IntervalVector::empty_set(); + auto hull = IntervalVector_::empty_set(); if(_left) hull |= _left->hull_box(); if(_right) hull |= _right->hull_box(); return hull; } - std::list>> boxes_list(const IntervalVector& intersect = IntervalVector<2>()) const + std::list>> boxes_list(const IntervalVector_& intersect = IntervalVector_()) const { - std::list>> l; + std::list>> l; boxes_list_push(l, intersect); return l; } @@ -91,7 +91,7 @@ namespace codac2 protected: - void boxes_list_push(std::list>>& l, const IntervalVector& intersect = IntervalVector<2>()) const + void boxes_list_push(std::list>>& l, const IntervalVector_& intersect = IntervalVector_()) const { if(is_leaf() && !_x.is_empty() && _x.intersects(intersect)) l.push_back(std::cref(_x)); @@ -115,7 +115,7 @@ namespace codac2 public: // todo - IntervalVector _x; + IntervalVector_ _x; std::shared_ptr _left = nullptr, _right = nullptr; }; diff --git a/src/core/2/domains/tube/codac2_AbstractConstTube.h b/src/core/2/domains/tube/codac2_AbstractConstTube.h index c84addb12..155018296 100644 --- a/src/core/2/domains/tube/codac2_AbstractConstTube.h +++ b/src/core/2/domains/tube/codac2_AbstractConstTube.h @@ -25,7 +25,6 @@ namespace codac2 using codac::Trajectory; using codac::TrajectoryVector; using codac::Interval; - using codac::IntervalVector; using codac::BoolInterval; template diff --git a/src/core/2/domains/tube/codac2_AbstractSlice.h b/src/core/2/domains/tube/codac2_AbstractSlice.h index 6328549aa..5ad21e0bb 100644 --- a/src/core/2/domains/tube/codac2_AbstractSlice.h +++ b/src/core/2/domains/tube/codac2_AbstractSlice.h @@ -24,7 +24,6 @@ namespace codac2 { using codac::Interval; - using codac::IntervalVector; using codac::TrajectoryVector; class TSlice; diff --git a/src/core/2/domains/tube/codac2_Slice.h b/src/core/2/domains/tube/codac2_Slice.h index ca136ca02..1810111bf 100644 --- a/src/core/2/domains/tube/codac2_Slice.h +++ b/src/core/2/domains/tube/codac2_Slice.h @@ -29,7 +29,6 @@ namespace codac2 { using codac::Interval; - using codac::IntervalVector; using codac::TrajectoryVector; using codac::BoolInterval; diff --git a/src/core/2/integration/codac2_AbstractDomain.h b/src/core/2/integration/codac2_AbstractDomain.h index f7b837f7b..550dfcb53 100644 --- a/src/core/2/integration/codac2_AbstractDomain.h +++ b/src/core/2/integration/codac2_AbstractDomain.h @@ -16,8 +16,6 @@ namespace codac2 { - using codac::IntervalVector; - /** * \class AbstractDomain * \brief ... diff --git a/src/core/2/variables/codac2_Matrix.h b/src/core/2/variables/codac2_Matrix.h index 7dce342a9..cd4cad8f3 100644 --- a/src/core/2/variables/codac2_Matrix.h +++ b/src/core/2/variables/codac2_Matrix.h @@ -18,7 +18,7 @@ namespace codac2 { - template + template class Matrix : public Eigen::Matrix { public: diff --git a/src/core/2/variables/codac2_Vector.h b/src/core/2/variables/codac2_Vector.h index e944e24b1..212741fe3 100644 --- a/src/core/2/variables/codac2_Vector.h +++ b/src/core/2/variables/codac2_Vector.h @@ -20,36 +20,40 @@ namespace codac2 { - template - class Vector : public Eigen::Matrix + using Eigen::Dynamic; + + template + class Vector_ : public Eigen::Matrix { public: - Vector() + Vector_() { } - Vector(double l) : Vector({ l }) + Vector_(size_t n) + : Eigen::Matrix(n) { - + assert(N == Dynamic || N == n); } - Vector(std::initializer_list l) + Vector_(std::initializer_list l) : Eigen::Matrix(l.size()) { + assert(N == l.size() || N == -1); size_t i = 0; for(double li : l) (*this)(i++,0) = li; } template - Vector(const Eigen::MatrixBase& other) + Vector_(const Eigen::MatrixBase& other) : Eigen::Matrix(other) { } - // This method allows you to assign Eigen expressions to MyVectorType + // This method allows you to assign Eigen expressions to MyVector_Type template - Vector& operator=(const Eigen::MatrixBase& other) + Vector_& operator=(const Eigen::MatrixBase& other) { this->Eigen::Matrix::operator=(other); return *this; @@ -60,29 +64,53 @@ namespace codac2 return Matrix(Eigen::Matrix(this->asDiagonal())); } - static Vector zeros() + static Vector_ zeros() { - Vector v; + Vector_ v; return v; } }; - template - Matrix diag(const Vector v) + template + Matrix diag(const Vector_ v) { return v.as_diag(); } - template - Vector to_codac2(const codac::Vector& x) + template + Vector_ to_codac2(const codac::Vector& x) { assert(x.size() == N); - Vector x_; + Vector_ x_; for(size_t i = 0 ; i < N ; i++) x_[i] = x[i]; return x_; } + class Vector : public Vector_<> + { + public: + + explicit Vector(int n) + : Vector_<>(n) + { } + + Vector(const Vector_<>& x) + : Vector_<>(x) + { } + + explicit Vector(std::initializer_list l) + : Vector_<>(l) + { } + + template + explicit Vector(const Vector_& v) + : Vector_<>(v) + { + + } + }; + } // namespace codac #endif \ No newline at end of file From 297bf8e6aaefac67681c8be2f0846073e3b9d2a0 Mon Sep 17 00:00:00 2001 From: SimonRohou Date: Tue, 5 Sep 2023 16:01:58 +0200 Subject: [PATCH 114/256] [core] templated Interval/Vector/Matrix --- src/core/2/domains/interval/codac2_IntervalMatrix.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/2/domains/interval/codac2_IntervalMatrix.h b/src/core/2/domains/interval/codac2_IntervalMatrix.h index 372a40111..d65a69fa1 100644 --- a/src/core/2/domains/interval/codac2_IntervalMatrix.h +++ b/src/core/2/domains/interval/codac2_IntervalMatrix.h @@ -21,7 +21,7 @@ namespace codac2 { template - class IntervalMatrix : public Eigen::Matrix + class IntervalMatrix_ : public Eigen::Matrix { public: From 5ba2ee0b9b25726a20435d6bad26fc01f64a90d8 Mon Sep 17 00:00:00 2001 From: SimonRohou Date: Fri, 8 Sep 2023 11:53:48 +0200 Subject: [PATCH 115/256] [core] templated Interval/Vector/Matrix --- src/core/2/domains/interval/codac2_Interval.h | 4 +- .../domains/interval/codac2_IntervalMatrix.h | 273 +++++++++++++++++- .../domains/interval/codac2_IntervalVector.h | 180 ++---------- src/core/2/domains/paving/codac2_Paving.h | 10 + src/core/2/variables/codac2_Matrix.h | 21 +- src/core/2/variables/codac2_Vector.h | 26 +- 6 files changed, 344 insertions(+), 170 deletions(-) diff --git a/src/core/2/domains/interval/codac2_Interval.h b/src/core/2/domains/interval/codac2_Interval.h index c87c3e56c..c829cb992 100644 --- a/src/core/2/domains/interval/codac2_Interval.h +++ b/src/core/2/domains/interval/codac2_Interval.h @@ -35,7 +35,7 @@ namespace Eigen IsComplex = 0, IsInteger = 0, IsSigned = 1, - RequireInitialization = 1, + RequireInitialization = 0, ReadCost = 1, AddCost = 3, MulCost = 3 @@ -49,7 +49,7 @@ namespace codac2 inline const Interval& real(const Interval& x) { return x; } inline Interval imag(const Interval&) { return 0.; } inline Interval abs(const Interval& x) { return ibex::abs(x); } - inline Interval abs2(const Interval& x) { return x*x; } + inline Interval abs2(const Interval& x) { return ibex::sqr(x); } } // namespace codac diff --git a/src/core/2/domains/interval/codac2_IntervalMatrix.h b/src/core/2/domains/interval/codac2_IntervalMatrix.h index d65a69fa1..dbcba4c66 100644 --- a/src/core/2/domains/interval/codac2_IntervalMatrix.h +++ b/src/core/2/domains/interval/codac2_IntervalMatrix.h @@ -13,18 +13,285 @@ #define __CODAC2_INTERVALMATRIX_H__ #include -#include #include #include #include +#include namespace codac2 { - template - class IntervalMatrix_ : public Eigen::Matrix + using Eigen::Dynamic; + + template + class IntervalMatrix_ : public Eigen::Matrix + { + public: + + IntervalMatrix_() + : Eigen::Matrix() + { + + } + + IntervalMatrix_(size_t nb_rows, size_t nb_cols) + : Eigen::Matrix(nb_rows, nb_cols) + { + assert(R == Dynamic || R == nb_rows); + assert(C == Dynamic || C == nb_cols); + } + + template + IntervalMatrix_(const Eigen::MatrixBase& other) + : Eigen::Matrix(other) + { } + + // This method allows you to assign Eigen expressions to IntervalMatrix_ + template + IntervalMatrix_& operator=(const Eigen::MatrixBase& other) + { + this->Eigen::Matrix::operator=(other); + return *this; + } + + constexpr size_t size() const + { + return this->Eigen::Matrix::size(); + } + + bool is_empty() const + { + for(size_t i = 0 ; i < size() ; i++) + if((this->data()+i)->is_empty()) + return true; + return false; + } + + bool intersects(const IntervalMatrix_& x) const + { + for(size_t i = 0 ; i < size() ; i++) + if(!(this->data()+i)->intersects(*(x.data()+i))) + return false; + return true; + } + + size_t thinnest_diam_index() const + { + return extr_diam_index(true); + } + + size_t largest_diam_index() const + { + return extr_diam_index(false); + } + + size_t extr_diam_index(bool min) const + { + // This code originates from the ibex-lib + // See: ibex_TemplateVector.h + // Author: Gilles Chabert + + double d = min ? POS_INFINITY : -1; // -1 to be sure that even a 0-diameter interval can be selected + size_t selected_index = -1; + bool unbounded = false; + assert(!this->is_empty() && "Diameter of an empty IntervalVector is undefined"); + + size_t i; + + for(i = 0 ; i < this->size() ; i++) + { + if((this->data()+i)->is_unbounded()) + { + unbounded = true; + if(!min) break; + } + else + { + double w = (this->data()+i)->diam(); + if(min ? wd) + { + selected_index = i; + d = w; + } + } + } + + if(min && selected_index == -1) + { + assert(unbounded); + // the selected interval is the first one. + i = 0; + } + + // The unbounded intervals are not considered if we look for the minimal diameter + // and some bounded intervals have been found (selected_index!=-1) + if(unbounded && (!min || selected_index==-1)) + { + double pt = min ? NEG_INFINITY : POS_INFINITY; // keep the point less/most distant from +oo (we normalize if the lower bound is -oo) + selected_index = i; + for(; i < this->size() ; i++) + { + if((this->data()+i)->lb() == NEG_INFINITY) + { + if((this->data()+i)->ub() == POS_INFINITY) + { + if(!min) + { + selected_index = i; + break; + } + } + if((min && (-(this->data()+i)->ub() > pt)) || (!min && (-(this->data()+i)->ub() < pt))) + { + selected_index = i; + pt = -(this->data()+i)->ub(); + } + } + else if((this->data()+i)->ub() == POS_INFINITY) + { + if((min && ((this->data()+i)->lb() > pt)) || (!min && ((this->data()+i)->lb() < pt))) + { + selected_index = i; + pt = (this->data()+i)->lb(); + } + } + } + } + + return selected_index; + } + + auto bisect(float ratio = 0.49) const + { + assert(Interval(0,1).interior_contains(ratio)); + size_t i = largest_diam_index(); + assert((this->data()+i)->is_bisectable()); + + auto p = std::make_pair(*this,*this); + auto pi = (this->data()+i)->bisect(ratio); + *(p.first.data()+i) = pi.first; + *(p.second.data()+i) = pi.second; + return p; + } + + double volume() const + { + if(this->is_empty()) + return 0.; + double v = 0.; + for(size_t i = 0 ; i < this->size() ; i++) + { + if((this->data()+i)->is_unbounded()) return POS_INFINITY; + if((this->data()+i)->is_degenerated()) return 0.; + v += log((this->data()+i)->diam()); + } + return exp(v); + } + + Matrix_ lb() const + { + assert(!this->is_empty()); // todo: use nan instead of assert? + Matrix_ lb; + for(size_t i = 0 ; i < this->size() ; i++) + *(lb.data()+i) = (this->data()+i)->lb(); + return lb; + } + + Matrix_ ub() const + { + assert(!this->is_empty()); // todo: use nan instead of assert? + Matrix_ ub; + for(size_t i = 0 ; i < this->size() ; i++) + *(ub.data()+i) = (this->data()+i)->ub(); + return ub; + } + + Matrix_ mid() const + { + assert(!this->is_empty()); // todo: use nan instead of assert? + Matrix_ m; + for(size_t i = 0 ; i < this->size() ; i++) + *(m.data()+i) = (this->data()+i)->mid(); + return m; + } + + void set_empty() + { + for(size_t i = 0 ; i < this->size() ; i++) + (this->data()+i)->set_empty(); + } + + static IntervalMatrix_ empty_set(size_t nb_rows = R, size_t nb_cols = C) + { + IntervalMatrix_ x(nb_rows, nb_cols); + x.set_empty(); + return x; + } + + auto& inflate(double r) + { + assert(r >= 0.); + for(size_t i = 0 ; i < this->size() ; i++) + (this->data()+i)->inflate(r); + return *this; + } + + auto& operator&=(const IntervalMatrix_& x) + { + if(!this->is_empty()) + { + if(x.is_empty()) + this->set_empty(); + else + for(size_t i = 0 ; i < this->size() ; i++) + *(this->data()+i) &= *(x.data()+i); + } + return *this; + } + + auto& operator|=(const IntervalMatrix_& x) + { + if(!x.is_empty()) + { + if(this->is_empty()) + *this = x; + else + for(size_t i = 0 ; i < this->size() ; i++) + *(this->data()+i) |= *(x.data()+i); + } + return *this; + } + + auto& operator+=(const IntervalMatrix_& x) + { + (*this).noalias() += x;//.template cast(); + return *this; + } + + auto& operator-=(const IntervalMatrix_& x) + { + (*this).noalias() -= x;//.template cast(); + return *this; + } + }; + + class IntervalMatrix : public IntervalMatrix_<> { public: + explicit IntervalMatrix(size_t nb_rows, size_t nb_cols) + : IntervalMatrix_<>(nb_rows, nb_cols) + { } + + IntervalMatrix(const IntervalMatrix_<>& x) + : IntervalMatrix_<>(x) + { } + + template + explicit IntervalMatrix(const Matrix_& v) + : IntervalMatrix_<>(v) + { + + } }; } // namespace codac diff --git a/src/core/2/domains/interval/codac2_IntervalVector.h b/src/core/2/domains/interval/codac2_IntervalVector.h index 65bb8167f..28488b8c9 100644 --- a/src/core/2/domains/interval/codac2_IntervalVector.h +++ b/src/core/2/domains/interval/codac2_IntervalVector.h @@ -28,144 +28,64 @@ namespace codac2 using Eigen::Dynamic; template - class IntervalVector_ : public Eigen::Matrix + class IntervalVector_ : public IntervalMatrix_ { public: IntervalVector_() - : Eigen::Matrix() + : IntervalMatrix_() { } IntervalVector_(size_t n) - : Eigen::Matrix(n) + : IntervalMatrix_(n,1) { assert(N == Dynamic || N == n); } template - explicit IntervalVector_(const Vector_& v) - : Eigen::Matrix(v.size()) + IntervalVector_(const Matrix_& v) + : IntervalMatrix_(v.size(),1) { static_assert(N == M || N == -1 || M == -1); - for(size_t i = 0 ; i < size() ; i++) + for(size_t i = 0 ; i < IntervalMatrix_::size() ; i++) (*this)[i] = v[i]; } //explicit IntervalVector_(const Interval& xi) - // : Eigen::Matrix() + // : IntervalMatrix_() //{ // (*this)(0,0) = xi; //} IntervalVector_(std::initializer_list l) - : Eigen::Matrix() + : IntervalMatrix_(l.size(),1) { - assert(l.size() == size()); + assert(l.size() == this->size()); size_t i = 0; for(const auto& li : l) (*this)[i++] = li; } + template + IntervalVector_(const IntervalMatrix_& x) + : IntervalMatrix_(x) + { + assert(M == Dynamic || M == N); + } + template IntervalVector_(const Eigen::MatrixBase& other) - : Eigen::Matrix(other) + : IntervalMatrix_(other) { } // This method allows you to assign Eigen expressions to IntervalVector_ template IntervalVector_& operator=(const Eigen::MatrixBase& other) { - this->Eigen::Matrix::operator=(other); + this->IntervalMatrix_::operator=(other); return *this; } - constexpr size_t size() const - { - return this->Eigen::Matrix::size(); - } - - bool is_empty() const - { - for(size_t i = 0 ; i < size() ; i++) - if((*this)[i].is_empty()) - return true; - return false; - } - - bool intersects(const IntervalVector_& x) const - { - // todo: case of Dynamic vs Fixed - for(size_t i = 0 ; i < size() ; i++) - if(!(*this)[i].intersects(x[i])) - return false; - return true; - } - - std::pair,IntervalVector_> bisect(float ratio = 0.49) const - { - assert(Interval(0,1).interior_contains(ratio)); - size_t i = largest_dim(); - assert((*this)[i].is_bisectable()); - - auto p = std::make_pair(*this,*this); - auto pi = (*this)[i].bisect(ratio); - p.first[i] = pi.first; - p.second[i] = pi.second; - return p; - } - - size_t largest_dim() const - { - return to_codac1(*this).extr_diam_index(false); - } - - double volume() const - { - if(is_empty()) - return 0.; - double v = 0.; - for(size_t i = 0 ; i < size() ; i++) - { - if((*this)[i].is_unbounded()) return POS_INFINITY; - if((*this)[i].is_degenerated()) return 0.; - v += log((*this)[i].diam()); - } - return exp(v); - } - - Vector_ lb() const - { - assert(!is_empty()); // todo: use nan instead of assert? - Vector_ lb(size()); - for(size_t i = 0 ; i < size() ; i++) - lb[i] = (*this)[i].lb(); - return lb; - } - - Vector_ ub() const - { - assert(!is_empty()); // todo: use nan instead of assert? - Vector_ ub(size()); - for(size_t i = 0 ; i < size() ; i++) - ub[i] = (*this)[i].ub(); - return ub; - } - - Vector_ mid() const - { - assert(!is_empty()); // todo: use nan instead of assert? - Vector_ m(size()); - for(size_t i = 0 ; i < size() ; i++) - m[i] = (*this)[i].mid(); - return m; - } - - void set_empty() - { - for(size_t i = 0 ; i < size() ; i++) - (*this)[i].set_empty(); - } - static IntervalVector_ empty_set(size_t n = N) { IntervalVector_ x(n); @@ -173,54 +93,6 @@ namespace codac2 return x; } - IntervalVector_& inflate(double r) - { - assert(r >= 0.); - for(size_t i = 0 ; i < size() ; i++) - (*this)[i].inflate(r); - return *this; - } - - IntervalVector_& operator&=(const IntervalVector_& x) - { - if(!is_empty()) - { - if(x.is_empty()) - set_empty(); - - else - for(size_t i = 0 ; i < size() ; i++) - (*this)[i] &= x[i]; - } - return *this; - } - - IntervalVector_& operator|=(const IntervalVector_& x) - { - if(!x.is_empty()) - { - if(is_empty()) - *this = x; - - else - for(size_t i = 0 ; i < size() ; i++) - (*this)[i] |= x[i]; - } - return *this; - } - - IntervalVector_& operator+=(const Vector_& x) - { - (*this).noalias() += x.template cast(); - return *this; - } - - IntervalVector_& operator-=(const Vector_& x) - { - (*this).noalias() -= x.template cast(); - return *this; - } - template IntervalVector_ subvector() const { @@ -231,7 +103,7 @@ namespace codac2 IntervalVector_<> subvector(size_t start_index, size_t end_index) const { assert(end_index >= 0 && start_index >= 0); - assert(end_index < size() && start_index <= end_index); + assert(end_index < this->size() && start_index <= end_index); IntervalVector_<> s(end_index-start_index+1); for(size_t i = 0 ; i < s.size() ; i++) @@ -245,6 +117,18 @@ namespace codac2 assert(I >= 0 && I < N && M+I <= N); this->template block(I,0) << x; } + + auto& operator+=(const IntervalVector_& x) + { + (*this).noalias() += x;//.template cast(); + return *this; + } + + auto& operator-=(const IntervalVector_& x) + { + (*this).noalias() -= x;//.template cast(); + return *this; + } }; template @@ -304,7 +188,7 @@ namespace codac2 { public: - explicit IntervalVector(int n) + explicit IntervalVector(size_t n) : IntervalVector_<>(n) { } diff --git a/src/core/2/domains/paving/codac2_Paving.h b/src/core/2/domains/paving/codac2_Paving.h index f1450fe35..027295a09 100644 --- a/src/core/2/domains/paving/codac2_Paving.h +++ b/src/core/2/domains/paving/codac2_Paving.h @@ -12,6 +12,7 @@ #ifndef __CODAC2_PAVING_H__ #define __CODAC2_PAVING_H__ +#include #include #include #include "codac2_Interval.h" @@ -41,6 +42,15 @@ namespace codac2 return _x; } + bool is_empty() const + { + if(is_leaf()) + return _x.is_empty(); + + else + return (_left && _left->is_empty()) && (_right && _right->is_empty()); + } + bool is_leaf() const { return not _left && not _right; diff --git a/src/core/2/variables/codac2_Matrix.h b/src/core/2/variables/codac2_Matrix.h index cd4cad8f3..c18cdbb82 100644 --- a/src/core/2/variables/codac2_Matrix.h +++ b/src/core/2/variables/codac2_Matrix.h @@ -18,30 +18,39 @@ namespace codac2 { + using Eigen::Dynamic; + template - class Matrix : public Eigen::Matrix + class Matrix_ : public Eigen::Matrix { public: - Matrix() + Matrix_() { } + + Matrix_(size_t nb_rows, size_t nb_cols) + : Eigen::Matrix(nb_rows, nb_cols) + { + assert(R == Dynamic || R == nb_rows); + assert(C == Dynamic || C == nb_cols); + } template - Matrix(const Eigen::MatrixBase& other) + Matrix_(const Eigen::MatrixBase& other) : Eigen::Matrix(other) { } - // This method allows you to assign Eigen expressions to MyVectorType + // This method allows you to assign Eigen expressions to Matrix_ template - Matrix& operator=(const Eigen::MatrixBase& other) + Matrix_& operator=(const Eigen::MatrixBase& other) { this->Eigen::Matrix::operator=(other); return *this; } - static Matrix eye() + static Matrix_ eye() { return Eigen::Matrix::Identity(); } diff --git a/src/core/2/variables/codac2_Vector.h b/src/core/2/variables/codac2_Vector.h index 212741fe3..61b273cd0 100644 --- a/src/core/2/variables/codac2_Vector.h +++ b/src/core/2/variables/codac2_Vector.h @@ -12,9 +12,6 @@ #ifndef __CODAC2_VECTOR_H__ #define __CODAC2_VECTOR_H__ -#include -#include -#include #include #include @@ -23,7 +20,7 @@ namespace codac2 using Eigen::Dynamic; template - class Vector_ : public Eigen::Matrix + class Vector_ : public Matrix_ { public: @@ -33,25 +30,32 @@ namespace codac2 } Vector_(size_t n) - : Eigen::Matrix(n) + : Matrix_(n,1) { assert(N == Dynamic || N == n); } - Vector_(std::initializer_list l) : Eigen::Matrix(l.size()) + Vector_(std::initializer_list l) : Matrix_(l.size(),1) { assert(N == l.size() || N == -1); size_t i = 0; for(double li : l) (*this)(i++,0) = li; } + + template + Vector_(const Matrix_& x) + : Matrix_(x) + { + assert(M == Dynamic || M == N); + } template Vector_(const Eigen::MatrixBase& other) - : Eigen::Matrix(other) + : Matrix_(other) { } - // This method allows you to assign Eigen expressions to MyVector_Type + // This method allows you to assign Eigen expressions to Vector_ template Vector_& operator=(const Eigen::MatrixBase& other) { @@ -59,9 +63,9 @@ namespace codac2 return *this; } - Matrix as_diag() const + Matrix_ as_diag() const { - return Matrix(Eigen::Matrix(this->asDiagonal())); + return Matrix_(Eigen::Matrix(this->asDiagonal())); } static Vector_ zeros() @@ -72,7 +76,7 @@ namespace codac2 }; template - Matrix diag(const Vector_ v) + Matrix_ diag(const Vector_ v) { return v.as_diag(); } From b24b033c635962a95369ee489066d31b5c1d9dc4 Mon Sep 17 00:00:00 2001 From: SimonRohou Date: Fri, 8 Sep 2023 19:53:18 +0200 Subject: [PATCH 116/256] [core] templated Interval/Vector/Matrix --- .../domains/interval/codac2_IntervalMatrix.h | 184 ++++++++++++++++-- .../domains/interval/codac2_IntervalVector.h | 28 ++- src/core/2/variables/codac2_Matrix.h | 1 + 3 files changed, 198 insertions(+), 15 deletions(-) diff --git a/src/core/2/domains/interval/codac2_IntervalMatrix.h b/src/core/2/domains/interval/codac2_IntervalMatrix.h index dbcba4c66..1c52316dd 100644 --- a/src/core/2/domains/interval/codac2_IntervalMatrix.h +++ b/src/core/2/domains/interval/codac2_IntervalMatrix.h @@ -28,7 +28,7 @@ namespace codac2 public: IntervalMatrix_() - : Eigen::Matrix() + : Eigen::Matrix(R,C) { } @@ -40,6 +40,13 @@ namespace codac2 assert(C == Dynamic || C == nb_cols); } + IntervalMatrix_(size_t nb_rows, size_t nb_cols, const Interval& x) + : IntervalMatrix_(nb_rows, nb_cols) + { + for(size_t i = 0 ; i < size() ; i++) + *(this->data()+i) = x; + } + template IntervalMatrix_(const Eigen::MatrixBase& other) : Eigen::Matrix(other) @@ -66,14 +73,127 @@ namespace codac2 return false; } + bool is_flat() const + { + if(is_empty()) return true; + for(size_t i = 0 ; i < size() ; i++) + if((this->data()+i)->is_degenerated()) // don't use diam() because of roundoff + return true; + return false; + } + + bool is_unbounded() const + { + if(is_empty()) return false; + for(size_t i = 0 ; i < size() ; i++) + if((this->data()+i)->is_unbounded()) + return true; + return false; + } + + bool is_subset(const IntervalMatrix_& x) const + { + if(is_empty()) return true; + if(x.is_empty()) return false; + for(size_t i = 0 ; i < size() ; i++) + if(!(this->data()+i)->is_subset(*(x.data()+i))) + return false; + return true; + } + + bool is_strict_subset(const IntervalMatrix_& x) const + { + if(x.is_empty()) return false; + if(is_empty()) return true; + bool one_dim_strict_subset = false; + for(size_t i = 0 ; i < size() ; i++) + { + if((this->data()+i)->is_strict_subset(*(x.data()+i))) + one_dim_strict_subset = true; + if(!(this->data()+i)->is_subset(*(x.data()+i))) + return false; + } + return one_dim_strict_subset; + } + + bool is_superset(const IntervalMatrix_& x) const + { + return x.is_subset(*this); + } + + bool is_strict_superset(const IntervalMatrix_& x) const + { + return x.is_strict_subset(*this); + } + + bool contains(const Matrix_& x) const + { + if(is_empty()) + return false; + for(size_t i = 0 ; i < size() ; i++) + if(!(this->data()+i)->contains(*(x.data()+i))) + return false; + return true; + } + + bool interior_contains(const IntervalMatrix_& x) const + { + if(is_empty()) + return false; + for(size_t i = 0 ; i < size() ; i++) + if(!(this->data()+i)->interior_contains(*(x.data()+i))) + return false; + return true; + } + bool intersects(const IntervalMatrix_& x) const { + if(is_empty() || x.is_empty()) + return false; for(size_t i = 0 ; i < size() ; i++) if(!(this->data()+i)->intersects(*(x.data()+i))) return false; return true; } + bool overlaps(const IntervalMatrix_& x) const + { + if(is_empty() || x.is_empty()) + return false; + for(size_t i = 0 ; i < size() ; i++) + if(!(this->data()+i)->overlaps(*(x.data()+i))) + return false; + return true; + } + + bool is_disjoint(const IntervalMatrix_& x) const + { + if(is_empty() || x.is_empty()) + return true; + for(size_t i = 0 ; i < size() ; i++) + if((this->data()+i)->is_disjoint(*(x.data()+i))) + return true; + return false; + } + + bool is_bisectable() const + { + for(size_t i = 0 ; i < size() ; i++) + if((this->data()+i)->is_bisectable()) + return true; + return false; + } + + double min_diam() const + { + return (this->data()+extr_diam_index(true))->diam(); + } + + double max_diam() const + { + return (this->data()+extr_diam_index(false))->diam(); + } + size_t thinnest_diam_index() const { return extr_diam_index(true); @@ -160,11 +280,10 @@ namespace codac2 return selected_index; } - auto bisect(float ratio = 0.49) const + auto bisect(size_t i, float ratio = 0.49) const { - assert(Interval(0,1).interior_contains(ratio)); - size_t i = largest_diam_index(); assert((this->data()+i)->is_bisectable()); + assert(Interval(0,1).interior_contains(ratio)); auto p = std::make_pair(*this,*this); auto pi = (this->data()+i)->bisect(ratio); @@ -173,6 +292,11 @@ namespace codac2 return p; } + auto bisect_largest_dim(float ratio = 0.49) const + { + return bisect(largest_diam_index(), ratio); + } + double volume() const { if(this->is_empty()) @@ -190,7 +314,7 @@ namespace codac2 Matrix_ lb() const { assert(!this->is_empty()); // todo: use nan instead of assert? - Matrix_ lb; + Matrix_ lb(this->rows(), this->cols()); for(size_t i = 0 ; i < this->size() ; i++) *(lb.data()+i) = (this->data()+i)->lb(); return lb; @@ -199,7 +323,7 @@ namespace codac2 Matrix_ ub() const { assert(!this->is_empty()); // todo: use nan instead of assert? - Matrix_ ub; + Matrix_ ub(this->rows(), this->cols()); for(size_t i = 0 ; i < this->size() ; i++) *(ub.data()+i) = (this->data()+i)->ub(); return ub; @@ -208,22 +332,44 @@ namespace codac2 Matrix_ mid() const { assert(!this->is_empty()); // todo: use nan instead of assert? - Matrix_ m; + Matrix_ mid(this->rows(), this->cols()); for(size_t i = 0 ; i < this->size() ; i++) - *(m.data()+i) = (this->data()+i)->mid(); - return m; + *(mid.data()+i) = (this->data()+i)->mid(); + return mid; } - void set_empty() + Matrix_ rad() const + { + assert(!this->is_empty()); // todo: use nan instead of assert? + Matrix_ rad(this->rows(), this->cols()); + for(size_t i = 0 ; i < this->size() ; i++) + *(rad.data()+i) = (this->data()+i)->rad(); + return rad; + } + + Matrix_ diam() const + { + assert(!this->is_empty()); // todo: use nan instead of assert? + Matrix_ diam(this->rows(), this->cols()); + for(size_t i = 0 ; i < this->size() ; i++) + *(diam.data()+i) = (this->data()+i)->diam(); + return diam; + } + + void init(const Interval& x) { for(size_t i = 0 ; i < this->size() ; i++) - (this->data()+i)->set_empty(); + *(this->data()+i) = x; + } + + void set_empty() + { + init(Interval::empty_set()); } static IntervalMatrix_ empty_set(size_t nb_rows = R, size_t nb_cols = C) { - IntervalMatrix_ x(nb_rows, nb_cols); - x.set_empty(); + IntervalMatrix_ x(nb_rows, nb_cols, Interval::empty_set()); return x; } @@ -261,6 +407,18 @@ namespace codac2 return *this; } + auto operator&(const IntervalMatrix_& x) + { + auto y = *this; + return y &= x; + } + + auto operator|(const IntervalMatrix_& x) + { + auto y = *this; + return y |= x; + } + auto& operator+=(const IntervalMatrix_& x) { (*this).noalias() += x;//.template cast(); diff --git a/src/core/2/domains/interval/codac2_IntervalVector.h b/src/core/2/domains/interval/codac2_IntervalVector.h index 28488b8c9..804d99811 100644 --- a/src/core/2/domains/interval/codac2_IntervalVector.h +++ b/src/core/2/domains/interval/codac2_IntervalVector.h @@ -41,6 +41,12 @@ namespace codac2 { assert(N == Dynamic || N == n); } + + IntervalVector_(size_t n, const Interval& x) + : IntervalMatrix_(n,1,x) + { + assert(N == Dynamic || N == n); + } template IntervalVector_(const Matrix_& v) @@ -57,6 +63,24 @@ namespace codac2 // (*this)(0,0) = xi; //} + IntervalVector_(size_t n, double bounds[][2]) + : IntervalMatrix_(n,1) + { + for(size_t i = 0 ; i < n ; i++) + { + if(bounds == 0) // in case the user called IntervalVector(n,0) and 0 is interpreted as NULL! + (*this)[i] = Interval::zero(); + else + (*this)[i] = Interval(bounds[i][0],bounds[i][1]); + } + } + + IntervalVector_(double bounds[][2]) + : IntervalVector_(this->size(), bounds) + { + + } + IntervalVector_(std::initializer_list l) : IntervalMatrix_(l.size(),1) { @@ -64,6 +88,7 @@ namespace codac2 size_t i = 0; for(const auto& li : l) (*this)[i++] = li; + // todo: use thias as faster? std::copy(l.begin(), l.end(), vec); } template @@ -88,8 +113,7 @@ namespace codac2 static IntervalVector_ empty_set(size_t n = N) { - IntervalVector_ x(n); - x.set_empty(); + IntervalVector_ x(n, Interval::empty_set()); return x; } diff --git a/src/core/2/variables/codac2_Matrix.h b/src/core/2/variables/codac2_Matrix.h index c18cdbb82..c2a2ccb18 100644 --- a/src/core/2/variables/codac2_Matrix.h +++ b/src/core/2/variables/codac2_Matrix.h @@ -26,6 +26,7 @@ namespace codac2 public: Matrix_() + : Eigen::Matrix() { } From 06d8a99732ca8ec2a436efb608ebc2a540ecf5e0 Mon Sep 17 00:00:00 2001 From: SimonRohou Date: Fri, 8 Sep 2023 23:39:57 +0200 Subject: [PATCH 117/256] [core] templated Interval/Vector/Matrix with tests --- src/core/2/domains/interval/codac2_Interval.h | 4 +- .../domains/interval/codac2_IntervalMatrix.h | 54 +- .../domains/interval/codac2_IntervalVector.h | 115 +- .../2/domains/interval/codac2_cart_prod.h | 97 ++ src/core/2/variables/codac2_Matrix.h | 4 +- src/core/2/variables/codac2_Vector.h | 18 +- src/core/CMakeLists.txt | 1 + tests/core/CMakeLists.txt | 57 +- tests/core/tests_codac2_intervalvector.cpp | 1166 +++++++++++++++++ 9 files changed, 1417 insertions(+), 99 deletions(-) create mode 100644 src/core/2/domains/interval/codac2_cart_prod.h create mode 100644 tests/core/tests_codac2_intervalvector.cpp diff --git a/src/core/2/domains/interval/codac2_Interval.h b/src/core/2/domains/interval/codac2_Interval.h index c829cb992..2b417a05a 100644 --- a/src/core/2/domains/interval/codac2_Interval.h +++ b/src/core/2/domains/interval/codac2_Interval.h @@ -18,6 +18,8 @@ namespace codac2 { + const double oo = POS_INFINITY; + using codac::Interval; } // namespace codac @@ -35,7 +37,7 @@ namespace Eigen IsComplex = 0, IsInteger = 0, IsSigned = 1, - RequireInitialization = 0, + RequireInitialization = 1, ReadCost = 1, AddCost = 3, MulCost = 3 diff --git a/src/core/2/domains/interval/codac2_IntervalMatrix.h b/src/core/2/domains/interval/codac2_IntervalMatrix.h index 1c52316dd..76ecb92f7 100644 --- a/src/core/2/domains/interval/codac2_IntervalMatrix.h +++ b/src/core/2/domains/interval/codac2_IntervalMatrix.h @@ -28,30 +28,31 @@ namespace codac2 public: IntervalMatrix_() - : Eigen::Matrix(R,C) + : Eigen::Matrix() { } - IntervalMatrix_(size_t nb_rows, size_t nb_cols) + explicit IntervalMatrix_(size_t nb_rows, size_t nb_cols) : Eigen::Matrix(nb_rows, nb_cols) { - assert(R == Dynamic || R == nb_rows); - assert(C == Dynamic || C == nb_cols); + assert(R == Dynamic || R == (int)nb_rows); + assert(C == Dynamic || C == (int)nb_cols); } - IntervalMatrix_(size_t nb_rows, size_t nb_cols, const Interval& x) + explicit IntervalMatrix_(size_t nb_rows, size_t nb_cols, const Interval& x) : IntervalMatrix_(nb_rows, nb_cols) { for(size_t i = 0 ; i < size() ; i++) *(this->data()+i) = x; } + // This constructor allows you to construct IntervalMatrix_ from Eigen expressions template IntervalMatrix_(const Eigen::MatrixBase& other) : Eigen::Matrix(other) { } - + // This method allows you to assign Eigen expressions to IntervalMatrix_ template IntervalMatrix_& operator=(const Eigen::MatrixBase& other) @@ -211,7 +212,7 @@ namespace codac2 // Author: Gilles Chabert double d = min ? POS_INFINITY : -1; // -1 to be sure that even a 0-diameter interval can be selected - size_t selected_index = -1; + int selected_index = -1; bool unbounded = false; assert(!this->is_empty() && "Diameter of an empty IntervalVector is undefined"); @@ -244,7 +245,7 @@ namespace codac2 // The unbounded intervals are not considered if we look for the minimal diameter // and some bounded intervals have been found (selected_index!=-1) - if(unbounded && (!min || selected_index==-1)) + if(unbounded && (!min || selected_index == -1)) { double pt = min ? NEG_INFINITY : POS_INFINITY; // keep the point less/most distant from +oo (we normalize if the lower bound is -oo) selected_index = i; @@ -381,6 +382,23 @@ namespace codac2 return *this; } + bool operator==(const IntervalMatrix_& x) const + { + if(x.size() != this->size()) + return false; + if(is_empty() || x.is_empty()) + return is_empty() && x.is_empty(); + for(size_t i = 0 ; i < this->size() ; i++) + if(*(this->data()+i) != *(x.data()+i)) + return false; + return true; + } + + bool operator!=(const IntervalMatrix_& x) const + { + return !(*this == x); + } + auto& operator&=(const IntervalMatrix_& x) { if(!this->is_empty()) @@ -407,6 +425,18 @@ namespace codac2 return *this; } + auto operator+(const IntervalMatrix_& x) + { + auto y = *this; + return y += x; + } + + auto operator-(const IntervalMatrix_& x) + { + auto y = *this; + return y -= x; + } + auto operator&(const IntervalMatrix_& x) { auto y = *this; @@ -432,6 +462,14 @@ namespace codac2 } }; + template + auto operator-(const IntervalMatrix_& x) + { + auto y = x; + y.init(0.); + return y -= x; + } + class IntervalMatrix : public IntervalMatrix_<> { public: diff --git a/src/core/2/domains/interval/codac2_IntervalVector.h b/src/core/2/domains/interval/codac2_IntervalVector.h index 804d99811..6b4a8b2f3 100644 --- a/src/core/2/domains/interval/codac2_IntervalVector.h +++ b/src/core/2/domains/interval/codac2_IntervalVector.h @@ -12,6 +12,7 @@ #ifndef __CODAC2_INTERVALVECTOR_H__ #define __CODAC2_INTERVALVECTOR_H__ +#include #include #include #include @@ -36,20 +37,24 @@ namespace codac2 : IntervalMatrix_() { } - IntervalVector_(size_t n) + explicit IntervalVector_(size_t n) : IntervalMatrix_(n,1) { - assert(N == Dynamic || N == n); + assert(N == Dynamic || N == (int)n); } - IntervalVector_(size_t n, const Interval& x) + explicit IntervalVector_(size_t n, const Interval& x) : IntervalMatrix_(n,1,x) { - assert(N == Dynamic || N == n); + assert(N == Dynamic || N == (int)n); } + + explicit IntervalVector_(const Interval& x) + : IntervalMatrix_(1,1,x) + { } template - IntervalVector_(const Matrix_& v) + explicit IntervalVector_(const Matrix_& v) : IntervalMatrix_(v.size(),1) { static_assert(N == M || N == -1 || M == -1); @@ -57,13 +62,7 @@ namespace codac2 (*this)[i] = v[i]; } - //explicit IntervalVector_(const Interval& xi) - // : IntervalMatrix_() - //{ - // (*this)(0,0) = xi; - //} - - IntervalVector_(size_t n, double bounds[][2]) + explicit IntervalVector_(size_t n, double bounds[][2]) : IntervalMatrix_(n,1) { for(size_t i = 0 ; i < n ; i++) @@ -75,7 +74,7 @@ namespace codac2 } } - IntervalVector_(double bounds[][2]) + explicit IntervalVector_(double bounds[][2]) : IntervalVector_(this->size(), bounds) { @@ -84,7 +83,7 @@ namespace codac2 IntervalVector_(std::initializer_list l) : IntervalMatrix_(l.size(),1) { - assert(l.size() == this->size()); + assert(N == Dynamic || (int)l.size() == N); size_t i = 0; for(const auto& li : l) (*this)[i++] = li; @@ -92,17 +91,18 @@ namespace codac2 } template - IntervalVector_(const IntervalMatrix_& x) + explicit IntervalVector_(const IntervalMatrix_& x) : IntervalMatrix_(x) { assert(M == Dynamic || M == N); } + // This constructor allows you to construct IntervalVector_ from Eigen expressions template IntervalVector_(const Eigen::MatrixBase& other) : IntervalMatrix_(other) { } - + // This method allows you to assign Eigen expressions to IntervalVector_ template IntervalVector_& operator=(const Eigen::MatrixBase& other) @@ -111,9 +111,10 @@ namespace codac2 return *this; } - static IntervalVector_ empty_set(size_t n = N) + static IntervalVector_ empty_set() { - IntervalVector_ x(n, Interval::empty_set()); + IntervalVector_ x; + x.set_empty(); return x; } @@ -174,40 +175,6 @@ namespace codac2 return x_; } - template - IntervalVector_ cart_prod(const IntervalVector_& x1, const Interval& x2) - { - IntervalVector_ x; - x << x1,x2; - return x; - } - - template - IntervalVector_ cart_prod(const Interval& x1, const IntervalVector_& x2) - { - IntervalVector_ x; - x << x1,x2; - return x; - } - - template - IntervalVector_ cart_prod(const IntervalVector_& x1, const IntervalVector_& x2) - { - IntervalVector_ x; - x << x1,x2; - return x; - } - - template - auto cart_prod(const T1& x1, const T2& x2, const Args&... xi) // recursive variadic function - { - auto x_ = cart_prod(x1, x2); - if constexpr(sizeof...(xi) > 0) - return cart_prod(x_, xi...); - else - return x_; - } - class IntervalVector : public IntervalVector_<> { public: @@ -216,15 +183,57 @@ namespace codac2 : IntervalVector_<>(n) { } - IntervalVector(const IntervalVector_<>& x) + explicit IntervalVector(size_t n, const Interval& x) + : IntervalVector_<>(n, x) + { } + + explicit IntervalVector(const Interval& x) + : IntervalVector_<>({x}) + { } + + explicit IntervalVector(const IntervalVector_<>& x) : IntervalVector_<>(x) { } template explicit IntervalVector(const Vector_& v) : IntervalVector_<>(v) + { } + + explicit IntervalVector(size_t n, double bounds[][2]) + : IntervalVector_<>(n, bounds) + { } + + IntervalVector(std::initializer_list l) + : IntervalVector_<>(l) + { } + + // This constructor allows you to construct IntervalVector from Eigen expressions + template + IntervalVector(const Eigen::MatrixBase& other) + : IntervalVector_<>(other) + { } + + // This method allows you to assign Eigen expressions to IntervalVector + template + IntervalVector& operator=(const Eigen::MatrixBase& other) { + this->IntervalVector_<>::operator=(other); + return *this; + } + + void resize(size_t n) + { + // With resize of Eigen, the data is reallocated and all previous values are lost. + auto save = *this; + IntervalVector_<>::resize(n); + for(size_t i = 0 ; i < min(save.size(),n) ; i++) + (*this)[i] = save[i]; + } + static IntervalVector empty_set(size_t n) + { + return IntervalVector(n, Interval::empty_set()); } }; diff --git a/src/core/2/domains/interval/codac2_cart_prod.h b/src/core/2/domains/interval/codac2_cart_prod.h new file mode 100644 index 000000000..2f499b2fe --- /dev/null +++ b/src/core/2/domains/interval/codac2_cart_prod.h @@ -0,0 +1,97 @@ +/** + * \file + * + * ---------------------------------------------------------------------------- + * \date 2023 + * \author Simon Rohou + * \copyright Copyright 2023 Codac Team + * \license This program is distributed under the terms of + * the GNU Lesser General Public License (LGPL). + */ + +#ifndef __CODAC2_CARTPROD_H__ +#define __CODAC2_CARTPROD_H__ + +#include "codac2_Interval.h" +#include "codac2_IntervalVector.h" + +namespace codac2 +{ + IntervalVector cart_prod_dyn(const Interval& x1, const Interval& x2) + { + return IntervalVector({x1,x2}); + } + + auto cart_prod_static(const Interval& x1, const Interval& x2) + { + return IntervalVector_<2>({x1,x2}); + } + + auto cart_prod_dyn(const IntervalVector& x1, const Interval& x2) + { + IntervalVector x(x1.size()+1); + x << x1,x2; + return x; + } + + template + auto cart_prod_static(const IntervalVector_& x1, const Interval& x2) + { + IntervalVector_ x; + x << x1,x2; + return x; + } + + auto cart_prod_dyn(const Interval& x1, const IntervalVector& x2) + { + IntervalVector x(x2.size()+1); + x << x1,x2; + return x; + } + + template + auto cart_prod_static(const Interval& x1, const IntervalVector_& x2) + { + IntervalVector_ x; + x << x1,x2; + return x; + } + + auto cart_prod_dyn(const IntervalVector& x1, const IntervalVector& x2) + { + IntervalVector x(x1.size()+x2.size()); + x << x1,x2; + return x; + } + + template + auto cart_prod_static(const IntervalVector_& x1, const IntervalVector_& x2) + { + IntervalVector_ x; + x << x1,x2; + return x; + } + + template + IntervalVector_ cart_prod(const T1& x1, const T2& x2, const Args&... xi) // recursive variadic function + { + auto x_ = cart_prod_static(x1, x2); + if constexpr(sizeof...(xi) > 0) + return cart_prod(x_, xi...); + else + return x_; + } + + template + IntervalVector cart_prod(const T1& x1, const T2& x2, const Args&... xi) // recursive variadic function + { + IntervalVector x_ = cart_prod_dyn(IntervalVector(x1), IntervalVector(x2)); + if constexpr(sizeof...(xi) > 0) + return cart_prod(x_, xi...); + else + return x_; + } + +} // namespace codac + +#endif \ No newline at end of file diff --git a/src/core/2/variables/codac2_Matrix.h b/src/core/2/variables/codac2_Matrix.h index c2a2ccb18..65bc70114 100644 --- a/src/core/2/variables/codac2_Matrix.h +++ b/src/core/2/variables/codac2_Matrix.h @@ -34,8 +34,8 @@ namespace codac2 Matrix_(size_t nb_rows, size_t nb_cols) : Eigen::Matrix(nb_rows, nb_cols) { - assert(R == Dynamic || R == nb_rows); - assert(C == Dynamic || C == nb_cols); + assert(R == Dynamic || R == (int)nb_rows); + assert(C == Dynamic || C == (int)nb_cols); } template diff --git a/src/core/2/variables/codac2_Vector.h b/src/core/2/variables/codac2_Vector.h index 61b273cd0..271efc37f 100644 --- a/src/core/2/variables/codac2_Vector.h +++ b/src/core/2/variables/codac2_Vector.h @@ -32,12 +32,12 @@ namespace codac2 Vector_(size_t n) : Matrix_(n,1) { - assert(N == Dynamic || N == n); + assert(N == Dynamic || N == (int)n); } Vector_(std::initializer_list l) : Matrix_(l.size(),1) { - assert(N == l.size() || N == -1); + assert(N == (int)l.size() || N == -1); size_t i = 0; for(double li : l) (*this)(i++,0) = li; @@ -99,7 +99,7 @@ namespace codac2 : Vector_<>(n) { } - Vector(const Vector_<>& x) + Vector(const Vector& x) : Vector_<>(x) { } @@ -108,11 +108,15 @@ namespace codac2 { } template - explicit Vector(const Vector_& v) + Vector(const Vector_& v) : Vector_<>(v) - { - - } + { } + + template + Vector(const Matrix_& v) + : Vector_<>(v) + { } + }; } // namespace codac diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index fdf90f992..33c832456 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -183,6 +183,7 @@ ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/interval/codac2_Interval.h ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/interval/codac2_IntervalMatrix.h ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/interval/codac2_IntervalVector.h + ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/interval/codac2_cart_prod.h ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/tube/codac2_AbstractConstTube.h ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/tube/codac2_AbstractSlice.cpp ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/tube/codac2_AbstractSlice.h diff --git a/tests/core/CMakeLists.txt b/tests/core/CMakeLists.txt index 868ad7da5..2bedd5190 100644 --- a/tests/core/CMakeLists.txt +++ b/tests/core/CMakeLists.txt @@ -6,35 +6,36 @@ set(TESTS_NAME codac-tests-core) list(APPEND SRC_TESTS ${CMAKE_CURRENT_SOURCE_DIR}/main.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_codac2_tubes.cpp + #${CMAKE_CURRENT_SOURCE_DIR}/tests_codac2_tubes.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/tests_codac2_intervalvector.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_predefined_tubes.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_predefined_tubes.h - ${CMAKE_CURRENT_SOURCE_DIR}/tests_arithmetic.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_cn.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_ctc_box.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_ctc_cart_prod.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_ctc_delay.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_ctc_deriv.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_ctc_chain.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_ctc_eval.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_ctc_picard.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_ctc_lohner.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_ctc_static.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_definition.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_functions.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_integration.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_operators.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_geometry.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_polygons.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_serialization.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_slices_structure.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_trajectory.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_values.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_sep_polygon.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_sep_qinterprojf.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_sep_fixpoint_proj.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_sep_polar.cpp + #${CMAKE_CURRENT_SOURCE_DIR}/tests_predefined_tubes.cpp + #${CMAKE_CURRENT_SOURCE_DIR}/tests_predefined_tubes.h + #${CMAKE_CURRENT_SOURCE_DIR}/tests_arithmetic.cpp + #${CMAKE_CURRENT_SOURCE_DIR}/tests_cn.cpp + #${CMAKE_CURRENT_SOURCE_DIR}/tests_ctc_box.cpp + #${CMAKE_CURRENT_SOURCE_DIR}/tests_ctc_cart_prod.cpp + #${CMAKE_CURRENT_SOURCE_DIR}/tests_ctc_delay.cpp + #${CMAKE_CURRENT_SOURCE_DIR}/tests_ctc_deriv.cpp + #${CMAKE_CURRENT_SOURCE_DIR}/tests_ctc_chain.cpp + #${CMAKE_CURRENT_SOURCE_DIR}/tests_ctc_eval.cpp + #${CMAKE_CURRENT_SOURCE_DIR}/tests_ctc_picard.cpp + #${CMAKE_CURRENT_SOURCE_DIR}/tests_ctc_lohner.cpp + #${CMAKE_CURRENT_SOURCE_DIR}/tests_ctc_static.cpp + #${CMAKE_CURRENT_SOURCE_DIR}/tests_definition.cpp + #${CMAKE_CURRENT_SOURCE_DIR}/tests_functions.cpp + #${CMAKE_CURRENT_SOURCE_DIR}/tests_integration.cpp + #${CMAKE_CURRENT_SOURCE_DIR}/tests_operators.cpp + #${CMAKE_CURRENT_SOURCE_DIR}/tests_geometry.cpp + #${CMAKE_CURRENT_SOURCE_DIR}/tests_polygons.cpp + #${CMAKE_CURRENT_SOURCE_DIR}/tests_serialization.cpp + #${CMAKE_CURRENT_SOURCE_DIR}/tests_slices_structure.cpp + #${CMAKE_CURRENT_SOURCE_DIR}/tests_trajectory.cpp + #${CMAKE_CURRENT_SOURCE_DIR}/tests_values.cpp + #${CMAKE_CURRENT_SOURCE_DIR}/tests_sep_polygon.cpp + #${CMAKE_CURRENT_SOURCE_DIR}/tests_sep_qinterprojf.cpp + #${CMAKE_CURRENT_SOURCE_DIR}/tests_sep_fixpoint_proj.cpp + #${CMAKE_CURRENT_SOURCE_DIR}/tests_sep_polar.cpp ) add_executable(${TESTS_NAME} ${SRC_TESTS}) diff --git a/tests/core/tests_codac2_intervalvector.cpp b/tests/core/tests_codac2_intervalvector.cpp new file mode 100644 index 000000000..7b90cefe3 --- /dev/null +++ b/tests/core/tests_codac2_intervalvector.cpp @@ -0,0 +1,1166 @@ +#include "catch_interval.hpp" +#include "vibes.h" + +#include "codac2_Vector.h" +#include "codac2_IntervalVector.h" +#include "codac2_cart_prod.h" + +using namespace Catch; +using namespace Detail; +using namespace std; +using namespace codac2; + + +TEST_CASE("Test codac2::IntervalVector") +{ + // These tests come from the IBEX library (G. Chabert) + + SECTION("cons01") + { + IntervalVector x(2); + x[0]=Interval::all_reals(); + x[1]=Interval::all_reals(); + CHECK(x==IntervalVector(2)); + CHECK(x==IntervalVector(x)); + IntervalVector y(2); + y = x; + CHECK(x==y); + } + + SECTION("cons02") + { + IntervalVector x(2); + x[0]=Interval(0,1); + x[1]=Interval(0,1); + CHECK(x==IntervalVector(2,Interval(0,1))); + CHECK(x==IntervalVector(x)); + IntervalVector y(2); + y = x; + CHECK(x==y); + } + + SECTION("cons03") + { + IntervalVector x(2); + x[0]=Interval(0,1); + x[1]=Interval(2,3); + CHECK(x==IntervalVector(x)); + IntervalVector y(2); + y = x; + CHECK(x==y); + } + + SECTION("cons04") + { + double bounds[][2] = {{0,1},{2,3}}; + IntervalVector x(2); + x[0]=Interval(0,1); + x[1]=Interval(2,3); + CHECK(x==IntervalVector(2,bounds)); + IntervalVector y(2,bounds); + y = x; + CHECK(x==y); + } + + SECTION("cons05") + { + IntervalVector x(2); + x[0].set_empty(); + x[1].set_empty(); + CHECK(x==IntervalVector::empty_set(2)); + CHECK(x.is_empty()); + } + + SECTION("consInitList") + { + IntervalVector x{ + {1.0, 2.0}, + {2.0, 3.0}, + {4} + }; + CHECK(x.size() == 3); + CHECK(x[0] == Interval(1.0, 2.0)); + CHECK(x[1] == Interval(2.0, 3.0)); + CHECK(x[2] == Interval(4.0, 4.0)); + } + + SECTION("set_empty01") + { + IntervalVector x(2); + CHECK(!x.is_empty()); + x.set_empty(); + CHECK(x.is_empty()); + } + + SECTION("is_empty01") + { + CHECK(IntervalVector::empty_set(2).is_empty()); + } + + SECTION("is_empty02") + { + CHECK(!IntervalVector(2).is_empty()); + } + + SECTION("resize01") + { + IntervalVector x(1); + x[0]=Interval(1,2); + x.resize(3); + CHECK(x.size()==3); + CHECK(x[0]==Interval(1,2)); + CHECK(x[1]==Interval::all_reals()); + CHECK(x[2]==Interval::all_reals()); + } + + SECTION("resize02") + { + IntervalVector x(1); + x[0]=Interval(1,2); + x.resize(1); + CHECK(x.size()==1); + CHECK(x[0]==Interval(1,2)); + } + + SECTION("resize03") + { + IntervalVector x(2); + x[0]=Interval(1,2); + x.set_empty(); + x.resize(3); + CHECK(x.size()==3); + CHECK(x.is_empty()); + CHECK(x[2]==Interval::all_reals()); + } + + SECTION("resize04") + { + IntervalVector x(5); + x[0]=Interval(1,2); + x[1]=Interval(3,4); + x.resize(2); + CHECK(x.size()==2); + CHECK(x[0]==Interval(1,2)); + CHECK(x[1]==Interval(3,4)); + } + + static double _x[][2]={{0,1},{2,3},{4,5}}; + + SECTION("subvector01") + { + double _x01[][2]={{0,1},{2,3}}; + CHECK(IntervalVector(3,_x).subvector(0,1)==IntervalVector(2,_x01)); + } + + SECTION("subvector02") + { + double _x12[][2]={{2,3},{4,5}}; + CHECK(IntervalVector(3,_x).subvector(1,2)==IntervalVector(2,_x12)); + } + + SECTION("subvector03") + { + double _x11[][2]={{2,3}}; + CHECK(IntervalVector(3,_x).subvector(1,1)==IntervalVector(1,_x11)); + } + + SECTION("subvector04") + { + double _x22[][2]={{4,5}}; + CHECK(IntervalVector(3,_x).subvector(2,2)==IntervalVector(1,_x22)); + } + + SECTION("subvector05") + { + CHECK(IntervalVector(3,_x).subvector(0,2)==IntervalVector(3,_x)); + } + + SECTION("subvector06") + { + CHECK(IntervalVector::empty_set(3).subvector(1,2).is_empty()); + } + + SECTION("cart_prod01") + { + CHECK(codac2::cart_prod(IntervalVector(3,_x),IntervalVector::empty_set(3)).is_empty()); + CHECK(codac2::cart_prod(IntervalVector::empty_set(3),IntervalVector(3,_x)).is_empty()); + CHECK(codac2::cart_prod(Interval(1,2),Interval(2,3),IntervalVector({{5,6},{8,9}}),Interval(5,6)) == IntervalVector({{1,2},{2,3},{5,6},{8,9},{5,6}})); + } + + SECTION("inter01") + { + double _x1[][2]={{0,2},{4,6}}; + double _x2[][2]={{1,3},{5,7}}; + double _res[][2]={{1,2},{5,6}}; + CHECK(((IntervalVector(2,_x1)) &=IntervalVector(2,_x2))==IntervalVector(2,_res)); + CHECK(((IntervalVector(2,_x1)) & IntervalVector(2,_x2))==IntervalVector(2,_res)); + } + + SECTION("staticcartprod01") + { + IntervalVector_<2> x{{1,2},{3,5}}; + IntervalVector_<3> y{{1,2},{3,5},{-oo,oo}}; + Interval z{6,7}; + CHECK(cart_prod<6>(x,y,z)==IntervalVector({{1,2},{3,5},{1,2},{3,5},{-oo,oo},{6,7}})); + CHECK(cart_prod<2>(z,z)==IntervalVector({{6,7},{6,7}})); + } + + SECTION("inter02") + { + double _x1[][2]={{0,2},{4,6}}; + double _x2[][2]={{1,3},{7,8}}; + CHECK(((IntervalVector(2,_x1)) &=IntervalVector(2,_x2)).is_empty()); + CHECK(((IntervalVector(2,_x1)) & IntervalVector(2,_x2)).is_empty()); + } + + SECTION("inter03") + { + double _x1[][2]={{0,2},{4,6}}; + CHECK(((IntervalVector(2,_x1)) &=IntervalVector::empty_set(2)).is_empty()); + CHECK(((IntervalVector(2,_x1)) & IntervalVector::empty_set(2)).is_empty()); + } + + SECTION("inter04") + { + double _x1[][2]={{0,2},{4,6}}; + CHECK(((IntervalVector::empty_set(2)) &=IntervalVector(2,_x1)).is_empty()); + CHECK(((IntervalVector::empty_set(2)) & IntervalVector(2,_x1)).is_empty()); + } + + SECTION("hull01") + { + double _x1[][2]={{0,1},{4,5}}; + double _x2[][2]={{2,3},{6,7}}; + double _res[][2]={{0,3},{4,7}}; + CHECK(((IntervalVector(2,_x1)) |=IntervalVector(2,_x2))==IntervalVector(2,_res)); + CHECK(((IntervalVector(2,_x1)) | IntervalVector(2,_x2))==IntervalVector(2,_res)); + } + + SECTION("hull02") + { + double _x1[][2]={{0,1},{4,5}}; + IntervalVector x1(2,_x1); + CHECK((x1 |= x1)==x1); + CHECK((x1 | x1)==x1); + } + + SECTION("hull03") + { + double _x1[][2]={{0,2},{4,6}}; + IntervalVector x1(2,_x1); + CHECK((x1 |=IntervalVector::empty_set(2))==x1); + CHECK((x1 | IntervalVector::empty_set(2))==x1); + } + + SECTION("hull04") + { + double _x1[][2]={{0,2},{4,6}}; + IntervalVector x1(2,_x1); + CHECK(((IntervalVector::empty_set(2)) |= x1)==x1); + CHECK(((IntervalVector::empty_set(2)) | x1)==x1); + } + + SECTION("eq01") + { + IntervalVector x(3,_x); + CHECK(x==x); + CHECK(!(x!=x)); + } + + SECTION("eq02") + { + IntervalVector x(3,_x); + double _x01[][2]={{0,1},{2,3}}; + IntervalVector x1(2,_x01); + CHECK(!(x==x1)); + CHECK(x!=x1); + } + + SECTION("eq03") + { + double _x1[][2]={{0,1},{4,5}}; + double _x2[][2]={{2,3},{6,7}}; + IntervalVector x1(2,_x1); + IntervalVector x2(2,_x2); + x1.set_empty(); + x2.set_empty(); + CHECK(x1==x2); + CHECK(!(x1!=x2)); + } + + SECTION("eq04") + { + CHECK(IntervalVector::empty_set(2)==IntervalVector::empty_set(2)); + CHECK(IntervalVector::empty_set(2)!=IntervalVector::empty_set(3)); + IntervalVector x(2); + x.set_empty(); + CHECK(IntervalVector::empty_set(2)==x); + } + + SECTION("mid01") + { + IntervalVector x(3,_x); + Vector m=x.mid(); + CHECK(m[0]==0.5); + CHECK(m[1]==2.5); + CHECK(m[2]==4.5); + } + + SECTION("is_flat01") + { + CHECK(!IntervalVector(3,_x).is_flat()); + } + + SECTION("is_flat02") + { + CHECK(IntervalVector::empty_set(3).is_flat()); + } + + SECTION("is_flat03") + { + CHECK(IntervalVector(1,Interval(0,0)).is_flat()); + CHECK(!IntervalVector(1,Interval(0,1)).is_flat()); + } + + SECTION("is_flat04") + { + double _x1[][2]={{0,1},{2,2},{3,4}}; + CHECK(IntervalVector(3,_x1).is_flat()); + } + + SECTION("is_flat05") + { + double _x1[][2]={{0,1},{2,3},{4,4}}; + CHECK(IntervalVector(3,_x1).is_flat()); + } + + SECTION("is_unbounded01") + { + CHECK(!IntervalVector::empty_set(3).is_unbounded()); + } + + SECTION("is_unbounded02") + { + double _x1[][2]={{0,1},{0,2},{NEG_INFINITY,0}}; + CHECK(IntervalVector(3,_x1).is_unbounded()); + } + + SECTION("is_unbounded03") + { + double _x1[][2]={{0,1},{0,2}}; + CHECK(!IntervalVector(2,_x1).is_unbounded()); + } + + SECTION("is_unbounded04") + { + CHECK(IntervalVector(1).is_unbounded()); + } + + SECTION("is_subset01") + { + double _x1[][2]={{0,2},{2,4}}; + double _x2[][2]={{0,1},{3,4}}; + IntervalVector x1(2,_x1); + IntervalVector x2(2,_x2); + + CHECK(x1.is_superset(x2)); + CHECK(x2.is_subset(x1)); + CHECK(x1.is_strict_superset(x2)); + //CHECK(!x2.is_strict_interior_subset(x1)); + } + + SECTION("is_subset02") + { + double _x1[][2]={{0,2},{2,4}}; + double _x2[][2]={{1,1},{3,4}}; + IntervalVector x1(2,_x1); + IntervalVector x2(2,_x2); + + CHECK(x1.is_superset(x2)); + CHECK(x2.is_subset(x1)); + CHECK(x1.is_strict_superset(x2)); + //CHECK(!x2.is_strict_interior_subset(x1)); + } + + SECTION("is_subset03") + { + double _x1[][2]={{0,2},{2,4}}; + double _x2[][2]={{0,1},{3,3}}; + IntervalVector x1(2,_x1); + IntervalVector x2(2,_x2); + + CHECK(x1.is_superset(x2)); + CHECK(x2.is_subset(x1)); + CHECK(x1.is_strict_superset(x2)); + //CHECK(!x2.is_strict_interior_subset(x1)); + } + + SECTION("is_subset04") + { + double _x1[][2]={{0,2},{2,4}}; + double _x2[][2]={{1,1},{3,3}}; + IntervalVector x1(2,_x1); + IntervalVector x2(2,_x2); + + CHECK(x1.is_superset(x2)); + CHECK(x2.is_subset(x1)); + CHECK(x1.is_strict_superset(x2)); + //CHECK(x2.is_strict_interior_subset(x1)); + } + + SECTION("is_subset05") + { + double _x1[][2]={{0,2},{2,4}}; + IntervalVector x1(2,_x1); + IntervalVector x2(IntervalVector::empty_set(2)); + + CHECK(x1.is_superset(x2)); + CHECK(x2.is_subset(x1)); + CHECK(x1.is_strict_superset(x2)); + //CHECK(x2.is_strict_interior_subset(x1)); + } + + SECTION("is_subset06") + { + double _x2[][2]={{1,1},{3,3}}; + + IntervalVector x1(IntervalVector::empty_set(2)); + IntervalVector x2(2,_x2); + + CHECK(!x1.is_superset(x2)); + CHECK(!x2.is_subset(x1)); + CHECK(!x1.is_strict_superset(x2)); + //CHECK(!x2.is_strict_interior_subset(x1)); + } + + SECTION("is_subset07") + { + double _x1[][2]={{0,2},{2,4}}; + double _x2[][2]={{1,1},{3,5}}; + + IntervalVector x1(2,_x1); + IntervalVector x2(2,_x2); + + CHECK(!x1.is_superset(x2)); + CHECK(!x2.is_subset(x1)); + CHECK(!x1.is_strict_superset(x2)); + //CHECK(!x2.is_strict_interior_subset(x1)); + } + + SECTION("extr_diam_index01") + { + double _x1[][2]={{0,2},{0,1},{0,3}}; + IntervalVector x1(3,_x1); + CHECK(x1.extr_diam_index(true)==1); + CHECK(x1.extr_diam_index(false)==2); + CHECK(x1.min_diam()==1); + CHECK(x1.max_diam()==3); + } + + SECTION("extr_diam_index02") + { + double _x1[][2]={{0,1},{0,3},{0,2}}; + IntervalVector x1(3,_x1); + CHECK(x1.extr_diam_index(true)==0); + CHECK(x1.extr_diam_index(false)==1); + CHECK(x1.min_diam()==1); + CHECK(x1.max_diam()==3); + } + + SECTION("extr_diam_index03") + { + double _x1[][2]={{0,3},{0,2},{0,1}}; + IntervalVector x1(3,_x1); + CHECK(x1.extr_diam_index(true)==2); + CHECK(x1.extr_diam_index(false)==0); + CHECK(x1.min_diam()==1); + CHECK(x1.max_diam()==3); + } + + SECTION("extr_diam_index04") + { + double _x1[][2]={{0,1},{0,2},{NEG_INFINITY,0}}; + IntervalVector x1(3,_x1); + CHECK(x1.extr_diam_index(true)==0); + CHECK(x1.extr_diam_index(false)==2); + CHECK(x1.min_diam()==1); + CHECK(x1.max_diam()==POS_INFINITY); + } + + SECTION("extr_diam_index05") + { + double _x1[][2]={{NEG_INFINITY,0}}; + IntervalVector x1(1,_x1); + CHECK(x1.extr_diam_index(true)==0); + CHECK(x1.extr_diam_index(false)==0); + CHECK(x1.min_diam()==POS_INFINITY); + CHECK(x1.max_diam()==POS_INFINITY); + } + + SECTION("extr_diam_index06") + { + double _x1[][2]={{NEG_INFINITY,0},{0,1},{NEG_INFINITY,1},{1,3}}; + IntervalVector x1(4,_x1); + CHECK(x1.extr_diam_index(true)==1); + CHECK(x1.extr_diam_index(false)==2); + CHECK(x1.min_diam()==1); + CHECK(x1.max_diam()==POS_INFINITY); + } + + SECTION("extr_diam_index07") + { + double _x1[][2]={{NEG_INFINITY,0},{-2,POS_INFINITY},{NEG_INFINITY,1}}; + IntervalVector x1(3,_x1); + CHECK(x1.extr_diam_index(true)==0); + CHECK(x1.extr_diam_index(false)==1); + CHECK(x1.min_diam()==POS_INFINITY); + CHECK(x1.max_diam()==POS_INFINITY); + } + + SECTION("extr_diam_index08") + { + double _x1[][2]={{NEG_INFINITY,0},{NEG_INFINITY,1},{-2,POS_INFINITY}}; + IntervalVector x1(3,_x1); + CHECK(x1.extr_diam_index(true)==0); + CHECK(x1.extr_diam_index(false)==2); + CHECK(x1.min_diam()==POS_INFINITY); + CHECK(x1.max_diam()==POS_INFINITY); + } + + SECTION("extr_diam_index09") + { + double _x1[][2]={{-2,POS_INFINITY},{NEG_INFINITY,0},{NEG_INFINITY,1}}; + IntervalVector x1(3,_x1); + CHECK(x1.extr_diam_index(true)==1); + CHECK(x1.extr_diam_index(false)==0); + CHECK(x1.min_diam()==POS_INFINITY); + CHECK(x1.max_diam()==POS_INFINITY); + } + + SECTION("extr_diam_index10") + { + double _x1[][2]={{-2,POS_INFINITY},{NEG_INFINITY,1},{NEG_INFINITY,0}}; + IntervalVector x1(3,_x1); + CHECK(x1.extr_diam_index(true)==2); + CHECK(x1.extr_diam_index(false)==0); + CHECK(x1.min_diam()==POS_INFINITY); + CHECK(x1.max_diam()==POS_INFINITY); + } + + SECTION("volume01") + { + double _x1[][2]={{0,1},{0,POS_INFINITY}}; + CHECK(IntervalVector(2,_x1).volume()==POS_INFINITY); + } + + SECTION("volume02") + { + double _x1[][2]={{0,1},{1,1}}; + CHECK(IntervalVector(2,_x1).volume()==0); + } + + SECTION("volume03") + { + double _x1[][2]={{0,2},{2,5},{4,8}}; + CHECK(Approx(24.0)==IntervalVector(3,_x1).volume()); + } + + SECTION("minus01") + { + double _x1[][2]={{0,3},{0,2},{0,1}}; + double _x2[][2]={{-3,0},{-2,0},{-1,0}}; + CHECK((-IntervalVector(3,_x1))==IntervalVector(3,_x2)); + } + + SECTION("minus02") + { + double _x1[][2]={{0,1},{0,POS_INFINITY}}; + double _x2[][2]={{-1,0},{NEG_INFINITY,0}}; + CHECK(-IntervalVector(2,_x1)==IntervalVector(2,_x2)); + } + + SECTION("minus03") + { + CHECK(-IntervalVector::empty_set(2)==IntervalVector::empty_set(2)); + } + + SECTION("add01") + { + double _x1[][2]={{0,3},{0,2},{0,1}}; + double _x2[][2]={{0,1},{0,1},{0,1}}; + double _x3[][2]={{0,4},{0,3},{0,2}}; + + IntervalVector x1(3,_x1); + IntervalVector x2(3,_x2); + IntervalVector x3(3,_x3); + IntervalVector e(IntervalVector::empty_set(3)); + + CHECK(x1+x2==x3); + //CHECK(x1+e,e); + CHECK((x1+e).is_empty()); + //CHECK(IntervalVector(x1)+=e,e); + CHECK((IntervalVector(x1)+=e).is_empty()); + + //CHECK(e+x1,e); + CHECK((e+x1).is_empty()); + //CHECK(e+=x1,e); + CHECK((e+=x1).is_empty()); + //CHECK(e+e,e); + CHECK((e+e).is_empty()); + //CHECK(e+=e,e); + CHECK((e+=e).is_empty()); + + CHECK((IntervalVector(x1)+=x2)==x3); + //CHECK(IntervalVector(x1)+=e,e); + CHECK((IntervalVector(x1)+=e).is_empty()); + + CHECK((IntervalVector(x2)+=x1)==x3); + } + + SECTION("sub01") + { + double _x1[][2]={{0,3},{0,2},{0,1}}; + double _x2[][2]={{0,1},{0,1},{0,1}}; + double _x3[][2]={{-1,3},{-1,2},{-1,1}}; + IntervalVector x1(3,_x1); + IntervalVector x2(3,_x2); + IntervalVector x3(3,_x3); + IntervalVector e(IntervalVector::empty_set(3)); + + CHECK(x1-x2==x3); + CHECK(x2-x1==-x3); + //CHECK(x1-e,e); + CHECK((x1-e).is_empty()); + //CHECK(IntervalVector(x1)-=e,e); + CHECK((IntervalVector(x1)-=e).is_empty()); + + //CHECK(e-x1,e); + CHECK((e-x1).is_empty()); + //CHECK(e-=x1,e); + CHECK((e-=x1).is_empty()); + //CHECK(e-e,e); + CHECK((e-e).is_empty()); + //CHECK(e-=e,e); + CHECK((e-=e).is_empty()); + + CHECK((IntervalVector(x1)-=x2)==x3); + CHECK((IntervalVector(x2)-=x1)==-x3); + } +} + +#if 0 + + + +void TestIntervalVector::compl01() { + double _b[][2]={{0,1},{0,1}}; + IntervalVector b(2,_b); + IntervalVector* c; + int n=b.complementary(c); + + CHECK(n==4); + CHECK(c[0].size()==2); + + CHECK(c[0][0]==Interval::neg_reals()); + CHECK(c[0][1]==Interval::all_reals()); + + CHECK(c[1][0]==Interval(1,POS_INFINITY)); + CHECK(c[1][1]==Interval::all_reals()); + + CHECK(c[2][0]==Interval(0,1)); + CHECK(c[2][1]==Interval::neg_reals()); + + CHECK(c[3][0]==Interval(0,1)); + CHECK(c[3][1]==Interval(1,POS_INFINITY)); + + delete[] c; +} + +/** + * complementary of an empty box = (-oo,oo)x...(-oo,oo) + */ +void TestIntervalVector::compl02() { + + IntervalVector* c; + int n=IntervalVector::empty_set(2).complementary(c); + CHECK(n==1); + CHECK(c[0].size()==2); + + CHECK(c[0][0]==Interval::all_reals()); + CHECK(c[0][1]==Interval::all_reals()); + + delete[] c; +} + +bool TestIntervalVector::test_diff(int n, double _x[][2], double _y[][2], int m, double _z[][2], bool compactness, bool debug) { + IntervalVector x(n,_x); + IntervalVector y(n,_y); + IntervalMatrix mz(m,n,_z); + IntervalVector* c; + int nn=x.diff(y,c,compactness); + if (debug) { + cout << x << " diff " << y << " gives:" << endl; + for (int i=0; i& expected, bool compactness = true) +{ + auto c = x.diff(y, compactness); - CHECK(c[0][0]==Interval::all_reals()); - CHECK(c[0][1]==Interval::all_reals()); + CHECK(!c.empty()); + CHECK(c.size()==expected.size()); + CHECK(c.front().size()==x.size()); - delete[] c; -} + auto it = c.begin(); + while(it != c.end()) + { + bool is_same = false; + for(const auto& ri : expected) + if(ri == *it) + { + is_same = true; + break; + } -bool TestIntervalVector::test_diff(int n, double _x[][2], double _y[][2], int m, double _z[][2], bool compactness, bool debug) { - IntervalVector x(n,_x); - IntervalVector y(n,_y); - IntervalMatrix mz(m,n,_z); - IntervalVector* c; - int nn=x.diff(y,c,compactness); - if (debug) { - cout << x << " diff " << y << " gives:" << endl; - for (int i=0; i Date: Mon, 11 Sep 2023 15:57:14 +0200 Subject: [PATCH 119/256] [core] testing IntervalMatrix --- .../domains/interval/codac2_IntervalMatrix.h | 78 ++- .../domains/interval/codac2_IntervalVector.h | 31 +- tests/core/CMakeLists.txt | 1 + tests/core/tests_codac2_intervalmatrix.cpp | 483 ++++++++++++++++++ 4 files changed, 565 insertions(+), 28 deletions(-) create mode 100644 tests/core/tests_codac2_intervalmatrix.cpp diff --git a/src/core/2/domains/interval/codac2_IntervalMatrix.h b/src/core/2/domains/interval/codac2_IntervalMatrix.h index 559b64d79..e74721fb4 100644 --- a/src/core/2/domains/interval/codac2_IntervalMatrix.h +++ b/src/core/2/domains/interval/codac2_IntervalMatrix.h @@ -51,6 +51,42 @@ namespace codac2 for(size_t i = 0 ; i < size() ; i++) *(this->data()+i) = x; } + + explicit IntervalMatrix_(size_t nb_rows, size_t nb_cols, double bounds[][2]) + : IntervalMatrix_(nb_rows, nb_cols) + { + size_t k = 0; + for(size_t i = 0 ; i < nb_rows ; i++) + for(size_t j = 0 ; j < nb_cols ; j++) + { + if(bounds == 0) // in case the user called IntervalVector(n,0) and 0 is interpreted as NULL! + (*this)(i,j) = Interval::zero(); + else + (*this)(i,j) = Interval(bounds[k][0],bounds[k][1]); + k++; + } + } + + IntervalMatrix_(std::initializer_list> l) + : IntervalMatrix_() + { + assert(R == Dynamic || (int)l.size() == R); + int cols = -1; + for(const auto& ri : l) { + assert(cols == -1 || cols == (int)ri.size()); + cols = (int)ri.size(); + } + this->resize(l.size(),cols); + size_t i = 0; + for(const auto& ri : l) + { + size_t j = 0; + for(const auto& ci : ri) + (*this)(i,j++) = ci; + i++; + } + // todo: use thias as faster? std::copy(l.begin(), l.end(), vec); + } // This constructor allows you to construct IntervalMatrix_ from Eigen expressions template @@ -71,6 +107,16 @@ namespace codac2 return this->Eigen::Matrix::size(); } + void resize(size_t nb_rows, size_t nb_cols) + { + // With resize of Eigen, the data is reallocated and all previous values are lost. + auto copy = *this; + this->Eigen::Matrix::resize(nb_rows, nb_cols); + for(size_t i = 0 ; i < min(copy.rows(),nb_rows) ; i++) + for(size_t j = 0 ; j < min(copy.cols(),nb_cols) ; j++) + (*this)(i,j) = copy(i,j); + } + bool is_empty() const { for(size_t i = 0 ; i < size() ; i++) @@ -79,6 +125,12 @@ namespace codac2 return false; } + static IntervalMatrix_ empty_set(size_t nb_rows = R, size_t nb_cols = C) + { + IntervalMatrix_ x(nb_rows, nb_cols, Interval::empty_set()); + return x; + } + bool is_flat() const { if(is_empty()) return true; @@ -369,12 +421,6 @@ namespace codac2 init(Interval::empty_set()); } - static IntervalMatrix_ empty_set(size_t nb_rows = R, size_t nb_cols = C) - { - IntervalMatrix_ x(nb_rows, nb_cols, Interval::empty_set()); - return x; - } - auto& inflate(double r) { assert(r >= 0.); @@ -385,7 +431,7 @@ namespace codac2 bool operator==(const IntervalMatrix_& x) const { - if(x.size() != this->size()) + if(x.size() != this->size() || x.rows() != this->rows() || x.cols() != this->cols()) return false; if(is_empty() || x.is_empty()) return is_empty() && x.is_empty(); @@ -491,15 +537,31 @@ namespace codac2 : IntervalMatrix_<>(nb_rows, nb_cols) { } + + explicit IntervalMatrix(size_t nb_rows, size_t nb_cols, const Interval& x) + : IntervalMatrix_<>(nb_rows, nb_cols, x) + { } + + explicit IntervalMatrix(size_t nb_rows, size_t nb_cols, double bounds[][2]) + : IntervalMatrix_<>(nb_rows, nb_cols, bounds) + { } + IntervalMatrix(const IntervalMatrix_<>& x) : IntervalMatrix_<>(x) { } + IntervalMatrix(std::initializer_list> l) + : IntervalMatrix_<>(l) + { } + template explicit IntervalMatrix(const Matrix_& v) : IntervalMatrix_<>(v) - { + { } + static IntervalMatrix empty_set(size_t nb_rows, size_t nb_cols) + { + return IntervalMatrix_<>::empty_set(nb_rows,nb_cols); } }; diff --git a/src/core/2/domains/interval/codac2_IntervalVector.h b/src/core/2/domains/interval/codac2_IntervalVector.h index 072ac383e..40a1e8cce 100644 --- a/src/core/2/domains/interval/codac2_IntervalVector.h +++ b/src/core/2/domains/interval/codac2_IntervalVector.h @@ -68,17 +68,9 @@ namespace codac2 } explicit IntervalVector_(size_t n, double bounds[][2]) - : IntervalMatrix_(n,1) - { - for(size_t i = 0 ; i < n ; i++) - { - if(bounds == 0) // in case the user called IntervalVector(n,0) and 0 is interpreted as NULL! - (*this)[i] = Interval::zero(); - else - (*this)[i] = Interval(bounds[i][0],bounds[i][1]); - } - } - + : IntervalMatrix_(n,1,bounds) + { } + explicit IntervalVector_(double bounds[][2]) : IntervalVector_(this->size(), bounds) { @@ -118,9 +110,12 @@ namespace codac2 static IntervalVector_ empty_set(size_t n = N) { - IntervalVector_ x(n); - x.set_empty(); - return x; + return IntervalMatrix_::empty_set(n,1); + } + + void resize(size_t n) + { + this->IntervalMatrix_::resize(n,1); } template @@ -298,16 +293,12 @@ namespace codac2 void resize(size_t n) { - // With resize of Eigen, the data is reallocated and all previous values are lost. - auto save = *this; - IntervalVector_<>::resize(n); - for(size_t i = 0 ; i < min(save.size(),n) ; i++) - (*this)[i] = save[i]; + this->IntervalVector_<>::resize(n); } static IntervalVector empty_set(size_t n) { - return IntervalVector_<>::empty_set(n); + return IntervalMatrix_<>::empty_set(n,1); } }; diff --git a/tests/core/CMakeLists.txt b/tests/core/CMakeLists.txt index 2bedd5190..4cc10e560 100644 --- a/tests/core/CMakeLists.txt +++ b/tests/core/CMakeLists.txt @@ -8,6 +8,7 @@ list(APPEND SRC_TESTS ${CMAKE_CURRENT_SOURCE_DIR}/main.cpp #${CMAKE_CURRENT_SOURCE_DIR}/tests_codac2_tubes.cpp ${CMAKE_CURRENT_SOURCE_DIR}/tests_codac2_intervalvector.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/tests_codac2_intervalmatrix.cpp #${CMAKE_CURRENT_SOURCE_DIR}/tests_predefined_tubes.cpp #${CMAKE_CURRENT_SOURCE_DIR}/tests_predefined_tubes.h diff --git a/tests/core/tests_codac2_intervalmatrix.cpp b/tests/core/tests_codac2_intervalmatrix.cpp new file mode 100644 index 000000000..8b8640a27 --- /dev/null +++ b/tests/core/tests_codac2_intervalmatrix.cpp @@ -0,0 +1,483 @@ +#include "catch_interval.hpp" +#include "vibes.h" + +#include "codac2_Matrix.h" +#include "codac2_IntervalVector.h" +#include "codac2_IntervalMatrix.h" + +using namespace Catch; +using namespace Detail; +using namespace std; +using namespace codac2; + +// Most of these tests come from the IBEX library (G. Chabert) +// They have been revised to fit the codac2::IntervalMatrix class + +IntervalMatrix M1() +{ + IntervalMatrix m(2,3); + double _r1[][2]={{0,1},{0,2},{0,3}}; + double _r2[][2]={{-1,0},{-2,0},{-3,0}}; + IntervalVector r1(3,_r1); + IntervalVector r2(3,_r2); + m.row(0)=r1; + m.row(1)=r2; + return m; +} + + +IntervalMatrix M2() // the transpose of M1 +{ + IntervalMatrix m(3,2); + double _c1[][2]={{0,1},{-1,0}}; + double _c2[][2]={{0,2},{-2,0}}; + double _c3[][2]={{0,3},{-3,0}}; + IntervalVector c1(2,_c1); + IntervalVector c2(2,_c2); + IntervalVector c3(2,_c3); + m.row(0)=c1; + m.row(1)=c2; + m.row(2)=c3; + return m; +} + +IntervalMatrix M3() // non-null intersection with M1 +{ + IntervalMatrix m(2,3); + double _r1[][2]={{1,2},{1,2},{2,4}}; + double _r2[][2]={{-2,-1},{-2,-1},{-4,-2}}; + IntervalVector r1(3,_r1); + IntervalVector r2(3,_r2); + m.row(0)=r1; + m.row(1)=r2; + return m; +} + + +TEST_CASE("Tests from IBEX IntervalMatrix") +{ + SECTION("eq01") + { + IntervalMatrix m(2,3); + IntervalMatrix m2(3,2); + CHECK(m!=m2); + CHECK(!(m==m2)); + } + + SECTION("eq02") + { + IntervalMatrix m(3,2); + IntervalMatrix m2(2,2); + CHECK(m!=m2); + CHECK(!(m==m2)); + } + + SECTION("eq03") + { + IntervalMatrix m(2,3); + IntervalMatrix m2(2,3); + + CHECK(m.rows()==2); + CHECK(m.cols()==3); + CHECK(m2.rows()==2); + CHECK(m2.cols()==3); + + m(0,0)=1; + m(0,1)=2; + m(0,2)=3; + m(1,0)=4; + m(1,1)=5; + m(1,2)=6; + m2(0,0)=1; + m2(0,1)=2; + m2(0,2)=3; + m2(1,0)=4; + m2(1,1)=5; + m2(1,2)=6; + + CHECK(m==m2); + CHECK(!(m!=m2)); + + m2(1,2)=7; + CHECK(m!=m2); + CHECK(!(m==m2)); + } + + SECTION("eq04") + { + IntervalMatrix m(2,3); + IntervalMatrix m2(2,3); + m(1,1)=-1; + m2(1,1)=-2; + CHECK(m!=m2); + CHECK(!(m==m2)); + m.set_empty(); + m2.set_empty(); + CHECK(m==m2); + CHECK(!(m!=m2)); + } + + SECTION("cons01") + { + IntervalMatrix m(2,3); + CHECK(m.rows()==2); + CHECK(m.cols()==3); + CHECK(m(0,0)==Interval::all_reals()); + CHECK(m(0,1)==Interval::all_reals()); + CHECK(m(0,2)==Interval::all_reals()); + CHECK(m(1,0)==Interval::all_reals()); + CHECK(m(1,1)==Interval::all_reals()); + CHECK(m(1,2)==Interval::all_reals()); + CHECK(m==IntervalMatrix(m)); + CHECK(m==(IntervalMatrix(2,3)=m)); + } + + SECTION("cons02") + { + IntervalMatrix m(2,3); + double _r1[][2]={{0,1},{0,2},{0,3}}; + double _r2[][2]={{-1,0},{-2,0},{-3,0}}; + IntervalVector r1(3,_r1); + IntervalVector r2(3,_r2); + m.row(0) = r1; + m.row(1) = r2; + + double _c1[][2]={{0,1},{-1,0}}; + double _c2[][2]={{0,2},{-2,0}}; + double _c3[][2]={{0,3},{-3,0}}; + IntervalVector c1(2,_c1); + IntervalVector c2(2,_c2); + IntervalVector c3(2,_c3); + + CHECK(m.rows()==2); + CHECK(m.cols()==3); + // not supported CHECK(m[0]==r1); + // not supported CHECK(m[1]==r2); +// not working CHECK(m.row(0)==r1); +// not working CHECK(m.row(1)==r2); + CHECK(m.col(0)==c1); + CHECK(m.col(1)==c2); + CHECK(m.col(2)==c3); + CHECK(m(0,0)==Interval(0,1)); + CHECK(m(0,1)==Interval(0,2)); + CHECK(m(0,2)==Interval(0,3)); + CHECK(m(1,0)==Interval(-1,0)); + CHECK(m(1,1)==Interval(-2,0)); + CHECK(m(1,2)==Interval(-3,0)); + CHECK(m==IntervalMatrix(m)); + CHECK(m==(IntervalMatrix(2,3)=m)); + } + + SECTION("cons03") + { + Interval x(-1,2); + IntervalMatrix m(2,3,x); + + CHECK(m.rows()==2); + CHECK(m.cols()==3); + for (int i=0; i<2; i++) { + for (int j=0; j<3; j++) + CHECK(m(i,j)==x); + } + + CHECK(m==IntervalMatrix(m)); + CHECK(m==(IntervalMatrix(2,3)=m)); + } + + SECTION("cons04") + { + double _m[][2]={ {0,1}, {0,2}, {0,3}, + {-1,0},{-2,0},{-3,0} }; + IntervalMatrix m(2,3,_m); + CHECK(m==M1()); + } + + SECTION("consInitList") + { + IntervalMatrix m{ + {{0,1}, {0,2}, {0,3}}, + {{-1,0},{-2,0},{-3,0}} + }; + CHECK(m == M1()); + } + + SECTION("empty01") + { + CHECK(IntervalMatrix::empty_set(2,3).rows()==2); + CHECK(IntervalMatrix::empty_set(2,3).cols()==3); + CHECK(IntervalMatrix(IntervalMatrix::empty_set(2,3))==IntervalMatrix::empty_set(2,3)); + CHECK((IntervalMatrix(2,3)=IntervalMatrix::empty_set(2,3))==IntervalMatrix::empty_set(2,3)); + } + + SECTION("is_empty01") + { + CHECK(!IntervalMatrix(2,3).is_empty()); + } + + SECTION("is_empty02") + { + CHECK(IntervalMatrix::empty_set(2,3).is_empty()); + } + + SECTION("set_empty01") + { + IntervalMatrix m(2,3); + m.set_empty(); + CHECK(m.is_empty()); + } + + // intersection of a matrix with itself + SECTION("inter01") + { + CHECK((M1()&=M1())==M1()); + } + + // intersection of two overlapping matrices + SECTION("inter02") + { + double _m[][2]={{1,1}, {1,2}, {2,3}, + {-1,-1},{-2,-1},{-3,-2}}; + + CHECK((M1()&=M3())==IntervalMatrix(2,3,_m)); + } + + // intersection of two non-overlapping matrices + SECTION("inter03") + { + IntervalMatrix m3(M3()); + m3(1,2)=Interval(-5,-4); + CHECK((M1()&=m3).is_empty()); + } + + SECTION("set_col01") + { + IntervalMatrix m(M1()); + + IntervalVector v(2); + v[0]=Interval(1,2); + v[1]=Interval(-2,-1); + + m.col(1)=v; + + double _m2[][2]={ {0,1}, {1,2}, {0,3}, + {-1,0},{-2,-1},{-3,0} }; + IntervalMatrix m2(2,3,_m2); + + CHECK(m==m2); + } + + SECTION("rows01") + { + CHECK(M1().block(0,0,2,3)==M1()); + } + + SECTION("rows02") + { + double _r0[][2]={ {0,1}, {0,2}, {0,3} }; + CHECK(M1().block(0,0,1,3)==IntervalMatrix(1,3,_r0)); + } + + SECTION("rows03") + { + double _r1[][2]={ {-1,0},{-2,0},{-3,0} }; + CHECK(M1().block(1,0,1,3)==IntervalMatrix(1,3,_r1)); + } + + SECTION("cols01") + { + CHECK(M1().block(0,0,2,3)==M1()); + } + + SECTION("cols02") + { + double _c0[][2]={ {0,1}, {-1,0} }; + CHECK(M1().block(0,0,2,1)==IntervalMatrix(2,1,_c0)); + } + + SECTION("cols03") + { + double _c1[][2]={ {0,2}, {-2,0} }; + CHECK(M1().block(0,1,2,1)==IntervalMatrix(2,1,_c1)); + } + + SECTION("cols04") + { + double _c2[][2]={ {0,3}, {-3,0} }; + CHECK(M1().block(0,2,2,1)==IntervalMatrix(2,1,_c2)); + } + + SECTION("cols04") + { + double _c12[][2]={ {0,2}, {0,3}, {-2,0}, {-3,0} }; + CHECK(M1().block(0,1,2,2)==IntervalMatrix(2,2,_c12)); + } + + SECTION("resize01") + { + IntervalMatrix m(2,2); + double _r1[][2]={{0,1},{0,2}}; + double _r2[][2]={{-1,0},{-2,0}}; + IntervalVector r1(2,_r1); + IntervalVector r2(2,_r2); + m.row(0)=r1; + m.row(1)=r2; + m.resize(2,3); + m(0,2)=Interval(0,3); + m(1,2)=Interval(-3,0); + + CHECK(m==M1()); + } + + SECTION("resize02") + { + IntervalMatrix m(1,3); + double _r1[][2]={{0,1},{0,2},{0,3}}; + IntervalVector r1(3,_r1); + m.row(0)=r1; + m.resize(2,3); + m(1,0)=Interval(-1,0); + m(1,1)=Interval(-2,0); + m(1,2)=Interval(-3,0); + + CHECK(m==M1()); + } + + SECTION("resize03") + { + IntervalMatrix e(IntervalMatrix::empty_set(1,1)); + e.resize(2,3); + CHECK(e.is_empty()); + } + + SECTION("minus01") + { + IntervalMatrix m(M1()); + IntervalMatrix m2(-m); + for (int i=0; i<2; i++) { + for (int j=0; j<3; j++) { + CHECK(m2(i,j)==-m(i,j)); + } + } + } + + SECTION("minus02") + { + CHECK(-IntervalMatrix::empty_set(2,3).is_empty()); + } + + SECTION("add01") + { + IntervalMatrix m(M1()); + IntervalMatrix m2(m+m); + + for (int i=0; i<2; i++) { + for (int j=0; j<3; j++) { + CHECK(m2(i,j)==m(i,j)+m(i,j)); + } + } + + CHECK(m2==(IntervalMatrix(m)+=m)); + } + + SECTION("add02") + { + IntervalMatrix m1(IntervalMatrix::empty_set(2,3)); + IntervalMatrix m2(2,3); + + CHECK((m1+m2).is_empty()); + CHECK((m1+=m2).is_empty()); + CHECK((m2+=m1).is_empty()); + } + + SECTION("sub01") + { + IntervalMatrix m(M1()); + IntervalMatrix m2(m-m); + for (int i=0; i<2; i++) { + for (int j=0; j<3; j++) { + CHECK(m2(i,j)==m(i,j)-m(i,j)); + } + } + + CHECK(m2==(IntervalMatrix(m)-=m)); + } + + SECTION("sub02") + { + IntervalMatrix m1(IntervalMatrix::empty_set(2,3)); + IntervalMatrix m2(2,3); + + CHECK((m1-m2).is_empty()); + CHECK((m1-=m2).is_empty()); + CHECK((m2-=m1).is_empty()); + } + + SECTION("mul01") + { + IntervalMatrix m(M1()); + IntervalMatrix m2(M2()); + IntervalMatrix m3(m*m2); + CHECK(m3.rows()==2); + CHECK(m3.cols()==2); + + for (int i=0; i<2; i++) { + for (int j=0; j<2; j++) + CHECK(m3(i,j)==m(i,0)*m2(0,j)+m(i,1)*m2(1,j)+m(i,2)*m2(2,j)); + } + + CHECK(m3==(IntervalMatrix(m)*=m2)); + } + + SECTION("mul02") + { + IntervalMatrix m1(IntervalMatrix::empty_set(2,3)); + IntervalMatrix m2(3,2); + + CHECK(IntervalMatrix(m1*m2).is_empty()); + CHECK(IntervalMatrix(m1*=m2).is_empty()); + CHECK(IntervalMatrix(m2*=m1).is_empty()); + } + + SECTION("put01") + { + // deprecated in codac (use Eigen instead) IntervalMatrix M1=2*Matrix::eye(3); + // deprecated in codac (use Eigen instead) IntervalVector V1(3); + // deprecated in codac (use Eigen instead) V1[0]=3; V1[1]=4; V1[2]=5; + // deprecated in codac (use Eigen instead) IntervalMatrix res(4,4); + // deprecated in codac (use Eigen instead) res.put(0,0,M1); + // deprecated in codac (use Eigen instead) res.put(0,3,V1,false); + // deprecated in codac (use Eigen instead) res.put(3,0,Vector::ones(3),true); + // deprecated in codac (use Eigen instead) res[3][3]=6; + // deprecated in codac (use Eigen instead) double _expected[16] = { 2,0,0,3, + // deprecated in codac (use Eigen instead) 0,2,0,4, + // deprecated in codac (use Eigen instead) 0,0,2,5, + // deprecated in codac (use Eigen instead) 1,1,1,6 }; + // deprecated in codac (use Eigen instead) CHECK(res==(Matrix(4,4,_expected))); + } +} + +#if 0 + +// Tests from IBEX that are not (yet) considered in Codac: + +void TestIntervalMatrix::rad01() { + RNG::srand(1); + IntervalMatrix M=Matrix::rand(2); + Matrix R=M.rad(); + CHECK(R[0][0]==M[0][0].rad()); + CHECK(R[0][1]==M[0][1].rad()); + CHECK(R[1][0]==M[1][0].rad()); + CHECK(R[1][1]==M[1][1].rad()); +} + +void TestIntervalMatrix::diam01() { + RNG::srand(1); + IntervalMatrix M=Matrix::rand(2); + Matrix R=M.diam(); + CHECK(R[0][0]==M[0][0].diam()); + CHECK(R[0][1]==M[0][1].diam()); + CHECK(R[1][0]==M[1][0].diam()); + CHECK(R[1][1]==M[1][1].diam()); +} + +#endif \ No newline at end of file From ae57262774a488bb47748266be145ed2b4ff4b60 Mon Sep 17 00:00:00 2001 From: SimonRohou Date: Mon, 11 Sep 2023 21:24:54 +0200 Subject: [PATCH 120/256] [core] templated Interval/Vector/Matrix --- .../domains/interval/codac2_IntervalMatrix.h | 6 ++++- src/core/2/domains/paving/codac2_Paving.h | 4 ++-- src/core/2/variables/codac2_Matrix.h | 24 +++++++++++++++++++ src/core/2/variables/codac2_Vector.h | 21 +++++++++++++--- 4 files changed, 49 insertions(+), 6 deletions(-) diff --git a/src/core/2/domains/interval/codac2_IntervalMatrix.h b/src/core/2/domains/interval/codac2_IntervalMatrix.h index e74721fb4..55e288c13 100644 --- a/src/core/2/domains/interval/codac2_IntervalMatrix.h +++ b/src/core/2/domains/interval/codac2_IntervalMatrix.h @@ -59,7 +59,7 @@ namespace codac2 for(size_t i = 0 ; i < nb_rows ; i++) for(size_t j = 0 ; j < nb_cols ; j++) { - if(bounds == 0) // in case the user called IntervalVector(n,0) and 0 is interpreted as NULL! + if(bounds == 0) // in case the user called IntervalMatrix_(r,c,0) and 0 is interpreted as NULL! (*this)(i,j) = Interval::zero(); else (*this)(i,j) = Interval(bounds[k][0],bounds[k][1]); @@ -67,6 +67,10 @@ namespace codac2 } } + explicit IntervalMatrix_(double bounds[][2]) + : IntervalMatrix_(R, C, bounds) + { } + IntervalMatrix_(std::initializer_list> l) : IntervalMatrix_() { diff --git a/src/core/2/domains/paving/codac2_Paving.h b/src/core/2/domains/paving/codac2_Paving.h index 027295a09..932bbe658 100644 --- a/src/core/2/domains/paving/codac2_Paving.h +++ b/src/core/2/domains/paving/codac2_Paving.h @@ -25,13 +25,13 @@ namespace codac2 { public: - Paving(size_t n) + explicit Paving(size_t n) : _x(IntervalVector_(n)) { } - Paving(const IntervalVector_& x) + explicit Paving(const IntervalVector_& x) : _x(x) { diff --git a/src/core/2/variables/codac2_Matrix.h b/src/core/2/variables/codac2_Matrix.h index 7a93622a5..e0203e4b8 100644 --- a/src/core/2/variables/codac2_Matrix.h +++ b/src/core/2/variables/codac2_Matrix.h @@ -38,6 +38,25 @@ namespace codac2 assert(C == Dynamic || C == (int)nb_cols); } + explicit Matrix_(size_t nb_rows, size_t nb_cols, double values[]) + : Matrix_(nb_rows, nb_cols) + { + size_t k = 0; + for(size_t i = 0 ; i < nb_rows ; i++) + for(size_t j = 0 ; j < nb_cols ; j++) + { + if(values == 0) // in case the user called Matrix_(r,c,0) and 0 is interpreted as NULL! + (*this)(i,j) = 0.; + else + (*this)(i,j) = values[k]; + k++; + } + } + + explicit Matrix_(double values[]) + : Matrix_(R, C, values) + { } + template Matrix_(const Eigen::MatrixBase& other) : Eigen::Matrix(other) @@ -92,6 +111,11 @@ namespace codac2 return *this; } + static Matrix_ zeros() + { + return Eigen::Matrix::Zero(); + } + }; } // namespace codac diff --git a/src/core/2/variables/codac2_Vector.h b/src/core/2/variables/codac2_Vector.h index 271efc37f..3538dff50 100644 --- a/src/core/2/variables/codac2_Vector.h +++ b/src/core/2/variables/codac2_Vector.h @@ -49,7 +49,15 @@ namespace codac2 { assert(M == Dynamic || M == N); } - + + explicit Vector_(size_t n, double values[]) + : Matrix_(n,1,values) + { } + + explicit Vector_(double values[]) + : Matrix_(N,1,values) + { } + template Vector_(const Eigen::MatrixBase& other) : Matrix_(other) @@ -70,8 +78,15 @@ namespace codac2 static Vector_ zeros() { - Vector_ v; - return v; + return Eigen::Matrix::Zero(); + } + + // todo: place this in common inheritance with IntervalVector_ + template + Vector_ subvector() const + { + assert(N1 >= 0 && N1 < N && N2 >= 0 && N2 < N && N1 <= N2); + return this->template block(N1,0); } }; From 067f1b9f14cd11758b76f012a351a1abbbb5e4aa Mon Sep 17 00:00:00 2001 From: SimonRohou Date: Thu, 14 Sep 2023 15:22:07 +0200 Subject: [PATCH 121/256] [core] templated Interval/Vector/Matrix --- src/core/2/3rd/codac2_eigen.h | 48 ++++++++++ src/core/2/domains/interval/codac2_Interval.h | 33 ------- .../domains/interval/codac2_IntervalMatrix.h | 51 ++++++++-- .../domains/interval/codac2_IntervalVector.h | 26 ++++-- .../2/domains/interval/codac2_cart_prod.cpp | 50 ++++++++++ .../2/domains/interval/codac2_cart_prod.h | 39 ++------ src/core/2/domains/paving/codac2_Paving.h | 48 +++++++--- src/core/2/variables/codac2_Matrix.h | 93 +++++++++++++++++-- src/core/2/variables/codac2_Vector.h | 24 ++++- src/core/CMakeLists.txt | 3 + 10 files changed, 313 insertions(+), 102 deletions(-) create mode 100644 src/core/2/3rd/codac2_eigen.h create mode 100644 src/core/2/domains/interval/codac2_cart_prod.cpp diff --git a/src/core/2/3rd/codac2_eigen.h b/src/core/2/3rd/codac2_eigen.h new file mode 100644 index 000000000..0f26dbced --- /dev/null +++ b/src/core/2/3rd/codac2_eigen.h @@ -0,0 +1,48 @@ +#ifndef EIGEN_NO_DEBUG +/* Disables Eigen's assertions if defined. + * Not defined by default, unless the NDEBUG macro is defined + * (this is a standard C++ macro which disables all asserts). + * https://eigen.tuxfamily.org/dox/TopicPreprocessorDirectives.html + */ +#define EIGEN_NO_DEBUG +#endif + +#ifndef __CODAC2_EIGEN_H__ +#define __CODAC2_EIGEN_H__ + +#include +#include +#include "codac2_Interval.h" + +namespace Eigen +{ + template<> struct NumTraits + : NumTraits // permits to get the epsilon, dummy_precision, lowest, highest functions + { + typedef codac2::Interval Real; + typedef codac2::Interval NonInteger; + typedef codac2::Interval Nested; + + enum { + IsComplex = 0, + IsInteger = 0, + IsSigned = 1, + RequireInitialization = 1, + ReadCost = 1, + AddCost = 3, + MulCost = 3 + }; + }; +} + +namespace codac2 +{ + inline const Interval& conj(const Interval& x) { return x; } + inline const Interval& real(const Interval& x) { return x; } + inline Interval imag(const Interval&) { return 0.; } + inline Interval abs(const Interval& x) { return ibex::abs(x); } + inline Interval abs2(const Interval& x) { return ibex::sqr(x); } + +} // namespace codac + +#endif \ No newline at end of file diff --git a/src/core/2/domains/interval/codac2_Interval.h b/src/core/2/domains/interval/codac2_Interval.h index 2b417a05a..5e4360577 100644 --- a/src/core/2/domains/interval/codac2_Interval.h +++ b/src/core/2/domains/interval/codac2_Interval.h @@ -13,8 +13,6 @@ #define __CODAC2_INTERVAL_H__ #include -#include -#include namespace codac2 { @@ -24,35 +22,4 @@ namespace codac2 } // namespace codac -namespace Eigen -{ - template<> struct NumTraits - : NumTraits // permits to get the epsilon, dummy_precision, lowest, highest functions - { - typedef codac2::Interval Real; - typedef codac2::Interval NonInteger; - typedef codac2::Interval Nested; - - enum { - IsComplex = 0, - IsInteger = 0, - IsSigned = 1, - RequireInitialization = 1, - ReadCost = 1, - AddCost = 3, - MulCost = 3 - }; - }; -} - -namespace codac2 -{ - inline const Interval& conj(const Interval& x) { return x; } - inline const Interval& real(const Interval& x) { return x; } - inline Interval imag(const Interval&) { return 0.; } - inline Interval abs(const Interval& x) { return ibex::abs(x); } - inline Interval abs2(const Interval& x) { return ibex::sqr(x); } - -} // namespace codac - #endif \ No newline at end of file diff --git a/src/core/2/domains/interval/codac2_IntervalMatrix.h b/src/core/2/domains/interval/codac2_IntervalMatrix.h index 55e288c13..2747e80a1 100644 --- a/src/core/2/domains/interval/codac2_IntervalMatrix.h +++ b/src/core/2/domains/interval/codac2_IntervalMatrix.h @@ -18,8 +18,7 @@ #define __CODAC2_INTERVALMATRIX_H__ #include -#include -#include +#include #include #include @@ -34,9 +33,7 @@ namespace codac2 IntervalMatrix_() : Eigen::Matrix() - { - - } + { } explicit IntervalMatrix_(size_t nb_rows, size_t nb_cols) : Eigen::Matrix(nb_rows, nb_cols) @@ -71,13 +68,28 @@ namespace codac2 : IntervalMatrix_(R, C, bounds) { } + explicit IntervalMatrix_(const Matrix_& lb, const Matrix_& ub) + : IntervalMatrix_(R, C) + { + for(size_t i = 0 ; i < this->rows() ; i++) + for(size_t j = 0 ; j < this->cols() ; j++) + (*this)(i,j) = Interval(lb(i,j),ub(i,j)); + } + + explicit IntervalMatrix_(const IntervalMatrix_& x) + : IntervalMatrix_(R, C) + { + for(size_t i = 0 ; i < size() ; i++) + *(this->data()+i) = *(x.data()+i); + } + IntervalMatrix_(std::initializer_list> l) : IntervalMatrix_() { - assert(R == Dynamic || (int)l.size() == R); + assert((R == Dynamic || (int)l.size() == R) && "ill-formed matrix"); int cols = -1; for(const auto& ri : l) { - assert(cols == -1 || cols == (int)ri.size()); + assert((cols == -1 || cols == (int)ri.size()) && "ill-formed matrix"); cols = (int)ri.size(); } this->resize(l.size(),cols); @@ -433,6 +445,14 @@ namespace codac2 return *this; } + auto& inflate(const Matrix_& r) + { + assert(r.minCoeff() >= 0.); + for(size_t i = 0 ; i < this->size() ; i++) + (this->data()+i)->inflate(*(r.data()+i)); + return *this; + } + bool operator==(const IntervalMatrix_& x) const { if(x.size() != this->size() || x.rows() != this->rows() || x.cols() != this->cols()) @@ -525,6 +545,23 @@ namespace codac2 } }; + template + std::ostream& operator<<(std::ostream& os, const IntervalMatrix_& x) + { + if(x.is_empty()) return os << "empty matrix"; + os << "("; + for(size_t i = 0 ; i < x.rows() ; i++) + { + os << "("; + for(size_t j = 0 ; j < x.cols() ; j++) + os << x(i,j) << (j auto operator-(const IntervalMatrix_& x) { diff --git a/src/core/2/domains/interval/codac2_IntervalVector.h b/src/core/2/domains/interval/codac2_IntervalVector.h index 40a1e8cce..c9c353afd 100644 --- a/src/core/2/domains/interval/codac2_IntervalVector.h +++ b/src/core/2/domains/interval/codac2_IntervalVector.h @@ -17,14 +17,15 @@ #ifndef __CODAC2_INTERVALVECTOR_H__ #define __CODAC2_INTERVALVECTOR_H__ +#include #include #include #include -#include -#include #include #include "codac2_Interval.h" #include "codac2_IntervalMatrix.h" +#include "codac2_Vector.h" +#include namespace codac2 { @@ -55,7 +56,7 @@ namespace codac2 } explicit IntervalVector_(const Interval& x) - : IntervalMatrix_(1,1,x) + : IntervalMatrix_(N,1,x) { } template @@ -73,9 +74,11 @@ namespace codac2 explicit IntervalVector_(double bounds[][2]) : IntervalVector_(this->size(), bounds) - { - - } + { } + + explicit IntervalVector_(const Vector_& lb, const Vector_& ub) + : IntervalMatrix_(lb, ub) + { } IntervalVector_(std::initializer_list l) : IntervalMatrix_(l.size(),1) @@ -225,6 +228,17 @@ namespace codac2 } }; + template + std::ostream& operator<<(std::ostream& os, const IntervalVector_& x) + { + if(x.is_empty()) return os << "empty vector"; + os << "("; + for(size_t i = 0 ; i < x.size() ; i++) + os << x[i] << (i codac::IntervalVector to_codac1(const IntervalVector_& x) { diff --git a/src/core/2/domains/interval/codac2_cart_prod.cpp b/src/core/2/domains/interval/codac2_cart_prod.cpp new file mode 100644 index 000000000..b154fc36f --- /dev/null +++ b/src/core/2/domains/interval/codac2_cart_prod.cpp @@ -0,0 +1,50 @@ +/** + * \file + * + * ---------------------------------------------------------------------------- + * \date 2022 + * \author Simon Rohou + * \copyright Copyright 2022 Codac Team + * \license This program is distributed under the terms of + * the GNU Lesser General Public License (LGPL). + */ + +#include +#include "codac2_cart_prod.h" + +using namespace std; + +namespace codac2 +{ + IntervalVector cart_prod_dyn(const Interval& x1, const Interval& x2) + { + return IntervalVector({x1,x2}); + } + + IntervalVector_<2> cart_prod_static(const Interval& x1, const Interval& x2) + { + return IntervalVector_<2>({x1,x2}); + } + + IntervalVector cart_prod_dyn(const IntervalVector& x1, const Interval& x2) + { + IntervalVector x(x1.size()+1); + x << x1,x2; + return x; + } + + IntervalVector cart_prod_dyn(const Interval& x1, const IntervalVector& x2) + { + IntervalVector x(x2.size()+1); + x << x1,x2; + return x; + } + + IntervalVector cart_prod_dyn(const IntervalVector& x1, const IntervalVector& x2) + { + IntervalVector x(x1.size()+x2.size()); + x << x1,x2; + return x; + } + +} // namespace codac \ No newline at end of file diff --git a/src/core/2/domains/interval/codac2_cart_prod.h b/src/core/2/domains/interval/codac2_cart_prod.h index 2f499b2fe..f9a939b6d 100644 --- a/src/core/2/domains/interval/codac2_cart_prod.h +++ b/src/core/2/domains/interval/codac2_cart_prod.h @@ -17,55 +17,32 @@ namespace codac2 { - IntervalVector cart_prod_dyn(const Interval& x1, const Interval& x2) - { - return IntervalVector({x1,x2}); - } - - auto cart_prod_static(const Interval& x1, const Interval& x2) - { - return IntervalVector_<2>({x1,x2}); - } - - auto cart_prod_dyn(const IntervalVector& x1, const Interval& x2) - { - IntervalVector x(x1.size()+1); - x << x1,x2; - return x; - } + IntervalVector cart_prod_dyn(const Interval& x1, const Interval& x2); + IntervalVector_<2> cart_prod_static(const Interval& x1, const Interval& x2); + IntervalVector cart_prod_dyn(const IntervalVector& x1, const Interval& x2); template - auto cart_prod_static(const IntervalVector_& x1, const Interval& x2) + IntervalVector_ cart_prod_static(const IntervalVector_& x1, const Interval& x2) { IntervalVector_ x; x << x1,x2; return x; } - auto cart_prod_dyn(const Interval& x1, const IntervalVector& x2) - { - IntervalVector x(x2.size()+1); - x << x1,x2; - return x; - } + IntervalVector cart_prod_dyn(const Interval& x1, const IntervalVector& x2); template - auto cart_prod_static(const Interval& x1, const IntervalVector_& x2) + IntervalVector_ cart_prod_static(const Interval& x1, const IntervalVector_& x2) { IntervalVector_ x; x << x1,x2; return x; } - auto cart_prod_dyn(const IntervalVector& x1, const IntervalVector& x2) - { - IntervalVector x(x1.size()+x2.size()); - x << x1,x2; - return x; - } + IntervalVector cart_prod_dyn(const IntervalVector& x1, const IntervalVector& x2); template - auto cart_prod_static(const IntervalVector_& x1, const IntervalVector_& x2) + IntervalVector_ cart_prod_static(const IntervalVector_& x1, const IntervalVector_& x2) { IntervalVector_ x; x << x1,x2; diff --git a/src/core/2/domains/paving/codac2_Paving.h b/src/core/2/domains/paving/codac2_Paving.h index 932bbe658..92409fee2 100644 --- a/src/core/2/domains/paving/codac2_Paving.h +++ b/src/core/2/domains/paving/codac2_Paving.h @@ -20,26 +20,32 @@ namespace codac2 { - template - class Paving : public std::enable_shared_from_this> + template + class PavingBase { public: - explicit Paving(size_t n) - : _x(IntervalVector_(n)) + explicit PavingBase(const IntervalVector_& x) + : _x(x) { } - explicit Paving(const IntervalVector_& x) - : _x(x) + virtual ~PavingBase() = default; + + const IntervalVector_& box() const { + return _x; + } + std::shared_ptr

left() + { + return _left; } - const IntervalVector_& box() const + std::shared_ptr

right() { - return _x; + return _right; } bool is_empty() const @@ -66,13 +72,14 @@ namespace codac2 return v; } - void bisect(float ratio = 0.49) + virtual void bisect(float ratio = 0.49) { assert(Interval(0.,1.).interior_contains(ratio)); assert(is_leaf() && "only leaves can be bisected"); + assert(_x.is_bisectable()); auto p = _x.bisect(ratio); - _left = std::make_shared(p.first); - _right = std::make_shared(p.second); + _left = std::make_shared

(p.first); + _right = std::make_shared

(p.second); } IntervalVector_ hull_box() const @@ -92,9 +99,9 @@ namespace codac2 return l; } - std::list*> leaves_list() + std::list leaves_list() { - std::list*> l; + std::list l; leaves_list_push(l); return l; } @@ -112,7 +119,7 @@ namespace codac2 } } - void leaves_list_push(std::list*>& l) + void leaves_list_push(std::list& l) { if(is_leaf() && !_x.is_empty()) l.push_back(this); @@ -126,9 +133,20 @@ namespace codac2 public: // todo IntervalVector_ _x; - std::shared_ptr _left = nullptr, _right = nullptr; + std::shared_ptr

_left = nullptr, _right = nullptr; + }; + + template + class Paving : public PavingBase,N> + { + public: + + explicit Paving(size_t n) + : PavingBase,N>(IntervalVector_(n)) + { } }; + } // namespace codac #endif \ No newline at end of file diff --git a/src/core/2/variables/codac2_Matrix.h b/src/core/2/variables/codac2_Matrix.h index e0203e4b8..22229c39f 100644 --- a/src/core/2/variables/codac2_Matrix.h +++ b/src/core/2/variables/codac2_Matrix.h @@ -12,9 +12,8 @@ #ifndef __CODAC2_MATRIX_H__ #define __CODAC2_MATRIX_H__ -#include -#include -#include +#include +#include namespace codac2 { @@ -27,9 +26,7 @@ namespace codac2 Matrix_() : Eigen::Matrix() - { - - } + { } Matrix_(size_t nb_rows, size_t nb_cols) : Eigen::Matrix(nb_rows, nb_cols) @@ -56,6 +53,27 @@ namespace codac2 explicit Matrix_(double values[]) : Matrix_(R, C, values) { } + + Matrix_(std::initializer_list> l) + : Matrix_() + { + assert((R == Dynamic || (int)l.size() == R) && "ill-formed matrix"); + int cols = -1; + for(const auto& ri : l) { + assert(cols == -1 || cols == (int)ri.size() && "ill-formed matrix"); + cols = (int)ri.size(); + } + this->resize(l.size(),cols); + size_t i = 0; + for(const auto& ri : l) + { + size_t j = 0; + for(const auto& ci : ri) + (*this)(i,j++) = ci; + i++; + } + // todo: use thias as faster? std::copy(l.begin(), l.end(), vec); + } template Matrix_(const Eigen::MatrixBase& other) @@ -75,6 +93,16 @@ namespace codac2 return Eigen::Matrix::Identity(); } + double min() const + { + return Eigen::Matrix::minCoeff(); + } + + double max() const + { + return Eigen::Matrix::maxCoeff(); + } + auto operator+(const Matrix_& x) const { auto y = *this; @@ -115,9 +143,60 @@ namespace codac2 { return Eigen::Matrix::Zero(); } - }; + template + std::ostream& operator<<(std::ostream& os, const Matrix_& x) + { + os << "("; + for(size_t i = 0 ; i < x.rows() ; i++) + { + os << "("; + for(size_t j = 0 ; j < x.cols() ; j++) + os << x(i,j) << (j + Matrix_ floor(const Matrix_& x) + { + Matrix_ f(x.rows(), x.cols()); + for(size_t i = 0 ; i < x.size() ; i++) + *(f.data()+i) = std::floor(*(x.data()+i)); + return f; + } + + template + Matrix_ round(const Matrix_& x) + { + Matrix_ f(x.rows(), x.cols()); + for(size_t i = 0 ; i < x.size() ; i++) + *(f.data()+i) = std::round(*(x.data()+i)); + return f; + } + + template + Matrix_ ceil(const Matrix_& x) + { + Matrix_ f(x.rows(), x.cols()); + for(size_t i = 0 ; i < x.size() ; i++) + *(f.data()+i) = std::ceil(*(x.data()+i)); + return f; + } + + template + Matrix_ abs(const Matrix_& x) + { + Matrix_ f(x.rows(), x.cols()); + for(size_t i = 0 ; i < x.size() ; i++) + *(f.data()+i) = std::fabs(*(x.data()+i)); + return f; + } + } // namespace codac #endif \ No newline at end of file diff --git a/src/core/2/variables/codac2_Vector.h b/src/core/2/variables/codac2_Vector.h index 3538dff50..a7fa84c75 100644 --- a/src/core/2/variables/codac2_Vector.h +++ b/src/core/2/variables/codac2_Vector.h @@ -12,6 +12,7 @@ #ifndef __CODAC2_VECTOR_H__ #define __CODAC2_VECTOR_H__ +#include #include #include @@ -25,9 +26,7 @@ namespace codac2 public: Vector_() - { - - } + { } Vector_(size_t n) : Matrix_(n,1) @@ -90,12 +89,31 @@ namespace codac2 } }; + template + std::ostream& operator<<(std::ostream& os, const Vector_& x) + { + os << "("; + for(size_t i = 0 ; i < x.size() ; i++) + os << x[i] << (i Matrix_ diag(const Vector_ v) { return v.as_diag(); } + template + codac::Vector to_codac1(const Vector_& x) + { + ibex::Vector x_(x.size()); + for(size_t i = 0 ; i < x.size() ; i++) + x_[i] = x[i]; + return x_; + } + template Vector_ to_codac2(const codac::Vector& x) { diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 33c832456..2717733c0 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -180,9 +180,11 @@ ${CMAKE_CURRENT_SOURCE_DIR}/sivia/codac_sivia.h) list(APPEND CODAC2_SRC # Files related to codac2 + ${CMAKE_CURRENT_SOURCE_DIR}/2/3rd/codac2_eigen.h ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/interval/codac2_Interval.h ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/interval/codac2_IntervalMatrix.h ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/interval/codac2_IntervalVector.h + ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/interval/codac2_cart_prod.cpp ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/interval/codac2_cart_prod.h ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/tube/codac2_AbstractConstTube.h ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/tube/codac2_AbstractSlice.cpp @@ -242,6 +244,7 @@ ${CMAKE_CURRENT_SOURCE_DIR}/cn ${CMAKE_CURRENT_SOURCE_DIR}/tools ${CMAKE_CURRENT_SOURCE_DIR}/sivia + ${CMAKE_CURRENT_SOURCE_DIR}/2/3rd ${CMAKE_CURRENT_SOURCE_DIR}/2/contractors ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/interval ${CMAKE_CURRENT_SOURCE_DIR}/2/domains/tube From bc79874b081fa900e2b6a4fd84c7933e7357dd03 Mon Sep 17 00:00:00 2001 From: SimonRohou Date: Thu, 14 Sep 2023 16:05:48 +0200 Subject: [PATCH 122/256] [codac2] corrected error on min function --- src/core/2/domains/interval/codac2_IntervalMatrix.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/2/domains/interval/codac2_IntervalMatrix.h b/src/core/2/domains/interval/codac2_IntervalMatrix.h index 2747e80a1..16fa84ac0 100644 --- a/src/core/2/domains/interval/codac2_IntervalMatrix.h +++ b/src/core/2/domains/interval/codac2_IntervalMatrix.h @@ -128,8 +128,8 @@ namespace codac2 // With resize of Eigen, the data is reallocated and all previous values are lost. auto copy = *this; this->Eigen::Matrix::resize(nb_rows, nb_cols); - for(size_t i = 0 ; i < min(copy.rows(),nb_rows) ; i++) - for(size_t j = 0 ; j < min(copy.cols(),nb_cols) ; j++) + for(size_t i = 0 ; i < min((size_t)copy.rows(),nb_rows) ; i++) + for(size_t j = 0 ; j < min((size_t)copy.cols(),nb_cols) ; j++) (*this)(i,j) = copy(i,j); } From d967d8934dcccea2d237972da4d32c478939887b Mon Sep 17 00:00:00 2001 From: SimonRohou Date: Fri, 15 Sep 2023 15:03:58 +0200 Subject: [PATCH 123/256] [codac2] templated Interval/Vector/Matrix --- .../domains/interval/codac2_IntervalMatrix.h | 21 ++++++++++--------- .../domains/interval/codac2_IntervalVector.h | 5 ----- src/core/2/variables/codac2_Matrix.h | 19 +++++++++++++++++ src/core/2/variables/codac2_Vector.h | 11 +++++----- tests/core/CMakeLists.txt | 2 +- 5 files changed, 37 insertions(+), 21 deletions(-) diff --git a/src/core/2/domains/interval/codac2_IntervalMatrix.h b/src/core/2/domains/interval/codac2_IntervalMatrix.h index 16fa84ac0..88995034f 100644 --- a/src/core/2/domains/interval/codac2_IntervalMatrix.h +++ b/src/core/2/domains/interval/codac2_IntervalMatrix.h @@ -33,7 +33,9 @@ namespace codac2 IntervalMatrix_() : Eigen::Matrix() - { } + { + + } explicit IntervalMatrix_(size_t nb_rows, size_t nb_cols) : Eigen::Matrix(nb_rows, nb_cols) @@ -45,8 +47,7 @@ namespace codac2 explicit IntervalMatrix_(size_t nb_rows, size_t nb_cols, const Interval& x) : IntervalMatrix_(nb_rows, nb_cols) { - for(size_t i = 0 ; i < size() ; i++) - *(this->data()+i) = x; + init(x); } explicit IntervalMatrix_(size_t nb_rows, size_t nb_cols, double bounds[][2]) @@ -62,6 +63,7 @@ namespace codac2 (*this)(i,j) = Interval(bounds[k][0],bounds[k][1]); k++; } + assert(k == this->size()); } explicit IntervalMatrix_(double bounds[][2]) @@ -69,15 +71,16 @@ namespace codac2 { } explicit IntervalMatrix_(const Matrix_& lb, const Matrix_& ub) - : IntervalMatrix_(R, C) + : IntervalMatrix_(lb.rows(), lb.cols()) { + assert(lb.rows() == ub.rows() && lb.cols() == ub.cols()); for(size_t i = 0 ; i < this->rows() ; i++) for(size_t j = 0 ; j < this->cols() ; j++) (*this)(i,j) = Interval(lb(i,j),ub(i,j)); } explicit IntervalMatrix_(const IntervalMatrix_& x) - : IntervalMatrix_(R, C) + : IntervalMatrix_(x.rows(), x.cols()) { for(size_t i = 0 ; i < size() ; i++) *(this->data()+i) = *(x.data()+i); @@ -101,7 +104,6 @@ namespace codac2 (*this)(i,j++) = ci; i++; } - // todo: use thias as faster? std::copy(l.begin(), l.end(), vec); } // This constructor allows you to construct IntervalMatrix_ from Eigen expressions @@ -143,8 +145,7 @@ namespace codac2 static IntervalMatrix_ empty_set(size_t nb_rows = R, size_t nb_cols = C) { - IntervalMatrix_ x(nb_rows, nb_cols, Interval::empty_set()); - return x; + return IntervalMatrix_(nb_rows, nb_cols, Interval::empty_set()); } bool is_flat() const @@ -550,10 +551,10 @@ namespace codac2 { if(x.is_empty()) return os << "empty matrix"; os << "("; - for(size_t i = 0 ; i < x.rows() ; i++) + for(int i = 0 ; i < x.rows() ; i++) { os << "("; - for(size_t j = 0 ; j < x.cols() ; j++) + for(int j = 0 ; j < x.cols() ; j++) os << x(i,j) << (j empty_set(size_t n = N) - { - return IntervalMatrix_::empty_set(n,1); - } - void resize(size_t n) { this->IntervalMatrix_::resize(n,1); diff --git a/src/core/2/variables/codac2_Matrix.h b/src/core/2/variables/codac2_Matrix.h index 22229c39f..af0dc3951 100644 --- a/src/core/2/variables/codac2_Matrix.h +++ b/src/core/2/variables/codac2_Matrix.h @@ -34,6 +34,14 @@ namespace codac2 assert(R == Dynamic || R == (int)nb_rows); assert(C == Dynamic || C == (int)nb_cols); } + + Matrix_(size_t nb_rows, size_t nb_cols, double x) + : Eigen::Matrix(nb_rows, nb_cols) + { + assert(R == Dynamic || R == (int)nb_rows); + assert(C == Dynamic || C == (int)nb_cols); + init(x); + } explicit Matrix_(size_t nb_rows, size_t nb_cols, double values[]) : Matrix_(nb_rows, nb_cols) @@ -88,6 +96,12 @@ namespace codac2 return *this; } + void init(double x) + { + for(size_t i = 0 ; i < this->size() ; i++) + *(this->data()+i) = x; + } + static Matrix_ eye() { return Eigen::Matrix::Identity(); @@ -143,6 +157,11 @@ namespace codac2 { return Eigen::Matrix::Zero(); } + + static Matrix_ ones() + { + return Eigen::Matrix::Ones(); + } }; template diff --git a/src/core/2/variables/codac2_Vector.h b/src/core/2/variables/codac2_Vector.h index a7fa84c75..8080b63bd 100644 --- a/src/core/2/variables/codac2_Vector.h +++ b/src/core/2/variables/codac2_Vector.h @@ -34,6 +34,12 @@ namespace codac2 assert(N == Dynamic || N == (int)n); } + Vector_(size_t n, double x) + : Matrix_(n,1,x) + { + assert(N == Dynamic || N == (int)n); + } + Vector_(std::initializer_list l) : Matrix_(l.size(),1) { assert(N == (int)l.size() || N == -1); @@ -75,11 +81,6 @@ namespace codac2 return Matrix_(Eigen::Matrix(this->asDiagonal())); } - static Vector_ zeros() - { - return Eigen::Matrix::Zero(); - } - // todo: place this in common inheritance with IntervalVector_ template Vector_ subvector() const diff --git a/tests/core/CMakeLists.txt b/tests/core/CMakeLists.txt index 4cc10e560..dc44acb6d 100644 --- a/tests/core/CMakeLists.txt +++ b/tests/core/CMakeLists.txt @@ -6,7 +6,7 @@ set(TESTS_NAME codac-tests-core) list(APPEND SRC_TESTS ${CMAKE_CURRENT_SOURCE_DIR}/main.cpp - #${CMAKE_CURRENT_SOURCE_DIR}/tests_codac2_tubes.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/tests_codac2_tubes.cpp ${CMAKE_CURRENT_SOURCE_DIR}/tests_codac2_intervalvector.cpp ${CMAKE_CURRENT_SOURCE_DIR}/tests_codac2_intervalmatrix.cpp From e89ad5d1d4ee34b3f43e1c85ad4ef297874d81fc Mon Sep 17 00:00:00 2001 From: SimonRohou Date: Mon, 18 Sep 2023 11:19:23 +0200 Subject: [PATCH 124/256] [codac2] templated Interval/Vector/Matrix --- src/core/2/domains/interval/codac2_IntervalMatrix.h | 5 +++-- src/core/2/variables/codac2_Vector.h | 8 ++++++++ src/core/arithmetic/codac_polygon_arithmetic.cpp | 9 +++++++-- src/core/arithmetic/codac_polygon_arithmetic.h | 1 + 4 files changed, 19 insertions(+), 4 deletions(-) diff --git a/src/core/2/domains/interval/codac2_IntervalMatrix.h b/src/core/2/domains/interval/codac2_IntervalMatrix.h index 88995034f..1f0692ad3 100644 --- a/src/core/2/domains/interval/codac2_IntervalMatrix.h +++ b/src/core/2/domains/interval/codac2_IntervalMatrix.h @@ -17,6 +17,7 @@ #ifndef __CODAC2_INTERVALMATRIX_H__ #define __CODAC2_INTERVALMATRIX_H__ +#include #include #include #include @@ -130,8 +131,8 @@ namespace codac2 // With resize of Eigen, the data is reallocated and all previous values are lost. auto copy = *this; this->Eigen::Matrix::resize(nb_rows, nb_cols); - for(size_t i = 0 ; i < min((size_t)copy.rows(),nb_rows) ; i++) - for(size_t j = 0 ; j < min((size_t)copy.cols(),nb_cols) ; j++) + for(size_t i = 0 ; i < std::min((size_t)copy.rows(),nb_rows) ; i++) + for(size_t j = 0 ; j < std::min((size_t)copy.cols(),nb_cols) ; j++) (*this)(i,j) = copy(i,j); } diff --git a/src/core/2/variables/codac2_Vector.h b/src/core/2/variables/codac2_Vector.h index 8080b63bd..14a0ede33 100644 --- a/src/core/2/variables/codac2_Vector.h +++ b/src/core/2/variables/codac2_Vector.h @@ -63,6 +63,14 @@ namespace codac2 : Matrix_(N,1,values) { } + template + explicit Vector_(const std::array& array) + : Matrix_(N,1) + { + for(size_t i = 0 ; i < N ; i++) + *(this->data()+i) = array[i]; + } + template Vector_(const Eigen::MatrixBase& other) : Matrix_(other) diff --git a/src/core/arithmetic/codac_polygon_arithmetic.cpp b/src/core/arithmetic/codac_polygon_arithmetic.cpp index 4ccc7a04f..cb7394def 100644 --- a/src/core/arithmetic/codac_polygon_arithmetic.cpp +++ b/src/core/arithmetic/codac_polygon_arithmetic.cpp @@ -67,7 +67,7 @@ namespace codac return ConvexPolygon(v_result_thick_pts); } - const ConvexPolygon operator&(const ConvexPolygon& p1, const ConvexPolygon& p2) + vector inter_thickpoints(const ConvexPolygon& p1, const ConvexPolygon& p2) { vector v_pts; @@ -116,7 +116,12 @@ namespace codac } } - return ConvexPolygon(v_pts); + return v_pts; + } + + const ConvexPolygon operator&(const ConvexPolygon& p1, const ConvexPolygon& p2) + { + return ConvexPolygon(inter_thickpoints(p1,p2)); } const ConvexPolygon operator&(const IntervalVector& p1, const ConvexPolygon& p2) diff --git a/src/core/arithmetic/codac_polygon_arithmetic.h b/src/core/arithmetic/codac_polygon_arithmetic.h index 251bd323c..fd1f2bdb4 100644 --- a/src/core/arithmetic/codac_polygon_arithmetic.h +++ b/src/core/arithmetic/codac_polygon_arithmetic.h @@ -28,6 +28,7 @@ namespace codac const ConvexPolygon operator*(const IntervalMatrix& m, const ConvexPolygon& x); + std::vector inter_thickpoints(const ConvexPolygon& p1, const ConvexPolygon& p2); const ConvexPolygon operator&(const ConvexPolygon& p1, const ConvexPolygon& p2); const ConvexPolygon operator&(const IntervalVector& p1, const ConvexPolygon& p2); const ConvexPolygon operator&(const ConvexPolygon& p1, const IntervalVector& p2); From 33d09f9af2efb3cef062fac593320197a43d72ad Mon Sep 17 00:00:00 2001 From: SimonRohou Date: Mon, 18 Sep 2023 14:08:33 +0200 Subject: [PATCH 125/256] [codac2] corrected cast in Paving --- src/core/2/domains/paving/codac2_Paving.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/core/2/domains/paving/codac2_Paving.h b/src/core/2/domains/paving/codac2_Paving.h index 92409fee2..0e137b0eb 100644 --- a/src/core/2/domains/paving/codac2_Paving.h +++ b/src/core/2/domains/paving/codac2_Paving.h @@ -122,7 +122,7 @@ namespace codac2 void leaves_list_push(std::list& l) { if(is_leaf() && !_x.is_empty()) - l.push_back(this); + l.push_back(dynamic_cast(this)); else { if(_left) _left->leaves_list_push(l); @@ -144,6 +144,10 @@ namespace codac2 explicit Paving(size_t n) : PavingBase,N>(IntervalVector_(n)) { } + + explicit Paving(const IntervalVector_& x) + : PavingBase,N>(x) + { } }; From 11ea9b5fd62810144a39f126212502662ab8ca1b Mon Sep 17 00:00:00 2001 From: SimonRohou Date: Mon, 18 Sep 2023 14:09:14 +0200 Subject: [PATCH 126/256] [codac2] Vector_: corrected wrong cast in template arguments --- src/core/2/variables/codac2_Vector.h | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/core/2/variables/codac2_Vector.h b/src/core/2/variables/codac2_Vector.h index 14a0ede33..4c23ed330 100644 --- a/src/core/2/variables/codac2_Vector.h +++ b/src/core/2/variables/codac2_Vector.h @@ -52,7 +52,7 @@ namespace codac2 Vector_(const Matrix_& x) : Matrix_(x) { - assert(M == Dynamic || M == N); + static_assert(M == Dynamic || M == N); } explicit Vector_(size_t n, double values[]) @@ -63,11 +63,12 @@ namespace codac2 : Matrix_(N,1,values) { } - template - explicit Vector_(const std::array& array) + template + explicit Vector_(const std::array& array) : Matrix_(N,1) { - for(size_t i = 0 ; i < N ; i++) + static_assert(N == Dynamic || N == (int)M); + for(size_t i = 0 ; i < M ; i++) *(this->data()+i) = array[i]; } From e91bca4e23fa63da80a0e4a617ef7b3c242e36a4 Mon Sep 17 00:00:00 2001 From: SimonRohou Date: Mon, 18 Sep 2023 17:41:15 +0200 Subject: [PATCH 127/256] [codac2] allowing Tube> --- src/core/2/domains/tube/codac2_Slice.h | 13 ++++-- src/core/2/domains/tube/codac2_Tube.h | 16 ++++---- tests/core/CMakeLists.txt | 55 +++++++++++++------------- 3 files changed, 44 insertions(+), 40 deletions(-) diff --git a/src/core/2/domains/tube/codac2_Slice.h b/src/core/2/domains/tube/codac2_Slice.h index 1810111bf..7d54928c0 100644 --- a/src/core/2/domains/tube/codac2_Slice.h +++ b/src/core/2/domains/tube/codac2_Slice.h @@ -122,7 +122,7 @@ namespace codac2 BoolInterval contains(const TrajectoryVector& x) const { - if constexpr(std::is_same::value) + if constexpr(!std::is_same::value) { assert(false && "not implemented"); return BoolInterval::MAYBE; @@ -303,10 +303,15 @@ namespace codac2 void set_unbounded() { - if constexpr(std::is_same::value || std::is_same::value) // 'if' to be removed with virtual set classes - _codomain = T(); - else + if constexpr(std::is_same::value) _codomain = T(size()); + else + _codomain = T(); + + //if constexpr(std::is_same::value || std::is_same::value) // 'if' to be removed with virtual set classes + // _codomain = T(); + //else + // _codomain = T(size()); } void set_component(size_t i, const Interval& xi) diff --git a/src/core/2/domains/tube/codac2_Tube.h b/src/core/2/domains/tube/codac2_Tube.h index 3090e2aa2..6f94728ad 100644 --- a/src/core/2/domains/tube/codac2_Tube.h +++ b/src/core/2/domains/tube/codac2_Tube.h @@ -43,11 +43,9 @@ namespace codac2 { public: - // need to specify T (dim at least) explicit Tube(const std::shared_ptr& tdomain) : - // need to specify T (dim at least) Tube(tdomain, T()) - // need to specify T (dim at least) { - // need to specify T (dim at least) - // need to specify T (dim at least) } + explicit Tube(const std::shared_ptr& tdomain) : + Tube(tdomain, T()) + { } explicit Tube(const std::shared_ptr& tdomain, const TFnc& f) : Tube(tdomain, (std::is_same::value ? T() : T(f.image_dim()))) @@ -267,7 +265,7 @@ namespace codac2 { if(!tdomain()->t0_tf().contains(t)) { - if constexpr(std::is_same::value || std::is_same::value) + if constexpr(!std::is_same::value) return T(); else return T(size()); @@ -284,7 +282,7 @@ namespace codac2 { if(!tdomain()->t0_tf().is_superset(t)) { - if constexpr(std::is_same::value || std::is_same::value) + if constexpr(!std::is_same::value) return T(); else return T(size()); @@ -308,7 +306,7 @@ namespace codac2 void set(const T& codomain) { - if constexpr(!std::is_same::value) { + if constexpr(std::is_same::value) { assert((size_t)codomain.size() == size()); } for(auto& s : *this) @@ -321,7 +319,7 @@ namespace codac2 void set(const T& codomain, double t) { - if constexpr(!std::is_same::value) { + if constexpr(std::is_same::value) { assert((size_t)codomain.size() == size()); } std::list::iterator it = _tdomain->sample(t,true); diff --git a/tests/core/CMakeLists.txt b/tests/core/CMakeLists.txt index dc44acb6d..e33acb3cb 100644 --- a/tests/core/CMakeLists.txt +++ b/tests/core/CMakeLists.txt @@ -9,34 +9,35 @@ list(APPEND SRC_TESTS ${CMAKE_CURRENT_SOURCE_DIR}/main.cpp ${CMAKE_CURRENT_SOURCE_DIR}/tests_codac2_tubes.cpp ${CMAKE_CURRENT_SOURCE_DIR}/tests_codac2_intervalvector.cpp ${CMAKE_CURRENT_SOURCE_DIR}/tests_codac2_intervalmatrix.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/tests_codac2_tubes_templated_types.cpp - #${CMAKE_CURRENT_SOURCE_DIR}/tests_predefined_tubes.cpp - #${CMAKE_CURRENT_SOURCE_DIR}/tests_predefined_tubes.h - #${CMAKE_CURRENT_SOURCE_DIR}/tests_arithmetic.cpp - #${CMAKE_CURRENT_SOURCE_DIR}/tests_cn.cpp - #${CMAKE_CURRENT_SOURCE_DIR}/tests_ctc_box.cpp - #${CMAKE_CURRENT_SOURCE_DIR}/tests_ctc_cart_prod.cpp - #${CMAKE_CURRENT_SOURCE_DIR}/tests_ctc_delay.cpp - #${CMAKE_CURRENT_SOURCE_DIR}/tests_ctc_deriv.cpp - #${CMAKE_CURRENT_SOURCE_DIR}/tests_ctc_chain.cpp - #${CMAKE_CURRENT_SOURCE_DIR}/tests_ctc_eval.cpp - #${CMAKE_CURRENT_SOURCE_DIR}/tests_ctc_picard.cpp - #${CMAKE_CURRENT_SOURCE_DIR}/tests_ctc_lohner.cpp - #${CMAKE_CURRENT_SOURCE_DIR}/tests_ctc_static.cpp - #${CMAKE_CURRENT_SOURCE_DIR}/tests_definition.cpp - #${CMAKE_CURRENT_SOURCE_DIR}/tests_functions.cpp - #${CMAKE_CURRENT_SOURCE_DIR}/tests_integration.cpp - #${CMAKE_CURRENT_SOURCE_DIR}/tests_operators.cpp - #${CMAKE_CURRENT_SOURCE_DIR}/tests_geometry.cpp - #${CMAKE_CURRENT_SOURCE_DIR}/tests_polygons.cpp - #${CMAKE_CURRENT_SOURCE_DIR}/tests_serialization.cpp - #${CMAKE_CURRENT_SOURCE_DIR}/tests_slices_structure.cpp - #${CMAKE_CURRENT_SOURCE_DIR}/tests_trajectory.cpp - #${CMAKE_CURRENT_SOURCE_DIR}/tests_values.cpp - #${CMAKE_CURRENT_SOURCE_DIR}/tests_sep_polygon.cpp - #${CMAKE_CURRENT_SOURCE_DIR}/tests_sep_qinterprojf.cpp - #${CMAKE_CURRENT_SOURCE_DIR}/tests_sep_fixpoint_proj.cpp - #${CMAKE_CURRENT_SOURCE_DIR}/tests_sep_polar.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/tests_predefined_tubes.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/tests_predefined_tubes.h + ${CMAKE_CURRENT_SOURCE_DIR}/tests_arithmetic.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/tests_cn.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/tests_ctc_box.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/tests_ctc_cart_prod.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/tests_ctc_delay.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/tests_ctc_deriv.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/tests_ctc_chain.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/tests_ctc_eval.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/tests_ctc_picard.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/tests_ctc_lohner.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/tests_ctc_static.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/tests_definition.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/tests_functions.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/tests_integration.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/tests_operators.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/tests_geometry.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/tests_polygons.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/tests_serialization.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/tests_slices_structure.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/tests_trajectory.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/tests_values.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/tests_sep_polygon.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/tests_sep_qinterprojf.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/tests_sep_fixpoint_proj.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/tests_sep_polar.cpp ) add_executable(${TESTS_NAME} ${SRC_TESTS}) From 75e6381df27c261326316443f19efbb9e153bb55 Mon Sep 17 00:00:00 2001 From: SimonRohou Date: Mon, 18 Sep 2023 17:42:08 +0200 Subject: [PATCH 128/256] [codac2] allowing Tube> --- .../tests_codac2_tubes_templated_types.cpp | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 tests/core/tests_codac2_tubes_templated_types.cpp diff --git a/tests/core/tests_codac2_tubes_templated_types.cpp b/tests/core/tests_codac2_tubes_templated_types.cpp new file mode 100644 index 000000000..a27f1a989 --- /dev/null +++ b/tests/core/tests_codac2_tubes_templated_types.cpp @@ -0,0 +1,32 @@ +#include "catch_interval.hpp" +#include "vibes.h" + +#include "codac2_Tube.h" +#include "codac2_Interval.h" +#include "codac2_IntervalMatrix.h" + +using namespace Catch; +using namespace Detail; +using namespace std; +using namespace codac2; + +TEST_CASE("Test codac2::tubes templated") +{ + SECTION("Testing tube of templated IntervalMatrix") + { + auto tdomain = create_tdomain(); + Tube> a(tdomain); + a(2.3) = IntervalMatrix_<2,3>({{1.,2.,3.},{4.,5.,6.}}); + CHECK(a.nb_slices() == 3); + + vector>*> v; + for(const auto& s : a) + v.push_back(&s); + CHECK(v[0]->t0_tf() == Interval(-codac2::oo,2.3)); + CHECK(v[0]->codomain() == (IntervalMatrix_<2,3>())); + CHECK(v[1]->t0_tf() == Interval(2.3)); + CHECK(v[1]->codomain() == (IntervalMatrix_<2,3>({{1.,2.,3.},{4.,5.,6.}}))); + CHECK(v[2]->t0_tf() == Interval(2.3,codac2::oo)); + CHECK(v[2]->codomain() == (IntervalMatrix_<2,3>())); + } +} \ No newline at end of file From 608202a0201b26c8659b1bca96696bbbb91f2b5c Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Mon, 25 Sep 2023 22:46:34 +0200 Subject: [PATCH 129/256] Force doxygen v1.9.6 on Windows since latest causes issues --- .github/workflows/vcmatrix.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/vcmatrix.yml b/.github/workflows/vcmatrix.yml index 10015d8a5..1f9e24eab 100644 --- a/.github/workflows/vcmatrix.yml +++ b/.github/workflows/vcmatrix.yml @@ -54,7 +54,7 @@ jobs: if: runner.os=='Windows' - run: choco install -y -r --no-progress eigen --version=3.4.0 ${{ matrix.cfg.choco_flags }} if: runner.os=='Windows' - - run: choco install -y -r --no-progress doxygen.install graphviz & pip install --upgrade wheel setuptools sphinx breathe sphinx-issues sphinx-tabs sphinx_rtd_theme sphinx-reredirects + - run: choco install -y -r --no-progress doxygen.install --version=1.9.6 & choco install -y -r --no-progress graphviz & pip install --upgrade wheel setuptools sphinx breathe sphinx-issues sphinx-tabs sphinx_rtd_theme sphinx-reredirects if: runner.os=='Windows' - run: | wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20230510/ibex.2.8.9.20230510.nupkg --no-check-certificate -nv From ee082308ac4cda3e5e7b69aab65136cbe15bf21c Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Tue, 26 Sep 2023 23:00:08 +0200 Subject: [PATCH 130/256] Workaround for error "NumPy requires GCC >= 8.4" in CentOS docker --- scripts/docker/build_pybinding.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/docker/build_pybinding.sh b/scripts/docker/build_pybinding.sh index c40c31d1a..db1526b54 100755 --- a/scripts/docker/build_pybinding.sh +++ b/scripts/docker/build_pybinding.sh @@ -30,7 +30,7 @@ for PYBIN in /opt/python/cp3*/bin; do auditwheel repair "$whl" -w /io/wheelhouse/ done - "${PYBIN}/python" -m pip install numpy + "${PYBIN}/python" -m pip install "numpy<1.23" # Recent versions of numpy require GCC >= 8.4... "${PYBIN}/python" -m pip install codac --no-deps --no-index -f /io/wheelhouse (cd "$HOME"; "${PYBIN}/python" -m unittest discover codac.tests) cd /io From 65784a201dda9841194fb581e689247adab07ef7 Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Tue, 26 Sep 2023 00:22:39 +0200 Subject: [PATCH 131/256] Workaround of square bracket operator for MATLAB compatibility --- python/src/core/cn/codac_py_Variable.cpp | 9 +++++ .../domains/interval/codac_py_Interval.cpp | 14 ++++++++ .../interval/codac_py_IntervalMatrix.cpp | 15 +++++++++ .../interval/codac_py_IntervalVector.cpp | 15 +++++++++ .../core/domains/tube/codac_py_TubeVector.cpp | 33 +++++++++++++++++++ .../src/core/functions/codac_py_TFunction.cpp | 10 ++++++ .../trajectory/codac_py_TrajectoryVector.cpp | 33 +++++++++++++++++++ .../thickset/codac_ThickInterval.cpp | 8 +++++ 8 files changed, 137 insertions(+) diff --git a/python/src/core/cn/codac_py_Variable.cpp b/python/src/core/cn/codac_py_Variable.cpp index bd5762552..2b6a44020 100644 --- a/python/src/core/cn/codac_py_Variable.cpp +++ b/python/src/core/cn/codac_py_Variable.cpp @@ -63,6 +63,15 @@ void export_IntervalVectorVar(py::module& m) "todo", py::return_value_policy::reference_internal) + .def("getitem", [](IntervalVectorVar& s, size_t index) -> IntervalVar& + { + if(index >= static_cast(s.size())) + throw py::index_error(); + return s[static_cast(index)]; + }, + "todo", + py::return_value_policy::reference_internal) + .def("__eq__", [](const IntervalVectorVar& s1, const IntervalVectorVar& s2) { return reinterpret_cast(&s1) == reinterpret_cast(&s2); }) .def("__hash__", [](const IntervalVectorVar& s1) { return reinterpret_cast(&s1); }) diff --git a/python/src/core/domains/interval/codac_py_Interval.cpp b/python/src/core/domains/interval/codac_py_Interval.cpp index 3903f4970..ce7b3511c 100644 --- a/python/src/core/domains/interval/codac_py_Interval.cpp +++ b/python/src/core/domains/interval/codac_py_Interval.cpp @@ -111,6 +111,20 @@ void export_Interval(py::module& m) else return NAN; }, py::return_value_policy::reference_internal) + + .def("getitem", [](Interval& s, size_t index) -> double + { + if(index < 0 || index > 1) + throw py::index_error(); + + cout << "Warning: indexing on intervals is deprecated." << endl + << " Use .lb(), .ub() methods instead of []." << endl; + + if(index == 0) return s.lb(); + else if(index == 1) return s.ub(); + else return NAN; + }, + py::return_value_policy::reference_internal) // Arithmetic diff --git a/python/src/core/domains/interval/codac_py_IntervalMatrix.cpp b/python/src/core/domains/interval/codac_py_IntervalMatrix.cpp index 0552b8747..e7aac5f25 100644 --- a/python/src/core/domains/interval/codac_py_IntervalMatrix.cpp +++ b/python/src/core/domains/interval/codac_py_IntervalMatrix.cpp @@ -103,6 +103,14 @@ void export_IntervalMatrix(py::module& m) }, py::return_value_policy::reference_internal) + .def("getitem", [](IntervalMatrix& s, size_t index) -> IntervalVector& + { + if(index < 0 || index >= static_cast(s.nb_rows())) + throw py::index_error(); + return s[static_cast(index)]; + }, + py::return_value_policy::reference_internal) + .def("__setitem__", [](IntervalMatrix& s, size_t index, IntervalVector& t) { if(index < 0 || index >= static_cast(s.nb_rows())) @@ -110,6 +118,13 @@ void export_IntervalMatrix(py::module& m) s[static_cast(index)] = t; }) + .def("setitem", [](IntervalMatrix& s, size_t index, IntervalVector& t) + { + if(index < 0 || index >= static_cast(s.nb_rows())) + throw py::index_error(); + s[static_cast(index)] = t; + }) + .def("assign", &assign_IntervalMatrix) .def(py::self == py::self) .def(py::self != py::self) diff --git a/python/src/core/domains/interval/codac_py_IntervalVector.cpp b/python/src/core/domains/interval/codac_py_IntervalVector.cpp index d9336906d..d14372136 100644 --- a/python/src/core/domains/interval/codac_py_IntervalVector.cpp +++ b/python/src/core/domains/interval/codac_py_IntervalVector.cpp @@ -183,12 +183,27 @@ void export_IntervalVector(py::module& m) }, py::return_value_policy::reference_internal) + .def("getitem", [](IntervalVector& s, size_t index) -> Interval& + { + if(index < 0 || index >= static_cast(s.size())) + throw py::index_error(); + return s[static_cast(index)]; + }, + py::return_value_policy::reference_internal) + .def("__setitem__", [](IntervalVector& s, size_t index, Interval& t) { if(index < 0 || index >= static_cast(s.size())) throw py::index_error(); s[static_cast(index)] = t; }) + + .def("setitem", [](IntervalVector& s, size_t index, Interval& t) + { + if(index < 0 || index >= static_cast(s.size())) + throw py::index_error(); + s[static_cast(index)] = t; + }) .def("__iter__", [](const IntervalVector& s) diff --git a/python/src/core/domains/tube/codac_py_TubeVector.cpp b/python/src/core/domains/tube/codac_py_TubeVector.cpp index b0f11fc5b..aca0dd891 100644 --- a/python/src/core/domains/tube/codac_py_TubeVector.cpp +++ b/python/src/core/domains/tube/codac_py_TubeVector.cpp @@ -479,6 +479,15 @@ void export_TubeVector(py::module& m) TUBEVECTOR_TUBE_OPERATORB_INT, py::return_value_policy::reference_internal) + .def("getitem", [](TubeVector& s, size_t index) -> Tube& + { + if(index >= static_cast(s.size())) + throw py::index_error(); + return s[static_cast(index)]; + }, + TUBEVECTOR_TUBE_OPERATORB_INT, + py::return_value_policy::reference_internal) + .def("__getitem__", [](const TubeVector& s, py::slice slice) -> TubeVector { size_t start, stop, step, slicelength; @@ -495,6 +504,22 @@ void export_TubeVector(py::module& m) }, TUBEVECTOR_CONSTTUBE_OPERATORB_INT) + .def("getitem", [](const TubeVector& s, py::slice slice) -> TubeVector + { + size_t start, stop, step, slicelength; + + if(!slice.compute(s.size(), &start, &stop, &step, &slicelength)) + throw py::error_already_set(); + + if(step != 1) + cout << "Warning slice step must be equal to 1\n"; + + // To respect the python convention, the stop index + // is not included in slice + return s.subvector(start, start+slicelength-1); + }, + TUBEVECTOR_CONSTTUBE_OPERATORB_INT) + .def("__setitem__", [](TubeVector& s, size_t index, Tube& t) { if(index >= static_cast(s.size())) @@ -503,6 +528,14 @@ void export_TubeVector(py::module& m) }, TUBEVECTOR_TUBE_OPERATORB_INT) + .def("setitem", [](TubeVector& s, size_t index, Tube& t) + { + if(index >= static_cast(s.size())) + throw py::index_error(); + s[static_cast(index)] = t; + }, + TUBEVECTOR_TUBE_OPERATORB_INT) + // Operators .def("__add__", [](const TubeVector& x) { return +x; }) diff --git a/python/src/core/functions/codac_py_TFunction.cpp b/python/src/core/functions/codac_py_TFunction.cpp index 9a78ffaec..63c55e1b6 100644 --- a/python/src/core/functions/codac_py_TFunction.cpp +++ b/python/src/core/functions/codac_py_TFunction.cpp @@ -150,5 +150,15 @@ void export_TFunction(py::module& m, py::class_& fnc) }, TFUNCTION_CONSTTFUNCTION_OPERATORB_INT, py::return_value_policy::reference_internal) + + .def("getitem", [](TFunction& s, size_t index) + { + if((int)index >= s.nb_var()) + throw py::index_error(); + + return s[static_cast(index)]; + }, + TFUNCTION_CONSTTFUNCTION_OPERATORB_INT, + py::return_value_policy::reference_internal) ; } diff --git a/python/src/core/variables/trajectory/codac_py_TrajectoryVector.cpp b/python/src/core/variables/trajectory/codac_py_TrajectoryVector.cpp index b5835d84c..af9b59c50 100644 --- a/python/src/core/variables/trajectory/codac_py_TrajectoryVector.cpp +++ b/python/src/core/variables/trajectory/codac_py_TrajectoryVector.cpp @@ -273,6 +273,15 @@ void export_TrajectoryVector(py::module& m) TRAJECTORYVECTOR_TRAJECTORY_OPERATORB_INT, py::return_value_policy::reference_internal) + .def("getitem", [](TrajectoryVector& s, size_t index) -> Trajectory& + { + if(index >= static_cast(s.size())) + throw py::index_error(); + return s[static_cast(index)]; + }, + TRAJECTORYVECTOR_TRAJECTORY_OPERATORB_INT, + py::return_value_policy::reference_internal) + .def("__getitem__", [](const TrajectoryVector& s, py::slice slice) -> TrajectoryVector { size_t start, stop, step, slicelength; @@ -289,6 +298,22 @@ void export_TrajectoryVector(py::module& m) }, TRAJECTORYVECTOR_CONSTTRAJECTORYVECTOR_SUBVECTOR_INT_INT) + .def("getitem", [](const TrajectoryVector& s, py::slice slice) -> TrajectoryVector + { + size_t start, stop, step, slicelength; + + if(!slice.compute(s.size(), &start, &stop, &step, &slicelength)) + throw py::error_already_set(); + + if(step != 1) + cout << "Warning slice step must be equal to 1\n"; + + // To respect the python convention, the stop index + // is not included in slice + return s.subvector(start, start+slicelength-1); + }, + TRAJECTORYVECTOR_CONSTTRAJECTORYVECTOR_SUBVECTOR_INT_INT) + .def("__setitem__", [](TrajectoryVector& s, size_t index, Trajectory& t) { if(index >= static_cast(s.size())) @@ -297,6 +322,14 @@ void export_TrajectoryVector(py::module& m) }, TRAJECTORYVECTOR_TRAJECTORY_OPERATORB_INT) + .def("setitem", [](TrajectoryVector& s, size_t index, Trajectory& t) + { + if(index >= static_cast(s.size())) + throw py::index_error(); + s[static_cast(index)] = t; + }, + TRAJECTORYVECTOR_TRAJECTORY_OPERATORB_INT) + // Operators .def("__add__", [](const TrajectoryVector& x) { return +x; }) diff --git a/python/src/unsupported/thickset/codac_ThickInterval.cpp b/python/src/unsupported/thickset/codac_ThickInterval.cpp index 0caf35ef6..795d365ee 100644 --- a/python/src/unsupported/thickset/codac_ThickInterval.cpp +++ b/python/src/unsupported/thickset/codac_ThickInterval.cpp @@ -96,10 +96,18 @@ void export_thickInterval(py::module& m) if (idx > self.size()) throw py::index_error(); return self[idx]; }) + .def("getitem", [](ThickBox& self, size_t idx){ + if (idx > self.size()) throw py::index_error(); + return self[idx]; + }) .def("__setitem__", [](ThickBox& self, size_t idx, ThickInterval& itv){ if (idx > self.size()) throw py::index_error(); self[idx]=itv; }) + .def("setitem", [](ThickBox& self, size_t idx, ThickInterval& itv){ + if (idx > self.size()) throw py::index_error(); + self[idx]=itv; + }) .def("superset", &ThickBox::superset) .def("to_string", &ThickBox::to_string) .def("__repr__", &ThickBox::to_string) From 5a12473d368672df667622503d168920dfdf9eb5 Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Wed, 27 Sep 2023 11:33:38 +0200 Subject: [PATCH 132/256] Add preliminary MATLAB example --- .../01_getting_started/a01_getting_started.m | 91 +++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 examples/tuto/01_getting_started/a01_getting_started.m diff --git a/examples/tuto/01_getting_started/a01_getting_started.m b/examples/tuto/01_getting_started/a01_getting_started.m new file mode 100644 index 000000000..030702085 --- /dev/null +++ b/examples/tuto/01_getting_started/a01_getting_started.m @@ -0,0 +1,91 @@ +% Codac - Examples +% Dynamic range-only localization +% ---------------------------------------------------------------------------- + +import py.codac.* + + +% =================== 0. Parameters, truth and data ==================== + +dt = 0.01; % timestep for tubes accuracy +tdomain = Interval(0, 3); % temporal limits [t_0,t_f]=[0,3] + +x = TubeVector(tdomain, dt, int32(4)); % 4d tube for state vectors +v = TubeVector(tdomain, dt, int32(4)); % 4d tube for derivatives of the states +u = TubeVector(tdomain, dt, int32(2)); % 2d tube for inputs of the system + +x_truth = TrajectoryVector(tdomain, TFunction(['(' ... + '10*cos(t)+t ;' ... + '5*sin(2*t)+t ;' ... + 'atan2((10*cos(2*t)+1),(-10*sin(t)+1)) ;' ... + 'sqrt((-10*sin(t)+1)^2+(10*cos(2*t)+1)^2))'])); % actual trajectory + +% Continuous measurements coming from the truth +measured_psi = x_truth.getitem(int32(2)).sample(dt).make_continuous(); +measured_psi = measured_psi + RandTrajectory(tdomain, dt, Interval(-0.01,0.01)); % adding some noise +measured_speed = x_truth.getitem(int32(3)).sample(dt); +measured_speed = measured_speed + RandTrajectory(tdomain, dt, Interval(-0.01,0.01)); % adding some noise + + +% =============== 1. Defining domains for our variables ================ + +x.setitem(int32(2), Tube(measured_psi, dt).inflate(0.01)); % measured_psi is a set of measurements +x.setitem(int32(3), Tube(measured_speed, dt).inflate(0.01)); + +e_y = Interval(-0.1,0.1); +y = {Interval(1.9+e_y), Interval(3.6+e_y), ... + Interval(2.8+e_y)}; % set of range-only observations +b = {[8,3],[0,5],[-2,1]}; % positions of the three 2d landmarks +t = [0.3, 1.5, 2.0]; % times of measurements + + +% =========== 2. Defining contractors to deal with equations =========== + +ctc_f = CtcFunction(Function('v[4]', 'x[4]', 'u[2]', '(v[0]-x[3]*cos(x[2]) ; v[1]-x[3]*sin(x[2]) ; v[2]-u[0] ; v[3]-u[1])')); + + +% =============== 3. Adding the contractors to a network =============== + +cn = ContractorNetwork(); % creating a network + +cn.add(ctc_f, py.list({v, x, u})); % adding the f constraint + +for i=1:length(y) % we add the observ. constraint for each range-only measurement + + p = cn.create_interm_var(IntervalVector(int32(4))); % intermed. variable (state at t_i) + + % Distance constraint: relation between the state at t_i and the ith beacon position + cn.add(CtcDist(), py.list({cn.subvector(p,int32(0),int32(1)), py.list(b{i}), y{i}})); + + % Eval constraint: relation between the state at t_i and all the states over [t_0,t_f]= + cn.add(CtcEval(), py.list({t(i), p, x, v})); +end + + +% ======================= 4. Solving the problem ======================= + +cn.contract(true); + + +% ============================ 5. Graphics ============================= + +beginDrawing(); +fig = VIBesFigMap('fig'); +fig.set_properties(int32(50), int32(50), int32(900), int32(550)); +fig.add_trajectory(x_truth, 'xtruth', int32(0), int32(1), 'white'); +fig.add_tube(x, 'x', int32(0), int32(1)); +fig.smooth_tube_drawing(true); + +for i=1:3 + fig.add_beacon(py.list(b{i}), 0.2); % drawing beacons + fig.draw_ring(b{i}(1), b{i}(2), y{i}, 'darkGray'); % drawing range-only measurements + fig.draw_vehicle(t(i), x_truth, 0.7); % drawing robot position at t +end + +fig.show(0); +endDrawing(); + + +% Checking if this example still works: +if x.volume() < 5; check = true; else; check = false; end % todo: x.contains(x_truth) +assert(check) From 189980b47dd06ecc4f537aabf95d36bf5d4af0b5 Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Sat, 7 Oct 2023 18:05:48 +0200 Subject: [PATCH 133/256] Add preliminary MATLAB documentation --- doc/doc/index.rst | 2 +- doc/doc/install/01-installation.rst | 2 +- doc/doc/install/02-start-py-project.rst | 6 +-- doc/doc/install/03-start-cpp-project.rst | 6 +-- doc/doc/install/04-start-matlab-project.rst | 42 +++++++++++++++++++++ doc/doc/toctree.rst | 1 + 6 files changed, 47 insertions(+), 12 deletions(-) create mode 100644 doc/doc/install/04-start-matlab-project.rst diff --git a/doc/doc/index.rst b/doc/doc/index.rst index 6c99cbe43..d9ee818c2 100644 --- a/doc/doc/index.rst +++ b/doc/doc/index.rst @@ -227,7 +227,7 @@ The distance function :math:`g(\mathbf{x},\mathbf{b})` between the robot and a l .. figure:: img/rangeonly-nox0.png | *You just solved a non-linear state-estimation without knowledge about initial condition.* -| See the full example on Github: `in C++ `_ or `in Python `_. +| See the full example on Github: `in C++ `_, `in Python `__ or `in MATLAB `_. In the tutorial and in the examples folder of this library, you will find more advanced problems such as Simultaneous Localization And Mapping (SLAM), data association problems or delayed systems. diff --git a/doc/doc/install/01-installation.rst b/doc/doc/install/01-installation.rst index 28245c0d0..0fb0dae8d 100644 --- a/doc/doc/install/01-installation.rst +++ b/doc/doc/install/01-installation.rst @@ -4,7 +4,7 @@ Installing the Codac library ############################ -Codac is available in both C++17 and Python3. Note that you can also :ref:`use Codac online in Python `, without having to install the library on your machine. +Codac is available in both C++17 and Python3 (:ref:`as well as MATLAB through its Python interface `). Note that you can also :ref:`use Codac online in Python `, without having to install the library on your machine. .. role:: gbg diff --git a/doc/doc/install/02-start-py-project.rst b/doc/doc/install/02-start-py-project.rst index 5f120567f..5a40f330b 100644 --- a/doc/doc/install/02-start-py-project.rst +++ b/doc/doc/install/02-start-py-project.rst @@ -30,11 +30,7 @@ Start a Python project python3 myscript.py -In order to visualize the tube, you need to launch the VIBes viewer independently. On Linux, you can for instance execute: - -.. code-block:: bash - - VIBes-viewer +In order to visualize the tube, you need to launch before the :ref:`VIBes viewer ` independently. If everything is well installed on your computer, you should see the following window appear: diff --git a/doc/doc/install/03-start-cpp-project.rst b/doc/doc/install/03-start-cpp-project.rst index 357004ece..49f1b5335 100644 --- a/doc/doc/install/03-start-cpp-project.rst +++ b/doc/doc/install/03-start-cpp-project.rst @@ -113,11 +113,7 @@ Lastly, the project can be run with: ./build/my_project | This script will create a simple tube and display it. -| In order to visualize the tube, you need to launch the VIBes viewer independently. On Linux, you can for instance execute: - -.. code-block:: bash - - VIBes-viewer +| In order to visualize the tube, you need to launch before the :ref:`VIBes viewer ` independently. If everything is well installed on your computer, you should see the following window appear: diff --git a/doc/doc/install/04-start-matlab-project.rst b/doc/doc/install/04-start-matlab-project.rst new file mode 100644 index 000000000..f62a1e11e --- /dev/null +++ b/doc/doc/install/04-start-matlab-project.rst @@ -0,0 +1,42 @@ +.. _sec-start-matlab-project: + +###################### +Start a MATLAB project +###################### + +.. tip:: + | You are using Python? + | :ref:`sec-start-py-project` + +.. warning:: + + | MATLAB support is preliminary, limited tests have been made for now. See `this example `_. + +| Codac is ready to be used if you have at least MATLAB R2019b, `a supported version of Python `_ and :ref:`installed Codac Python package `. + +.. admonition:: MATLAB specificities + + | Integers in function calls from the Codac library should be cast to `int32`, e.g. `4` would be `int32(4)` (however do not change those that are in strings), otherwise remember that all numbers are `double` by default in MATLAB. + | The operator `[index]` for a Codac object should be replaced with `.getitem(int32(index))` when getting its value while it should be replaced with `.setitem(int32(index))` when setting its value (please note that indices of Codac objects start at `0` contrary to typical MATLAB objects such as MATLAB arrays or cell arrays). + | Python lists of objects should be converted to MATLAB cell arrays. + | Also, when a Codac function needs a Python list parameter, the corresponding MATLAB cell array should be given as `py.list(...)`. + +.. code-block:: matlab + + import py.codac.* + + x = Tube(Interval(0,10), 0.01, TFunction("cos(t)+abs(t-5)*[-0.1,0.1]")); + + beginDrawing(); + fig = VIBesFigTube("My first tube"); + fig.add_tube(x, "x"); + fig.show(); + endDrawing(); + +| This script will create a simple tube and display it. + +In order to visualize the tube, you need to launch before the :ref:`VIBes viewer ` independently. + +If everything is well installed on your computer, you should see the following window appear: + +.. Figure:: img/helloworld.png diff --git a/doc/doc/toctree.rst b/doc/doc/toctree.rst index 0cbc450cc..3a5f68eb7 100644 --- a/doc/doc/toctree.rst +++ b/doc/doc/toctree.rst @@ -13,6 +13,7 @@ Codac: constraint-programming for robotics /install/01-installation /install/02-start-py-project /install/03-start-cpp-project + /install/04-start-matlab-project .. toctree:: From f727e17ca301671c67bf8582cc1cf23823bd3926 Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Sat, 7 Oct 2023 18:24:33 +0200 Subject: [PATCH 134/256] Add Python 3.12 support for Windows and macOS --- .github/workflows/macosmatrix.yml | 2 ++ .github/workflows/vcmatrix.yml | 2 ++ 2 files changed, 4 insertions(+) diff --git a/.github/workflows/macosmatrix.yml b/.github/workflows/macosmatrix.yml index 272b2999c..9ae62fb2c 100644 --- a/.github/workflows/macosmatrix.yml +++ b/.github/workflows/macosmatrix.yml @@ -19,12 +19,14 @@ jobs: fail-fast: false matrix: cfg: + - { os: macos-11, shell: bash, arch: arm64, runtime: bigsur, cmake_flags: '-fPIC', trgt: '11.0', cpcfg: '-macosx_11_0_arm64', py_v_maj: 3, py_v_min: 12, desc: 'macOS Big Sur Python 3.12 arm64' } - { os: macos-11, shell: bash, arch: arm64, runtime: bigsur, cmake_flags: '-fPIC', trgt: '11.0', cpcfg: '-macosx_11_0_arm64', py_v_maj: 3, py_v_min: 11, desc: 'macOS Big Sur Python 3.11 arm64' } - { os: macos-11, shell: bash, arch: arm64, runtime: bigsur, cmake_flags: '-fPIC', trgt: '11.0', cpcfg: '-macosx_11_0_arm64', py_v_maj: 3, py_v_min: 10, desc: 'macOS Big Sur Python 3.10 arm64' } - { os: macos-11, shell: bash, arch: arm64, runtime: bigsur, cmake_flags: '-fPIC', trgt: '11.0', cpcfg: '-macosx_11_0_arm64', py_v_maj: 3, py_v_min: 9, desc: 'macOS Big Sur Python 3.9 arm64' } - { os: macos-11, shell: bash, arch: arm64, runtime: bigsur, cmake_flags: '-fPIC', trgt: '11.0', cpcfg: '-macosx_11_0_arm64', py_v_maj: 3, py_v_min: 8, desc: 'macOS Big Sur Python 3.8 arm64' } - { os: macos-11, shell: bash, arch: arm64, runtime: bigsur, cmake_flags: '-fPIC', trgt: '11.0', cpcfg: '-macosx_11_0_arm64', py_v_maj: 3, py_v_min: 7, desc: 'macOS Big Sur Python 3.7 arm64' } - { os: macos-11, shell: bash, arch: arm64, runtime: bigsur, cmake_flags: '-fPIC', trgt: '11.0', cpcfg: '-macosx_11_0_arm64', py_v_maj: 3, py_v_min: 6, desc: 'macOS Big Sur Python 3.6 arm64' } + - { os: macos-11, shell: bash, arch: x86_64, runtime: bigsur, cmake_flags: '-fPIC', trgt: '10.9', cpcfg: '-macosx_10_9_x86_64', py_v_maj: 3, py_v_min: 12, desc: 'macOS Big Sur Python 3.12 x86_64' } - { os: macos-11, shell: bash, arch: x86_64, runtime: bigsur, cmake_flags: '-fPIC', trgt: '10.9', cpcfg: '-macosx_10_9_x86_64', py_v_maj: 3, py_v_min: 11, desc: 'macOS Big Sur Python 3.11 x86_64' } - { os: macos-11, shell: bash, arch: x86_64, runtime: bigsur, cmake_flags: '-fPIC', trgt: '10.9', cpcfg: '-macosx_10_9_x86_64', py_v_maj: 3, py_v_min: 10, desc: 'macOS Big Sur Python 3.10 x86_64' } - { os: macos-11, shell: bash, arch: x86_64, runtime: bigsur, cmake_flags: '-fPIC', trgt: '10.9', cpcfg: '-macosx_10_9_x86_64', py_v_maj: 3, py_v_min: 9, desc: 'macOS Big Sur Python 3.9 x86_64' } diff --git a/.github/workflows/vcmatrix.yml b/.github/workflows/vcmatrix.yml index 1f9e24eab..70a4c18d9 100644 --- a/.github/workflows/vcmatrix.yml +++ b/.github/workflows/vcmatrix.yml @@ -16,10 +16,12 @@ jobs: fail-fast: false matrix: cfg: + - { os: windows-2022, shell: cmd, arch: x86, runtime: vc17, cmake_params: '-G "Visual Studio 17" -T v143 -A Win32', choco_flags: '--x86', cpcfg: '-win32', py_v_maj: 3, py_v_min: 12, desc: 'Windows Visual Studio 2022 x86 Python 3.12' } - { os: windows-2022, shell: cmd, arch: x86, runtime: vc17, cmake_params: '-G "Visual Studio 17" -T v143 -A Win32', choco_flags: '--x86', cpcfg: '-win32', py_v_maj: 3, py_v_min: 11, desc: 'Windows Visual Studio 2022 x86 Python 3.11' } - { os: windows-2022, shell: cmd, arch: x86, runtime: vc16, cmake_params: '-G "Visual Studio 17" -T v142 -A Win32', choco_flags: '--x86', cpcfg: '-win32', py_v_maj: 3, py_v_min: 10, desc: 'Windows Visual Studio 2019 x86 Python 3.10' } - { os: windows-2022, shell: cmd, arch: x86, runtime: vc16, cmake_params: '-G "Visual Studio 17" -T v142 -A Win32', choco_flags: '--x86', cpcfg: '-win32', py_v_maj: 3, py_v_min: 9, desc: 'Windows Visual Studio 2019 x86 Python 3.9' } - { os: windows-2022, shell: cmd, arch: x86, runtime: vc16, cmake_params: '-G "Visual Studio 17" -T v142 -A Win32', choco_flags: '--x86', cpcfg: '-win32', py_v_maj: 3, py_v_min: 8, desc: 'Windows Visual Studio 2019 x86 Python 3.8' } + - { os: windows-2022, shell: cmd, arch: x64, runtime: vc17, cmake_params: '-G "Visual Studio 17" -T v143 -A x64', cpcfg: '-win_amd64', py_v_maj: 3, py_v_min: 12, desc: 'Windows Visual Studio 2022 x64 Python 3.12' } - { os: windows-2022, shell: cmd, arch: x64, runtime: vc17, cmake_params: '-G "Visual Studio 17" -T v143 -A x64', cpcfg: '-win_amd64', py_v_maj: 3, py_v_min: 11, desc: 'Windows Visual Studio 2022 x64 Python 3.11' } - { os: windows-2022, shell: cmd, arch: x64, runtime: vc16, cmake_params: '-G "Visual Studio 17" -T v142 -A x64', cpcfg: '-win_amd64', py_v_maj: 3, py_v_min: 10, desc: 'Windows Visual Studio 2019 x64 Python 3.10' } - { os: windows-2022, shell: cmd, arch: x64, runtime: vc16, cmake_params: '-G "Visual Studio 17" -T v142 -A x64', cpcfg: '-win_amd64', py_v_maj: 3, py_v_min: 9, desc: 'Windows Visual Studio 2019 x64 Python 3.9' } From 47470a9d6527f31a52aea8f7867550e4fafb61e8 Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Sat, 7 Oct 2023 21:47:57 +0200 Subject: [PATCH 135/256] Add macOS 13 support --- .github/workflows/unixmatrix.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/unixmatrix.yml b/.github/workflows/unixmatrix.yml index 86be07424..1e55f8775 100644 --- a/.github/workflows/unixmatrix.yml +++ b/.github/workflows/unixmatrix.yml @@ -42,6 +42,8 @@ jobs: - { os: windows-2019, shell: cmd, arch: x86, runtime: mingw7, cmake_params: '-G "MinGW Makefiles"', cmake_flags: '-fPIC', choco_flags: '--x86', desc: 'Windows MinGW 7.3.0 x86' } - { os: ubuntu-22.04, shell: bash, arch: amd64, runtime: jammy, cmake_flags: '-fPIC', desc: 'Ubuntu 22.04' } - { os: ubuntu-20.04, shell: bash, arch: amd64, runtime: focal, cmake_flags: '-fPIC', desc: 'Ubuntu 20.04' } + - { os: macos-13, shell: bash, arch: arm64, runtime: ventura, cmake_params: '-D CMAKE_SYSTEM_NAME=Darwin -D CMAKE_OSX_ARCHITECTURES=arm64', cmake_flags: '-fPIC', desc: 'macOS Ventura arm64' } + - { os: macos-13, shell: bash, arch: x86_64, runtime: ventura, cmake_params: '-D CMAKE_SYSTEM_NAME=Darwin -D CMAKE_OSX_ARCHITECTURES=x86_64', cmake_flags: '-fPIC', desc: 'macOS Ventura x86_64' } - { os: macos-12, shell: bash, arch: arm64, runtime: monterey, cmake_params: '-D CMAKE_SYSTEM_NAME=Darwin -D CMAKE_OSX_ARCHITECTURES=arm64', cmake_flags: '-fPIC', desc: 'macOS Monterey arm64' } - { os: macos-12, shell: bash, arch: x86_64, runtime: monterey, cmake_params: '-D CMAKE_SYSTEM_NAME=Darwin -D CMAKE_OSX_ARCHITECTURES=x86_64', cmake_flags: '-fPIC', desc: 'macOS Monterey x86_64' } - { os: macos-11, shell: bash, arch: arm64, runtime: bigsur, cmake_params: '-D CMAKE_SYSTEM_NAME=Darwin -D CMAKE_OSX_ARCHITECTURES=arm64', cmake_flags: '-fPIC', desc: 'macOS Big Sur arm64' } From e5bc9ab5a25fb2ff1da7c541d7bdf38cf1366923 Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Sat, 7 Oct 2023 21:55:57 +0200 Subject: [PATCH 136/256] Test specific IBEX version --- .github/workflows/dockerpi.yml | 2 +- .github/workflows/macosmatrix.yml | 2 +- .github/workflows/unixmatrix.yml | 14 +++++++------- .github/workflows/vcmatrix.yml | 6 +++--- scripts/docker/build_pybinding.sh | 2 +- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/.github/workflows/dockerpi.yml b/.github/workflows/dockerpi.yml index 5b4767369..a07c4e506 100644 --- a/.github/workflows/dockerpi.yml +++ b/.github/workflows/dockerpi.yml @@ -16,7 +16,7 @@ jobs: fetch-depth: 0 clean: false - run: docker run --rm --privileged multiarch/qemu-user-static:register --reset - - run: docker run -i -v "${PWD}/..:${PWD}/.." lebarsfa/pi:buster-for-codac /bin/bash -c "uname -a && cat /etc/os-release && cd ${PWD} && pwd && lsb_release -a && sudo apt-get -q update --allow-releaseinfo-change ; sudo apt-get -y install libeigen3-dev ; wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20230510/ibex_armv6hf_buster.zip --no-check-certificate -nv ; unzip -q ibex_armv6hf_buster.zip && rm -Rf ibex_armv6hf_buster.zip && sudo cp -Rf ibex/* /usr/local/ && mkdir build ; cd build && cmake -E env CXXFLAGS="-fPIC" CFLAGS="-fPIC" cmake -D CMAKE_INSTALL_PREFIX=../codac .. && cmake --build . --target install && cd .. && mkdir -p codac_standalone/example ; cd codac_standalone ; wget https://community.chocolatey.org/api/v2/package/eigen/3.4.0 --no-check-certificate -nv ; unzip -q 3.4.0 -d eigen ; rm -Rf 3.4.0 eigen/*.xml eigen/*.nuspec eigen/_* eigen/package eigen/tools ; mkdir -p ibex/include ; mkdir -p ibex/lib ; mkdir -p ibex/share ; mkdir -p ibex/bin ; cp -Rf /usr/local/include/ibex* ibex/include/ ; cp -Rf /usr/local/lib/*ibex* ibex/lib/ ; cp -Rf /usr/local/share/*ibex* ibex/share/ ; cp -Rf /usr/local/share/pkgconfig ibex/share/ ; cp -Rf /usr/local/bin/ibex* ibex/bin/ ; cp -Rf ../codac . ; cp -Rf ../tests/test_codac/* ./example/ ; cd .. ; zip -q -r codac_standalone_armv6hf_buster.zip codac_standalone ; cd codac_standalone/example && cmake . && cmake --build . && ./my_project" + - run: docker run -i -v "${PWD}/..:${PWD}/.." lebarsfa/pi:buster-for-codac /bin/bash -c "uname -a && cat /etc/os-release && cd ${PWD} && pwd && lsb_release -a && sudo apt-get -q update --allow-releaseinfo-change ; sudo apt-get -y install libeigen3-dev ; wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20231007/ibex_armv6hf_buster.zip --no-check-certificate -nv ; unzip -q ibex_armv6hf_buster.zip && rm -Rf ibex_armv6hf_buster.zip && sudo cp -Rf ibex/* /usr/local/ && mkdir build ; cd build && cmake -E env CXXFLAGS="-fPIC" CFLAGS="-fPIC" cmake -D CMAKE_INSTALL_PREFIX=../codac .. && cmake --build . --target install && cd .. && mkdir -p codac_standalone/example ; cd codac_standalone ; wget https://community.chocolatey.org/api/v2/package/eigen/3.4.0 --no-check-certificate -nv ; unzip -q 3.4.0 -d eigen ; rm -Rf 3.4.0 eigen/*.xml eigen/*.nuspec eigen/_* eigen/package eigen/tools ; mkdir -p ibex/include ; mkdir -p ibex/lib ; mkdir -p ibex/share ; mkdir -p ibex/bin ; cp -Rf /usr/local/include/ibex* ibex/include/ ; cp -Rf /usr/local/lib/*ibex* ibex/lib/ ; cp -Rf /usr/local/share/*ibex* ibex/share/ ; cp -Rf /usr/local/share/pkgconfig ibex/share/ ; cp -Rf /usr/local/bin/ibex* ibex/bin/ ; cp -Rf ../codac . ; cp -Rf ../tests/test_codac/* ./example/ ; cd .. ; zip -q -r codac_standalone_armv6hf_buster.zip codac_standalone ; cd codac_standalone/example && cmake . && cmake --build . && ./my_project" - uses: xresloader/upload-to-github-release@v1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/macosmatrix.yml b/.github/workflows/macosmatrix.yml index 9ae62fb2c..c05777c35 100644 --- a/.github/workflows/macosmatrix.yml +++ b/.github/workflows/macosmatrix.yml @@ -57,7 +57,7 @@ jobs: - run: wget https://github.com/Homebrew/homebrew-core/raw/d2267b9f2ad247bc9c8273eb755b39566a474a70/Formula/doxygen.rb ; brew reinstall ./doxygen.rb ; brew pin doxygen ; brew install graphviz ; python -m pip install --upgrade pip ; pip install --upgrade wheel setuptools sphinx breathe sphinx_rtd_theme sphinx-tabs sphinx-issues sphinx-reredirects if: runner.os=='macOS' - run: | - wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20230510/ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip --no-check-certificate -nv + wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20231007/ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip --no-check-certificate -nv unzip -q ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip rm -Rf ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip cp -Rf ibex/* /usr/local/ diff --git a/.github/workflows/unixmatrix.yml b/.github/workflows/unixmatrix.yml index 1e55f8775..2d0dc1479 100644 --- a/.github/workflows/unixmatrix.yml +++ b/.github/workflows/unixmatrix.yml @@ -85,23 +85,23 @@ jobs: - run: | choco install -y -r --no-progress checksum wget zip choco install -y -r --no-progress eigen --version=3.4.0 ${{ matrix.cfg.choco_flags }} - wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20230510/ibex.2.8.9.20230510.nupkg --no-check-certificate -nv - choco install -y -r --no-progress --ignore-dependencies -s . ibex --version=2.8.9.20230510 ${{ matrix.cfg.choco_flags }} --params "'/url:https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20230510/ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip'" - del /f /q ibex.2.8.9.20230510.nupkg + wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20231007/ibex.2.8.9.20231007.nupkg --no-check-certificate -nv + choco install -y -r --no-progress --ignore-dependencies -s . ibex --version=2.8.9.20231007 ${{ matrix.cfg.choco_flags }} --params "'/url:https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20231007/ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip'" + del /f /q ibex.2.8.9.20231007.nupkg if: runner.os=='Windows' - run: | # Replace these 2 lines by the next ones to test a specific binary package of IBEX. #sudo sh -c 'echo "deb [trusted=yes] https://packages.ensta-bretagne.fr/`lsb_release --id -s | tr [:upper:] [:lower:]`/`lsb_release -cs` ./" > /etc/apt/sources.list.d/ensta-bretagne.list' #sudo apt-get -q update ; sudo apt-get -y install libibex-dev libeigen3-dev dpkg-dev || true sudo apt-get -q update ; sudo apt-get -y install libeigen3-dev dpkg-dev || true - wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20230510/libibex-dev-2.8.9.20230510-0${{ matrix.cfg.runtime }}0_${{ matrix.cfg.arch }}.deb --no-check-certificate -nv - sudo dpkg -i libibex-dev-2.8.9.20230510-0${{ matrix.cfg.runtime }}0_${{ matrix.cfg.arch }}.deb - rm -Rf libibex-dev-2.8.9.20230510-0${{ matrix.cfg.runtime }}0_${{ matrix.cfg.arch }}.deb + wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20231007/libibex-dev-2.8.9.20231007-0${{ matrix.cfg.runtime }}0_${{ matrix.cfg.arch }}.deb --no-check-certificate -nv + sudo dpkg -i libibex-dev-2.8.9.20231007-0${{ matrix.cfg.runtime }}0_${{ matrix.cfg.arch }}.deb + rm -Rf libibex-dev-2.8.9.20231007-0${{ matrix.cfg.runtime }}0_${{ matrix.cfg.arch }}.deb shell: bash if: runner.os=='Linux' - run: | brew install eigen - wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20230510/ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip --no-check-certificate -nv + wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20231007/ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip --no-check-certificate -nv unzip -q ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip rm -Rf ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip sudo cp -Rf ibex/* /usr/local/ diff --git a/.github/workflows/vcmatrix.yml b/.github/workflows/vcmatrix.yml index 70a4c18d9..88b6bb5ce 100644 --- a/.github/workflows/vcmatrix.yml +++ b/.github/workflows/vcmatrix.yml @@ -59,9 +59,9 @@ jobs: - run: choco install -y -r --no-progress doxygen.install --version=1.9.6 & choco install -y -r --no-progress graphviz & pip install --upgrade wheel setuptools sphinx breathe sphinx-issues sphinx-tabs sphinx_rtd_theme sphinx-reredirects if: runner.os=='Windows' - run: | - wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20230510/ibex.2.8.9.20230510.nupkg --no-check-certificate -nv - choco install -y -r --no-progress --ignore-dependencies -s . ibex --version=2.8.9.20230510 ${{ matrix.cfg.choco_flags }} --params "'/url:https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20230510/ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip'" - del /f /q ibex.2.8.9.20230510.nupkg + wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20231007/ibex.2.8.9.20231007.nupkg --no-check-certificate -nv + choco install -y -r --no-progress --ignore-dependencies -s . ibex --version=2.8.9.20231007 ${{ matrix.cfg.choco_flags }} --params "'/url:https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20231007/ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip'" + del /f /q ibex.2.8.9.20231007.nupkg - run: | mkdir build ; cd build cmake -E env CXXFLAGS=" /wd4267 /wd4244 /wd4305 /wd4996" CFLAGS=" /wd4267 /wd4244 /wd4305 /wd4996" cmake ${{ matrix.cfg.cmake_params }} -D CMAKE_INSTALL_PREFIX="../codac" -D WITH_PYTHON=ON .. diff --git a/scripts/docker/build_pybinding.sh b/scripts/docker/build_pybinding.sh index db1526b54..292d9e0b3 100755 --- a/scripts/docker/build_pybinding.sh +++ b/scripts/docker/build_pybinding.sh @@ -2,7 +2,7 @@ set -e -x -wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20230510/ibex_x86_64_manylinux2010.zip --no-check-certificate -nv +wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20231007/ibex_x86_64_manylinux2010.zip --no-check-certificate -nv unzip -q ibex_x86_64_manylinux2010.zip rm -Rf ibex_x86_64_manylinux2010.zip sudo cp -Rf ibex/* /usr/local/ From 1f5a4b9b1ec786ba518a93f38e92bb5464423edd Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Sun, 8 Oct 2023 16:56:08 +0200 Subject: [PATCH 137/256] numpy>=1.25 on macOS need to be imported before codac due to specific checks related to floating point (however python -m unittest discover codac.tests probably imports codac first anyway) --- .github/workflows/macosmatrix.yml | 3 +-- python/codac/tests/test_actions.py | 5 ++++- .../codac/tests/tests_from_pyibex/test_image.py | 16 ++++++++-------- .../tests/tests_from_pyibex/test_thickImage.py | 11 +++++------ 4 files changed, 18 insertions(+), 17 deletions(-) diff --git a/.github/workflows/macosmatrix.yml b/.github/workflows/macosmatrix.yml index c05777c35..ee8a629a7 100644 --- a/.github/workflows/macosmatrix.yml +++ b/.github/workflows/macosmatrix.yml @@ -69,8 +69,7 @@ jobs: cmake --build . --config Release --target pip_package ; cp `ls *.whl` ../`ls *.whl | sed "s/py3-none-any/cp${{ matrix.cfg.py_v_maj }}${{ matrix.cfg.py_v_min }}-cp${{ matrix.cfg.py_v_maj }}${{ matrix.cfg.py_v_min }}${{ matrix.cfg.cpcfg }}/"` cd .. shell: bash - # numpy v1.25.0 causes issues... - - run: pip install *.whl ; python -c "import sys; print(sys.version)" ; python examples/tuto/01_getting_started/01_getting_started.py ; pip install "numpy<1.25" ; python -m unittest discover codac.tests + - run: pip install *.whl ; python -c "import sys; print(sys.version)" ; python examples/tuto/01_getting_started/01_getting_started.py ; pip install numpy ; python -m unittest discover codac.tests shell: bash if: (runner.os=='macOS')&&(matrix.cfg.arch=='x86_64') - uses: xresloader/upload-to-github-release@v1 diff --git a/python/codac/tests/test_actions.py b/python/codac/tests/test_actions.py index 426bc1ac7..cca74b28f 100644 --- a/python/codac/tests/test_actions.py +++ b/python/codac/tests/test_actions.py @@ -9,9 +9,12 @@ # the GNU Lesser General Public License (LGPL). import unittest +try: + import numpy as np +except: + print("NUMPY UNAVAILABLE") from codac import * from codac.codac2 import * -import numpy as np class TestActions(unittest.TestCase): diff --git a/python/codac/tests/tests_from_pyibex/test_image.py b/python/codac/tests/tests_from_pyibex/test_image.py index f39efb29a..72757eeb9 100644 --- a/python/codac/tests/tests_from_pyibex/test_image.py +++ b/python/codac/tests/tests_from_pyibex/test_image.py @@ -10,15 +10,15 @@ # the GNU Lesser General Public License (LGPL). import unittest -from codac import * -from codac.unsupported import * -import sys try: import numpy as np has_np = True -except ImportError: - print("NUMPY NOT FOUND") +except: + print("NUMPY UNAVAILABLE") has_np = False +from codac import * +from codac.unsupported import * +import sys # Interval.__str__=lambda x: "("+str(x[0])+', '+str(x[1])+")" # Interval.__repr__=lambda x: "("+x[0]+', '+x[1]+")" @@ -51,7 +51,7 @@ def test_points_upperLeft(self): IntervalVector([[-5, -4], [4, 5]])) -@unittest.skipUnless(has_np, "Numpy not found") +@unittest.skipUnless(has_np, "Numpy unavailable") class TestImage(unittest.TestCase): def test_world_to_grid(self): @@ -85,7 +85,7 @@ def test_1(self): # print(im.pixelAt(i,j), end=' ') # print('') -@unittest.skipUnless(has_np, "Numpy not found") +@unittest.skipUnless(has_np, "Numpy unavailable") class TestWorldToGrid(unittest.TestCase): def setUp(self): @@ -142,7 +142,7 @@ def test_boundingBox(self): # X0 = IntervalVector([[-7, -6], [0,1]]) # self.assertEqual(self.im.test(X0), MAYBE) -@unittest.skipUnless(has_np, "Numpy not found") +@unittest.skipUnless(has_np, "Numpy unavailable") class TestCtcRaster(unittest.TestCase): def setUp(self): diff --git a/python/codac/tests/tests_from_pyibex/test_thickImage.py b/python/codac/tests/tests_from_pyibex/test_thickImage.py index f18f582e6..ec459a770 100644 --- a/python/codac/tests/tests_from_pyibex/test_thickImage.py +++ b/python/codac/tests/tests_from_pyibex/test_thickImage.py @@ -10,17 +10,16 @@ # the GNU Lesser General Public License (LGPL). import unittest -from codac import * -from codac.unsupported import * - try: import numpy as np has_np = True -except ImportError: - print("NUMPY NOT FOUND") +except: + print("NUMPY UNAVAILABLE") has_np = False +from codac import * +from codac.unsupported import * -@unittest.skipUnless(has_np, "Numpy not found") +@unittest.skipUnless(has_np, "Numpy unavailable") class TestThickImage(unittest.TestCase): def setUp(self): From 2cdee1c4c2a3509036be4f2b389964b38edad68d Mon Sep 17 00:00:00 2001 From: SimonRohou Date: Tue, 10 Oct 2023 18:16:29 +0200 Subject: [PATCH 138/256] [codac2] added operators --- python/src/core/cn/codac_py_ContractorNetwork.cpp | 2 +- .../src/core/contractors/dyn/codac_py_CtcLohner.cpp | 4 ++-- src/core/2/domains/interval/codac2_IntervalMatrix.h | 11 +++++++++++ src/core/2/variables/codac2_Matrix.h | 5 +++++ src/core/2/variables/codac2_Vector.h | 5 +++++ 5 files changed, 24 insertions(+), 3 deletions(-) diff --git a/python/src/core/cn/codac_py_ContractorNetwork.cpp b/python/src/core/cn/codac_py_ContractorNetwork.cpp index 6eda95f97..51495f2bb 100644 --- a/python/src/core/cn/codac_py_ContractorNetwork.cpp +++ b/python/src/core/cn/codac_py_ContractorNetwork.cpp @@ -243,7 +243,7 @@ void export_ContractorNetwork(py::module& m) .def("contract", [](ContractorNetwork& cn, py::dict var_dom, bool verbose) { - cn.contract(pydict_to_unorderedmapdomains(var_dom), verbose); + return cn.contract(pydict_to_unorderedmapdomains(var_dom), verbose); }, CONTRACTORNETWORK_DOUBLE_CONTRACT_UNORDEREDMAPDOMAINDOMAIN_BOOL, "var_dom"_a, "verbose"_a=false) diff --git a/python/src/core/contractors/dyn/codac_py_CtcLohner.cpp b/python/src/core/contractors/dyn/codac_py_CtcLohner.cpp index 89a5b1358..889de3edb 100644 --- a/python/src/core/contractors/dyn/codac_py_CtcLohner.cpp +++ b/python/src/core/contractors/dyn/codac_py_CtcLohner.cpp @@ -28,8 +28,8 @@ using namespace pybind11::literals; void export_CtcLohner(py::module& m, py::class_& dyn_ctc) { - py::class_ ctc_picard(m, "CtcLohner", dyn_ctc, CTCLOHNER_MAIN); - ctc_picard + py::class_ ctc_lohner(m, "CtcLohner", dyn_ctc, CTCLOHNER_MAIN); + ctc_lohner .def(py::init(), CTCLOHNER_CTCLOHNER_FUNCTION_INT_DOUBLE, diff --git a/src/core/2/domains/interval/codac2_IntervalMatrix.h b/src/core/2/domains/interval/codac2_IntervalMatrix.h index 1f0692ad3..fc2121e15 100644 --- a/src/core/2/domains/interval/codac2_IntervalMatrix.h +++ b/src/core/2/domains/interval/codac2_IntervalMatrix.h @@ -149,6 +149,11 @@ namespace codac2 return IntervalMatrix_(nb_rows, nb_cols, Interval::empty_set()); } + static IntervalMatrix_ eye() + { + return Eigen::Matrix::Identity(); + } + bool is_flat() const { if(is_empty()) return true; @@ -572,6 +577,12 @@ namespace codac2 return y -= x; } + template + auto operator*(double a, const IntervalMatrix_& x) + { + return Interval(a)*x; + } + class IntervalMatrix : public IntervalMatrix_<> { public: diff --git a/src/core/2/variables/codac2_Matrix.h b/src/core/2/variables/codac2_Matrix.h index af0dc3951..3cf9c142d 100644 --- a/src/core/2/variables/codac2_Matrix.h +++ b/src/core/2/variables/codac2_Matrix.h @@ -129,6 +129,11 @@ namespace codac2 return y -= x; } + auto operator-() const + { + return Eigen::Matrix::operator-(); + } + auto operator&(const Matrix_& x) const { auto y = *this; diff --git a/src/core/2/variables/codac2_Vector.h b/src/core/2/variables/codac2_Vector.h index 4c23ed330..fe9bb8476 100644 --- a/src/core/2/variables/codac2_Vector.h +++ b/src/core/2/variables/codac2_Vector.h @@ -90,6 +90,11 @@ namespace codac2 return Matrix_(Eigen::Matrix(this->asDiagonal())); } + Matrix_<1,N> transpose() const + { + return Matrix_<1,N>(Eigen::Matrix::transpose()); + } + // todo: place this in common inheritance with IntervalVector_ template Vector_ subvector() const From 4dabfb6d706ad5c82cb76de59ee0e4eb48480676 Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Wed, 11 Oct 2023 11:36:51 +0200 Subject: [PATCH 139/256] Potential bug for IntervalVector += in Python --- python/src/core/domains/interval/codac_py_IntervalVector.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/src/core/domains/interval/codac_py_IntervalVector.cpp b/python/src/core/domains/interval/codac_py_IntervalVector.cpp index d14372136..31fd80d1f 100644 --- a/python/src/core/domains/interval/codac_py_IntervalVector.cpp +++ b/python/src/core/domains/interval/codac_py_IntervalVector.cpp @@ -260,7 +260,7 @@ void export_IntervalVector(py::module& m) .def(py::self |= py::self) .def("__add__", [](IntervalVector& a, const Vector& x) { return a+x; }) - .def("__iadd__", [](IntervalVector& a, const Vector& x) { return a+x; }) + .def("__iadd__", [](IntervalVector& a, const Vector& x) { return a+=x; }) .def("__radd__", [](IntervalVector& a, const Vector& x) { return a+x; }) .def("__sub__", [](IntervalVector& a, const Vector& x) { return a-x; }) From 54966a6be0ddd6fafe64bd90fe6401822ba49587 Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Tue, 10 Oct 2023 22:53:37 +0200 Subject: [PATCH 140/256] Workarounds of unsupported operators or special functions for MATLAB compatibility, see https://fr.mathworks.com/help/matlab/matlab_external/differences-between-matlab-python.html --- .../domains/interval/codac_py_Interval.cpp | 34 +++++++++++- .../interval/codac_py_IntervalMatrix.cpp | 2 + .../interval/codac_py_IntervalVector.cpp | 12 +++++ .../src/core/domains/tube/codac_py_Tube.cpp | 52 +++++++++++++++++++ .../core/domains/tube/codac_py_TubeVector.cpp | 44 ++++++++++++++++ 5 files changed, 143 insertions(+), 1 deletion(-) diff --git a/python/src/core/domains/interval/codac_py_Interval.cpp b/python/src/core/domains/interval/codac_py_Interval.cpp index ce7b3511c..616e82d9d 100644 --- a/python/src/core/domains/interval/codac_py_Interval.cpp +++ b/python/src/core/domains/interval/codac_py_Interval.cpp @@ -134,7 +134,15 @@ void export_Interval(py::module& m) .def(py::self * py::self) .def(py::self / py::self) .def(py::self & py::self) + // For MATLAB compatibility. + //.def_static("inter", [](const Interval& x, const Interval& y) { return x&y; }) + // For MATLAB compatibility. + .def("inter", [](const Interval& s, const Interval& y) { return s&y; }) .def(py::self | py::self) + // For MATLAB compatibility. + //.def_static("union", [](const Interval& x, const Interval& y) { return x|y; }) + // For MATLAB compatibility. + .def("union", [](const Interval& s, const Interval& y) { return s|y; }) .def("__iadd__", [](Interval& x, Interval& o) { return x += o; }) .def("__isub__", [](Interval& x, Interval& o) { return x -= o; }) @@ -143,7 +151,11 @@ void export_Interval(py::module& m) .def("__ifloordiv__", [](Interval& x, Interval& o) { return x /= o; }) .def(py::self &= py::self) + // For MATLAB compatibility. + .def("inter_self", [](Interval& s, const Interval& a) { return s&=a; }) .def(py::self |= py::self) + // For MATLAB compatibility. + .def("union_self", [](Interval& s, const Interval& a) { return s|=a; }) .def(py::self + double()) .def(py::self += double()) @@ -160,9 +172,25 @@ void export_Interval(py::module& m) .def(-py::self) .def("__abs__", [](const Interval& a) { return ibex::abs(a); }) + // For MATLAB compatibility. + //.def_static("abs", [](const Interval& a) { return ibex::abs(a); }) + // For MATLAB compatibility. + .def("abs", [](const Interval& s) { return ibex::abs(s); }) .def("__pow__", [](const Interval& x, int n) { return ibex::pow(x, n); }) + // For MATLAB compatibility. + //.def_static("pow", [](const Interval& x, int n) { return ibex::pow(x, n); }) + // For MATLAB compatibility. + .def("pow", [](const Interval& s, int n) { return ibex::pow(s, n); }) .def("__pow__", [](const Interval& x, double d) { return ibex::pow(x, d); }) - .def("__pow__", [](const Interval &x, const Interval &y) { return ibex::pow(x, y); }) + // For MATLAB compatibility. + //.def_static("pow", [](const Interval& x, double d) { return ibex::pow(x, d); }) + // For MATLAB compatibility. + .def("pow", [](const Interval& s, double d) { return ibex::pow(s, d); }) + .def("__pow__", [](const Interval& x, const Interval& y) { return ibex::pow(x, y); }) + // For MATLAB compatibility. + //.def_static("pow", [](const Interval& x, const Interval& y) { return ibex::pow(x, y); }) + // For MATLAB compatibility. + .def("pow", [](const Interval& s, const Interval& y) { return ibex::pow(s, y); }) .def("lb", &Interval::lb, "return the upper bound") .def("ub", &Interval::ub, "return the lower bound") @@ -212,8 +240,12 @@ void export_Interval(py::module& m) .def("bisect", &Interval::bisect, DOCS_INTERVAL_BISECT, py::arg("ratio")=0.5) .def("__get_item__", get_item, "x[0] returns the lb and x[1] returns ub") + // For MATLAB compatibility. + .def_static("get_item", [](Interval& x, size_t i) { return get_item(x, i); }) .def("copy", &interval_copy, "return a new object which is the copy of x") .def("__hash__", [](const Interval& s1) { return reinterpret_cast(&s1); }) + // For MATLAB compatibility. + .def("hash", [](const Interval& s1) { return reinterpret_cast(&s1); }) //.def( "__pow__", pow__) // Constants diff --git a/python/src/core/domains/interval/codac_py_IntervalMatrix.cpp b/python/src/core/domains/interval/codac_py_IntervalMatrix.cpp index e7aac5f25..abac3d269 100644 --- a/python/src/core/domains/interval/codac_py_IntervalMatrix.cpp +++ b/python/src/core/domains/interval/codac_py_IntervalMatrix.cpp @@ -132,6 +132,8 @@ void export_IntervalMatrix(py::module& m) .def(py::self - py::self) .def(py::self * py::self) .def(py::self &= py::self) + // For MATLAB compatibility. + .def("inter_self", [](IntervalMatrix& s, const IntervalMatrix& a) { return s&=a; }) .def(-py::self) .def(py::self += py::self) // todo: .def(py::self += other()) diff --git a/python/src/core/domains/interval/codac_py_IntervalVector.cpp b/python/src/core/domains/interval/codac_py_IntervalVector.cpp index 31fd80d1f..1d7670dc6 100644 --- a/python/src/core/domains/interval/codac_py_IntervalVector.cpp +++ b/python/src/core/domains/interval/codac_py_IntervalVector.cpp @@ -243,7 +243,15 @@ void export_IntervalVector(py::module& m) .def(py::self - py::self) .def(py::self * py::self) .def(py::self & py::self) + // For MATLAB compatibility. + //.def_static("inter", [](const IntervalVector& x, const IntervalVector& y) { return x&y; }) + // For MATLAB compatibility. + .def("inter", [](const IntervalVector& s, const IntervalVector& y) { return s&y; }) .def(py::self | py::self) + // For MATLAB compatibility. + //.def_static("union", [](const IntervalVector& x, const IntervalVector& y) { return x|y; }) + // For MATLAB compatibility. + .def("union", [](const IntervalVector& s, const IntervalVector& y) { return s|y; }) .def(-py::self) .def(py::self += py::self) @@ -257,7 +265,11 @@ void export_IntervalVector(py::module& m) .def("__rmul__", [](IntervalVector& a, const Interval& x) { return x*a; }) .def(py::self &= py::self) + // For MATLAB compatibility. + .def("inter_self", [](IntervalVector& s, const IntervalVector& a) { return s&=a; }) .def(py::self |= py::self) + // For MATLAB compatibility. + .def("union_self", [](IntervalVector& s, const IntervalVector& a) { return s|=a; }) .def("__add__", [](IntervalVector& a, const Vector& x) { return a+x; }) .def("__iadd__", [](IntervalVector& a, const Vector& x) { return a+=x; }) diff --git a/python/src/core/domains/tube/codac_py_Tube.cpp b/python/src/core/domains/tube/codac_py_Tube.cpp index 9bff4710f..8892ebd9b 100644 --- a/python/src/core/domains/tube/codac_py_Tube.cpp +++ b/python/src/core/domains/tube/codac_py_Tube.cpp @@ -370,21 +370,45 @@ void export_Tube(py::module& m) .def("__ior__", [](Tube& s,const Interval& o) { return s |= o; }, TUBE_CONSTTUBE_OPERATORUNIEQ_INTERVAL) + // For MATLAB compatibility. + .def("union_self", [](Tube& s,const Interval& o) { return s |= o; }, + TUBE_CONSTTUBE_OPERATORUNIEQ_INTERVAL) + .def("__ior__", [](Tube& s,const Trajectory& o) { return s |= o; }, TUBE_CONSTTUBE_OPERATORUNIEQ_TRAJECTORY) + // For MATLAB compatibility. + .def("union_self", [](Tube& s,const Trajectory& o) { return s |= o; }, + TUBE_CONSTTUBE_OPERATORUNIEQ_TRAJECTORY) + .def("__ior__", [](Tube& s,const Tube& o) { return s |= o; }, TUBE_CONSTTUBE_OPERATORUNIEQ_TUBE) + // For MATLAB compatibility. + .def("union_self", [](Tube& s,const Tube& o) { return s |= o; }, + TUBE_CONSTTUBE_OPERATORUNIEQ_TUBE) + .def("__iand__", [](Tube& s,const Interval& o) { return s &= o; }, TUBE_CONSTTUBE_OPERATORINTEQ_INTERVAL) + // For MATLAB compatibility. + .def("inter_self", [](Tube& s,const Interval& o) { return s &= o; }, + TUBE_CONSTTUBE_OPERATORINTEQ_INTERVAL) + .def("__iand__", [](Tube& s,const Trajectory& o) { return s &= o; }, TUBE_CONSTTUBE_OPERATORINTEQ_TRAJECTORY) + // For MATLAB compatibility. + .def("inter_self", [](Tube& s,const Trajectory& o) { return s &= o; }, + TUBE_CONSTTUBE_OPERATORINTEQ_TRAJECTORY) + .def("__iand__", [](Tube& s,const Tube& o) { return s &= o; }, TUBE_CONSTTUBE_OPERATORINTEQ_TUBE) + // For MATLAB compatibility. + .def("inter_self", [](Tube& s,const Tube& o) { return s &= o; }, + TUBE_CONSTTUBE_OPERATORINTEQ_TUBE) + // String .def("class_name", &Tube::class_name, @@ -493,21 +517,49 @@ void export_Tube(py::module& m) .def("__rtruediv__", [](const Tube& y, const TubeVector& x) { return x/y; }) .def("__or__", [](const Tube& x, const Tube& y) { return x|y; }) + // For MATLAB compatibility. + .def("union", [](const Tube& x, const Tube& y) { return x|y; }) .def("__or__", [](const Tube& x, double y) { return x|y; }) + // For MATLAB compatibility. + .def("union", [](const Tube& x, double y) { return x|y; }) .def("__or__", [](const Tube& x, const Interval& y) { return x|y; }) + // For MATLAB compatibility. + .def("union", [](const Tube& x, const Interval& y) { return x|y; }) .def("__or__", [](const Tube& x, const Trajectory& y) { return x|y; }) + // For MATLAB compatibility. + .def("union", [](const Tube& x, const Trajectory& y) { return x|y; }) .def("__ror__", [](const Tube& y, double x) { return x|y; }) + // For MATLAB compatibility. + .def("union", [](const Tube& y, double x) { return x|y; }) .def("__ror__", [](const Tube& y, const Interval& x) { return x|y; }) + // For MATLAB compatibility. + .def("union", [](const Tube& y, const Interval& x) { return x|y; }) .def("__ror__", [](const Tube& y, const Trajectory& x) { return x|y; }) + // For MATLAB compatibility. + .def("union", [](const Tube& y, const Trajectory& x) { return x|y; }) .def("__and__", [](const Tube& x, const Tube& y) { return x&y; }) + // For MATLAB compatibility. + .def("inter", [](const Tube& x, const Tube& y) { return x&y; }) .def("__and__", [](const Tube& x, double y) { return x&y; }) + // For MATLAB compatibility. + .def("inter", [](const Tube& x, double y) { return x&y; }) .def("__and__", [](const Tube& x, const Interval& y) { return x&y; }) + // For MATLAB compatibility. + .def("inter", [](const Tube& x, const Interval& y) { return x&y; }) .def("__and__", [](const Tube& x, const Trajectory& y) { return x&y; }) + // For MATLAB compatibility. + .def("inter", [](const Tube& x, const Trajectory& y) { return x&y; }) .def("__rand__", [](const Tube& y, double x) { return x&y; }) + // For MATLAB compatibility. + .def("inter", [](const Tube& y, double x) { return x&y; }) .def("__rand__", [](const Tube& y, const Interval& x) { return x&y; }) + // For MATLAB compatibility. + .def("inter", [](const Tube& y, const Interval& x) { return x&y; }) .def("__rand__", [](const Tube& y, const Trajectory& x) { return x&y; }) + // For MATLAB compatibility. + .def("inter", [](const Tube& y, const Trajectory& x) { return x&y; }) ; } \ No newline at end of file diff --git a/python/src/core/domains/tube/codac_py_TubeVector.cpp b/python/src/core/domains/tube/codac_py_TubeVector.cpp index aca0dd891..720237e8a 100644 --- a/python/src/core/domains/tube/codac_py_TubeVector.cpp +++ b/python/src/core/domains/tube/codac_py_TubeVector.cpp @@ -394,21 +394,45 @@ void export_TubeVector(py::module& m) .def("__ior__", [](TubeVector& s,const IntervalVector& o) { return s |= o;}, TUBEVECTOR_CONSTTUBEVECTOR_OPERATORUNIEQ_INTERVALVECTOR) + // For MATLAB compatibility. + .def("union_self", [](TubeVector& s,const IntervalVector& o) { return s |= o;}, + TUBEVECTOR_CONSTTUBEVECTOR_OPERATORUNIEQ_INTERVALVECTOR) + .def("__ior__", [](TubeVector& s,const TrajectoryVector& o) { return s |= o;}, TUBEVECTOR_CONSTTUBEVECTOR_OPERATORUNIEQ_TRAJECTORYVECTOR) + // For MATLAB compatibility. + .def("union_self", [](TubeVector& s,const TrajectoryVector& o) { return s |= o;}, + TUBEVECTOR_CONSTTUBEVECTOR_OPERATORUNIEQ_TRAJECTORYVECTOR) + .def("__ior__", [](TubeVector& s,const TubeVector& o) { return s |= o;}, TUBEVECTOR_CONSTTUBEVECTOR_OPERATORUNIEQ_TUBEVECTOR) + // For MATLAB compatibility. + .def("union_self", [](TubeVector& s,const TubeVector& o) { return s |= o;}, + TUBEVECTOR_CONSTTUBEVECTOR_OPERATORUNIEQ_TUBEVECTOR) + .def("__iand__", [](TubeVector& s,const IntervalVector& o) { return s &= o;}, TUBEVECTOR_CONSTTUBEVECTOR_OPERATORINTEQ_INTERVALVECTOR) + // For MATLAB compatibility. + .def("inter_self", [](TubeVector& s,const IntervalVector& o) { return s &= o;}, + TUBEVECTOR_CONSTTUBEVECTOR_OPERATORINTEQ_INTERVALVECTOR) + .def("__iand__", [](TubeVector& s,const TrajectoryVector& o) { return s &= o;}, TUBEVECTOR_CONSTTUBEVECTOR_OPERATORINTEQ_TRAJECTORYVECTOR) + // For MATLAB compatibility. + .def("inter_self", [](TubeVector& s,const TrajectoryVector& o) { return s &= o;}, + TUBEVECTOR_CONSTTUBEVECTOR_OPERATORINTEQ_TRAJECTORYVECTOR) + .def("__iand__", [](TubeVector& s,const TubeVector& o) { return s &= o;}, TUBEVECTOR_CONSTTUBEVECTOR_OPERATORINTEQ_TUBEVECTOR) + // For MATLAB compatibility. + .def("inter_self", [](TubeVector& s,const TubeVector& o) { return s &= o;}, + TUBEVECTOR_CONSTTUBEVECTOR_OPERATORINTEQ_TUBEVECTOR) + // String .def("class_name", &TubeVector::class_name, @@ -568,17 +592,37 @@ void export_TubeVector(py::module& m) // todo .def("__truediv__", [](const IntervalVector& x, const Tube& y); .def("__or__", [](const TubeVector& x, const TubeVector& y) { return x|y; }) + // For MATLAB compatibility. + .def("union", [](const TubeVector& x, const TubeVector& y) { return x|y; }) .def("__or__", [](const TubeVector& x, const IntervalVector& y) { return x|y; }) + // For MATLAB compatibility. + .def("union", [](const TubeVector& x, const IntervalVector& y) { return x|y; }) .def("__or__", [](const TubeVector& x, const TrajectoryVector& y) { return x|y; }) + // For MATLAB compatibility. + .def("union", [](const TubeVector& x, const TrajectoryVector& y) { return x|y; }) .def("__ror__", [](const TubeVector& y, const IntervalVector& x) { return x|y; }) + // For MATLAB compatibility. + .def("union", [](const TubeVector& y, const IntervalVector& x) { return x|y; }) .def("__ror__", [](const TubeVector& y, const TrajectoryVector& x) { return x|y; }) + // For MATLAB compatibility. + .def("union", [](const TubeVector& y, const TrajectoryVector& x) { return x|y; }) .def("__and__", [](const TubeVector& x, const TubeVector& y) { return x&y; }) + // For MATLAB compatibility. + .def("inter", [](const TubeVector& x, const TubeVector& y) { return x&y; }) .def("__and__", [](const TubeVector& x, const IntervalVector& y) { return x&y; }) + // For MATLAB compatibility. + .def("inter", [](const TubeVector& x, const IntervalVector& y) { return x&y; }) .def("__and__", [](const TubeVector& x, const TrajectoryVector& y) { return x&y; }) + // For MATLAB compatibility. + .def("inter", [](const TubeVector& x, const TrajectoryVector& y) { return x&y; }) .def("__rand__", [](const TubeVector& y, const IntervalVector& x) { return x&y; }) + // For MATLAB compatibility. + .def("inter", [](const TubeVector& y, const IntervalVector& x) { return x&y; }) .def("__rand__", [](const TubeVector& y, const TrajectoryVector& x) { return x&y; }) + // For MATLAB compatibility. + .def("inter", [](const TubeVector& y, const TrajectoryVector& x) { return x&y; }) ; } \ No newline at end of file From d8a498e8fd2b884020ae3e9b7847fb5d9ae4a3d2 Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Tue, 10 Oct 2023 23:25:04 +0200 Subject: [PATCH 141/256] Update examples --- .../01_getting_started/01_getting_started.py | 2 +- .../01_getting_started/a01_getting_started.m | 5 +- .../a03_static_rangebearing.m | 86 ++++++++++++++ .../05_dyn_rangebearing.py | 2 +- .../a05_dyn_rangebearing.m | 111 ++++++++++++++++++ 5 files changed, 201 insertions(+), 5 deletions(-) create mode 100644 examples/tuto/03_static_rangebearing/a03_static_rangebearing.m create mode 100644 examples/tuto/05_dyn_rangebearing/a05_dyn_rangebearing.m diff --git a/examples/tuto/01_getting_started/01_getting_started.py b/examples/tuto/01_getting_started/01_getting_started.py index f6f1c5379..692375f1f 100644 --- a/examples/tuto/01_getting_started/01_getting_started.py +++ b/examples/tuto/01_getting_started/01_getting_started.py @@ -1,5 +1,5 @@ # Codac - Examples -# Dynamic range-only localization +# Getting started: 2 minutes to Codac # ---------------------------------------------------------------------------- from codac import * diff --git a/examples/tuto/01_getting_started/a01_getting_started.m b/examples/tuto/01_getting_started/a01_getting_started.m index 030702085..e593481f9 100644 --- a/examples/tuto/01_getting_started/a01_getting_started.m +++ b/examples/tuto/01_getting_started/a01_getting_started.m @@ -1,5 +1,5 @@ % Codac - Examples -% Dynamic range-only localization +% Getting started: 2 minutes to Codac % ---------------------------------------------------------------------------- import py.codac.* @@ -87,5 +87,4 @@ % Checking if this example still works: -if x.volume() < 5; check = true; else; check = false; end % todo: x.contains(x_truth) -assert(check) +assert(x.volume() < 5) % todo: x.contains(x_truth) diff --git a/examples/tuto/03_static_rangebearing/a03_static_rangebearing.m b/examples/tuto/03_static_rangebearing/a03_static_rangebearing.m new file mode 100644 index 000000000..95abb361c --- /dev/null +++ b/examples/tuto/03_static_rangebearing/a03_static_rangebearing.m @@ -0,0 +1,86 @@ +% Codac - Examples +% Static range-bearing localization +% ---------------------------------------------------------------------------- + +import py.codac.* + +% =================== 0. Parameters, truth and data ==================== + +% Truth (unknown pose) +x_truth = [0,0,pi/6]; % (x,y,heading) + +% Creating random map of landmarks +map_area = IntervalVector(int32(2), [-8,8]); +v_map = DataLoader().generate_landmarks_boxes(map_area, int32(1)); + +% The following function generates a set of [range]x[bearing] values +v_obs = DataLoader().generate_static_observations(py.list(x_truth), v_map, false); + +% Adding uncertainties on the measurements +for i=1:length(v_obs) % for each observation: + v_obs{i}.getitem(int32(0)).inflate(0.3); % range + v_obs{i}.getitem(int32(1)).inflate(0.1); % bearing +end + + +% =============== 1. Defining domains for our variables ================ + +x = IntervalVector(int32(2)); % unknown position +heading = Interval(x_truth(3)).inflate(0.01); % measured heading + + +% =========== 2. Defining contractors to deal with equations =========== + +ctc_plus = CtcFunction(Function('a', 'b', 'c', 'a+b-c')); % a+b=c +ctc_minus = CtcFunction(Function('a', 'b', 'c', 'a-b-c')); % a-b=c +% We also use the predefined contractor CtcPolar(), no need to build it + + +% =============== 3. Adding the contractors to a network =============== + +cn = ContractorNetwork(); + +for i=1:length(v_obs) + + % Intermediate variables + alpha = cn.create_interm_var(Interval()); + d = cn.create_interm_var(IntervalVector(int32(2))); + + cn.add(ctc_plus, py.list({v_obs{i}.getitem(int32(1)), heading, alpha})); + cn.add(ctc_minus, py.list({v_map{i}, x, d})); + cn.add(CtcPolar(), py.list({d, v_obs{i}.getitem(int32(0)), alpha})); +end + + +% ======================= 4. Solving the problem ======================= + +cn.contract(); + + +% ============================ 5. Graphics ============================= + +beginDrawing(); + +fig = VIBesFigMap('Map'); +fig.set_properties(int32(50), int32(50), int32(600), int32(600)); + +for i=1:length(v_map) + iv = v_map{i}; + fig.add_beacon(iv.mid(), 0.2); +end + +for i=1:length(v_obs) + y = v_obs{i}; + fig.draw_pie(x_truth(1), x_truth(2), y.getitem(int32(0)).union(Interval(0)), heading+y.getitem(int32(1)), 'lightGray'); + fig.draw_pie(x_truth(1), x_truth(2), y.getitem(int32(0)), heading+y.getitem(int32(1)), 'gray'); +end + +fig.draw_vehicle(py.list(x_truth),0.5); +fig.draw_box(x); % estimated position +fig.show(); + +endDrawing(); + + +% Checking if this example still works: +assert(x.contains(py.list(x_truth(1:2)))) diff --git a/examples/tuto/05_dyn_rangebearing/05_dyn_rangebearing.py b/examples/tuto/05_dyn_rangebearing/05_dyn_rangebearing.py index 0104b19e4..3e4256fa7 100644 --- a/examples/tuto/05_dyn_rangebearing/05_dyn_rangebearing.py +++ b/examples/tuto/05_dyn_rangebearing/05_dyn_rangebearing.py @@ -1,5 +1,5 @@ # Codac - Examples -# Dynamic range-only localization +# Dynamic range-bearing localization # ---------------------------------------------------------------------------- from codac import * diff --git a/examples/tuto/05_dyn_rangebearing/a05_dyn_rangebearing.m b/examples/tuto/05_dyn_rangebearing/a05_dyn_rangebearing.m new file mode 100644 index 000000000..1bf058759 --- /dev/null +++ b/examples/tuto/05_dyn_rangebearing/a05_dyn_rangebearing.m @@ -0,0 +1,111 @@ +% Codac - Examples +% Dynamic range-bearing localization +% ---------------------------------------------------------------------------- + +import py.codac.* + + +% =================== 0. Parameters, truth and data ==================== + +dt = 0.05; % timestep for tubes accuracy +tdomain = Interval(0,3); % temporal limits [t_0,t_f]=[0,3] + +x_truth = TrajectoryVector(tdomain, TFunction(['(' ... + '10*cos(t)+t ;' ... + '5*sin(2*t)+t ;' ... + 'atan2((10*cos(2*t)+1),(-10*sin(t)+1)) ;' ... + 'sqrt((-10*sin(t)+1)^2+(10*cos(2*t)+1)^2))'])); % actual trajectory + +% Continuous measurements coming from the truth +measured_psi = x_truth.getitem(int32(2)).sample(dt).make_continuous(); +measured_psi = measured_psi + RandTrajectory(tdomain, dt, Interval(-0.01,0.01)); % adding some noise +measured_speed = x_truth.getitem(int32(3)).sample(dt); +measured_speed = measured_speed + RandTrajectory(tdomain, dt, Interval(-0.01,0.01)); % adding some noise + +% Creating random map of landmarks +map_area = IntervalVector(int32(2), [-8,8]); +v_map = DataLoader().generate_landmarks_boxes(map_area, int32(30)); + +% The following function generates a set of [range]x[bearing] values +v_obs = DataLoader().generate_observations(x_truth, v_map, int32(10)); + +% Adding uncertainties on the measurements +for i=1:length(v_obs) % for each observation: + obs = v_obs{i}; + obs.getitem(int32(1)).inflate(0.3); % range + obs.getitem(int32(2)).inflate(0.1); % bearing +end + +% =============== 1. Defining domains for our variables ================ + +x = TubeVector(tdomain, dt, int32(4)); % 4d tube for state vectors +v = TubeVector(tdomain, dt, int32(4)); % 4d tube for derivatives of the states +u = TubeVector(tdomain, dt, int32(2)); % 2d tube for inputs of the system + +x.setitem(int32(2), Tube(measured_psi, dt).inflate(0.01)); % measured_psi is a set of measurements +x.setitem(int32(3), Tube(measured_speed, dt).inflate(0.01)); + + +% =========== 2. Defining contractors to deal with equations =========== + +ctc_f = CtcFunction(Function('v[4]', 'x[4]', 'u[2]', ... + '(v[0]-x[3]*cos(x[2]) ; v[1]-x[3]*sin(x[2]) ; v[2]-u[0] ; v[3]-u[1])')); +ctc_plus = CtcFunction(Function('a', 'b', 'c', 'a+b-c')); % a+b=c +ctc_minus = CtcFunction(Function('a', 'b', 'c', 'a-b-c')); % a-b=c +% We also use the predefined contractors CtcPolar(), CtcEval(), no need to build them + + +% =============== 3. Adding the contractors to a network =============== + +cn = ContractorNetwork(); % creating a network +cn.add(ctc_f, py.list({v, x, u})); % adding the f constraint + +for i=1:length(v_obs) % we add the observ. constraint for each range-only measurement + y = v_obs{i}; + + % Intermediate variables + alpha = cn.create_interm_var(Interval()); % absolute angle robot-landmark + d = cn.create_interm_var(IntervalVector(int32(2))); % dist robot-landmark + p = cn.create_interm_var(IntervalVector(int32(4))); % state at t_i + + cn.add(ctc_plus, py.list({y.getitem(int32(2)), p.getitem(int32(2)), alpha})); + cn.add(ctc_minus, py.list({cn.subvector(y,int32(3),int32(4)), cn.subvector(p,int32(0),int32(1)), d})); + cn.add(CtcPolar(), py.list({d, y.getitem(int32(1)), alpha})); + cn.add(CtcEval(), py.list({y.getitem(int32(0)), p, x, v})); +end + + +% ======================= 4. Solving the problem ======================= + +cn.contract(true); + + +% ============================ 5. Graphics ============================= + +beginDrawing(); +fig = VIBesFigMap('fig'); +fig.set_properties(int32(50), int32(50), int32(900), int32(550)); +fig.add_trajectory(x_truth, 'xtruth', int32(0), int32(1), int32(2)); +fig.add_tube(x, 'x', int32(0), int32(1)); +fig.smooth_tube_drawing(true); + +for i=1:length(v_map) + b = v_map{i}; + fig.add_beacon(b.mid(), 0.2); % drawing beacons +end + +for i=1:length(v_obs) + y = v_obs{i}; + t_obs = y.getitem(int32(0)).mid(); + t_state = x_truth(t_obs); + fig.draw_pie(t_state{1}, t_state{2}, y.getitem(int32(1)).union(Interval(0.01)), t_state{3} + y.getitem(int32(2)), 'lightGray'); % drawing range-bearing measurements + fig.draw_pie(t_state{1}, t_state{2}, y.getitem(int32(1)), t_state{3} + y.getitem(int32(2)), 'darkGray'); % drawing range-bearing measurements + fig.draw_vehicle(t_obs, x_truth, 0.7); +end + +fig.show(double(0)); +endDrawing(); + + +% Checking if this example still works: +assert(x.contains(x_truth) == py.codac.core.BoolInterval(int32(2))); From db14531410fa2aaa0fb1d473fd70e4fea3c591f3 Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Wed, 11 Oct 2023 22:53:34 +0200 Subject: [PATCH 142/256] Update MATLAB documentation --- doc/doc/install/04-start-matlab-project.rst | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/doc/doc/install/04-start-matlab-project.rst b/doc/doc/install/04-start-matlab-project.rst index f62a1e11e..b69699ea5 100644 --- a/doc/doc/install/04-start-matlab-project.rst +++ b/doc/doc/install/04-start-matlab-project.rst @@ -18,8 +18,15 @@ Start a MATLAB project | Integers in function calls from the Codac library should be cast to `int32`, e.g. `4` would be `int32(4)` (however do not change those that are in strings), otherwise remember that all numbers are `double` by default in MATLAB. | The operator `[index]` for a Codac object should be replaced with `.getitem(int32(index))` when getting its value while it should be replaced with `.setitem(int32(index))` when setting its value (please note that indices of Codac objects start at `0` contrary to typical MATLAB objects such as MATLAB arrays or cell arrays). + | The operators/functions `x&y` (intersection) for a Codac object should be replaced with `x.inter(y)`, `x|y` (union) with `x.union(y)`, `x&=y` (intersection and update of x) with `x.inter_self(y)`, `x|=y` (union and update of x) with `x.union_self(y)`, `x**y` (power) with `x.pow(y)`, `abs(x)` (absolute) with `x.abs()`. | Python lists of objects should be converted to MATLAB cell arrays. - | Also, when a Codac function needs a Python list parameter, the corresponding MATLAB cell array should be given as `py.list(...)`. + | Also, when a Codac function needs a `py.list` or `Vector` parameter, the corresponding MATLAB cell array should be given as `py.list(...)` (however when the Codac function do not need a `py.list` or `Vector` parameter but just an element of a MATLAB cell array, do not convert with `py.list(...)` and be sure to get the cell array element with `{}` operator). + | Be sure that Python multiline strings are correctly converted to multiline MATLAB strings between `'`. Remember that multiline statements in MATLAB need `...` before next line. + | Please also convert Python `for` loops to typical MATLAB `for` loops, same for `if-else`, `+=` statements. + +.. admonition:: Automating Python to MATLAB conversions + + | `Bing Chat with GPT-4 `_ in `Precise` conversation style has been used successfully to help convert Python examples to MATLAB, using the description above and by providing `01_getting_started.py` and the corresponding `a01_getting_started.m` as example (about 80% of the code for `03_static_rangebearing.m` and `05_dyn_rangebearing.m` was correctly converted this way). .. code-block:: matlab From 1c90377fdabdc4193ecd47deaec5351063dbbbbe Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Sun, 15 Oct 2023 00:59:51 +0200 Subject: [PATCH 143/256] Update examples --- doc/doc/tutorial/08-rangeonly-slam/solution.m | 125 ++++++++++++++++++ 1 file changed, 125 insertions(+) create mode 100644 doc/doc/tutorial/08-rangeonly-slam/solution.m diff --git a/doc/doc/tutorial/08-rangeonly-slam/solution.m b/doc/doc/tutorial/08-rangeonly-slam/solution.m new file mode 100644 index 000000000..5560d2cd6 --- /dev/null +++ b/doc/doc/tutorial/08-rangeonly-slam/solution.m @@ -0,0 +1,125 @@ +% Codac - Examples +% Dynamic range-bearing localization +% ---------------------------------------------------------------------------- + +import py.codac.* + +% =========== CREATING DATA =========== + +dt = 0.05; +iteration_dt = 0.2; +tdomain = Interval(0,15); % [t0,tf] + +% Initial pose x0=(0,0,2) +x0 = [0, 0, 2]; + +% System input +u = Trajectory(tdomain, TFunction('3*(sin(t)^2)+t/100'), dt); + +% Noise +i_n = Interval(-0.03,0.03); % the noises are known to be bounded by i_n + +n_u = RandTrajectory(tdomain, dt, i_n); % input noise +n_theta = RandTrajectory(tdomain, dt, i_n); % heading noise + +% Actual trajectories (state + derivative) +v_truth = TrajectoryVector(int32(3)); +x_truth = TrajectoryVector(int32(3)); +v_truth.setitem(int32(2), u + n_u); +x_truth.setitem(int32(2), v_truth.getitem(int32(2)).primitive() + x0(3)); +v_truth.setitem(int32(0), 10*cos(x_truth.getitem(int32(2)))); +v_truth.setitem(int32(1), 10*sin(x_truth.getitem(int32(2)))); +x_truth.setitem(int32(0), v_truth.getitem(int32(0)).primitive() + x0(1)); +x_truth.setitem(int32(1), v_truth.getitem(int32(1)).primitive() + x0(2)); + +% Bounded trajectories (dead reckoning) +v = TubeVector(tdomain, dt, int32(3)); +x = TubeVector(tdomain, dt, int32(3)); +v.setitem(int32(2), Tube(u, dt).inflate(i_n.rad())); % command u with bounded uncertainties +x.setitem(int32(2), Tube(x_truth.getitem(int32(2))+n_theta, dt).inflate(i_n.rad())); % heading measurement with bounded uncertainties +v.setitem(int32(0), 10*cos(x.getitem(int32(2)))); +v.setitem(int32(1), 10*sin(x.getitem(int32(2)))); +x = v.primitive()+IntervalVector(x0); % dead reckoning + +% Set of landmarks +v_m = { py.list([6,12]), py.list([-2,-5]), py.list([-3,20]), py.list([3,4]) }; + +% =========== GRAPHICS =========== + +beginDrawing(); + +fig_map = VIBesFigMap('slam'); +fig_map.set_properties(int32(50), int32(50), int32(1200), int32(600)); +fig_map.add_tube(x, 'x', int32(0), int32(1)); +fig_map.add_trajectory(x_truth, 'truth', int32(0), int32(1), 'white'); +fig_map.smooth_tube_drawing(true); +fig_map.add_landmarks(py.list(v_m), single(0.4)); +fig_map.show(double(1)); + +% =========== CONTRACTOR NETWORK =========== + +v_m_boxes = cell(size(v_m)); +for i=1:length(v_m) + v_m_boxes(i) = {IntervalVector(int32(2))}; +end + +% Contractor Network: + +cn = ContractorNetwork(); + +t = tdomain.lb(); +prev_t_obs = t; + +while t < tdomain.ub() + + if t-prev_t_obs > 2*dt % new observation each 2*delta + + % Creating new observation to a random landmark + + landmark_id = randi([1 length(v_m)]); % a random landmark is perceived + + xt = double(x_truth(t)); + pos_x = [xt(1), xt(2)]; + pos_b = double(v_m{landmark_id}); + + yi = Interval(sqrt((pos_x(1)-pos_b(1))^2+(pos_x(2)-pos_b(2))^2)); + yi.inflate(0.03); % adding range bounded uncertainty + + prev_t_obs = t; + + % Adding related observation constraints to the network + + % Alias (for ease of reading) + b = v_m_boxes{landmark_id}; + + % Intermediate variables + ti = Interval(t); + xi = IntervalVector(int32(3)); + + % Contractors + cn.add(CtcEval(), py.list({ti, xi, x, v})); + cn.add(CtcDist(), py.list({xi.getitem(int32(0)), xi.getitem(int32(1)), b.getitem(int32(0)), b.getitem(int32(1)), yi})); + + end + + contraction_dt = cn.contract_during(iteration_dt); + if iteration_dt>contraction_dt + pause(iteration_dt-contraction_dt); % iteration delay + end + + % Display the current slice x + fig_map.draw_box(x(t).subvector(int32(0),int32(1))); + + t = t + dt; + +end + +cn.contract(true); % lets the solver run the remaining contractions + +fig_map.show(); +for i=1:length(v_m_boxes) + b = v_m_boxes{i}; + fig_map.draw_box(b); +end + +endDrawing(); From a4500627cafd21a45b1d15bcb37352746482d33a Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Sun, 15 Oct 2023 01:36:55 +0200 Subject: [PATCH 144/256] Corrections in union and inter for MATLAB compatibility --- python/src/core/domains/tube/codac_py_Tube.cpp | 12 ++++++------ python/src/core/domains/tube/codac_py_TubeVector.cpp | 8 ++++---- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/python/src/core/domains/tube/codac_py_Tube.cpp b/python/src/core/domains/tube/codac_py_Tube.cpp index 8892ebd9b..a77f3a8db 100644 --- a/python/src/core/domains/tube/codac_py_Tube.cpp +++ b/python/src/core/domains/tube/codac_py_Tube.cpp @@ -531,13 +531,13 @@ void export_Tube(py::module& m) .def("__ror__", [](const Tube& y, double x) { return x|y; }) // For MATLAB compatibility. - .def("union", [](const Tube& y, double x) { return x|y; }) + .def("union", [](double x, const Tube& y) { return x|y; }) .def("__ror__", [](const Tube& y, const Interval& x) { return x|y; }) // For MATLAB compatibility. - .def("union", [](const Tube& y, const Interval& x) { return x|y; }) + .def("union", [](const Interval& x, const Tube& y) { return x|y; }) .def("__ror__", [](const Tube& y, const Trajectory& x) { return x|y; }) // For MATLAB compatibility. - .def("union", [](const Tube& y, const Trajectory& x) { return x|y; }) + .def("union", [](const Trajectory& x, const Tube& y) { return x|y; }) .def("__and__", [](const Tube& x, const Tube& y) { return x&y; }) // For MATLAB compatibility. @@ -554,12 +554,12 @@ void export_Tube(py::module& m) .def("__rand__", [](const Tube& y, double x) { return x&y; }) // For MATLAB compatibility. - .def("inter", [](const Tube& y, double x) { return x&y; }) + .def("inter", [](double x, const Tube& y) { return x&y; }) .def("__rand__", [](const Tube& y, const Interval& x) { return x&y; }) // For MATLAB compatibility. - .def("inter", [](const Tube& y, const Interval& x) { return x&y; }) + .def("inter", [](const Interval& x, const Tube& y) { return x&y; }) .def("__rand__", [](const Tube& y, const Trajectory& x) { return x&y; }) // For MATLAB compatibility. - .def("inter", [](const Tube& y, const Trajectory& x) { return x&y; }) + .def("inter", [](const Trajectory& x, const Tube& y) { return x&y; }) ; } \ No newline at end of file diff --git a/python/src/core/domains/tube/codac_py_TubeVector.cpp b/python/src/core/domains/tube/codac_py_TubeVector.cpp index 720237e8a..06078f9a8 100644 --- a/python/src/core/domains/tube/codac_py_TubeVector.cpp +++ b/python/src/core/domains/tube/codac_py_TubeVector.cpp @@ -603,10 +603,10 @@ void export_TubeVector(py::module& m) .def("__ror__", [](const TubeVector& y, const IntervalVector& x) { return x|y; }) // For MATLAB compatibility. - .def("union", [](const TubeVector& y, const IntervalVector& x) { return x|y; }) + .def("union", [](const IntervalVector& x, const TubeVector& y) { return x|y; }) .def("__ror__", [](const TubeVector& y, const TrajectoryVector& x) { return x|y; }) // For MATLAB compatibility. - .def("union", [](const TubeVector& y, const TrajectoryVector& x) { return x|y; }) + .def("union", [](const TrajectoryVector& x, const TubeVector& y) { return x|y; }) .def("__and__", [](const TubeVector& x, const TubeVector& y) { return x&y; }) // For MATLAB compatibility. @@ -620,9 +620,9 @@ void export_TubeVector(py::module& m) .def("__rand__", [](const TubeVector& y, const IntervalVector& x) { return x&y; }) // For MATLAB compatibility. - .def("inter", [](const TubeVector& y, const IntervalVector& x) { return x&y; }) + .def("inter", [](const IntervalVector& x, const TubeVector& y) { return x&y; }) .def("__rand__", [](const TubeVector& y, const TrajectoryVector& x) { return x&y; }) // For MATLAB compatibility. - .def("inter", [](const TubeVector& y, const TrajectoryVector& x) { return x&y; }) + .def("inter", [](const TrajectoryVector& x, const TubeVector& y) { return x&y; }) ; } \ No newline at end of file From b5da8ce2214c962ae7ce6bfecd2b8dafda253056 Mon Sep 17 00:00:00 2001 From: SimonRohou Date: Sun, 15 Oct 2023 11:50:22 +0200 Subject: [PATCH 145/256] [doc] minor update --- doc/doc/tutorial/01-basics/index.rst | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/doc/doc/tutorial/01-basics/index.rst b/doc/doc/tutorial/01-basics/index.rst index 97cc17f65..77963deda 100644 --- a/doc/doc/tutorial/01-basics/index.rst +++ b/doc/doc/tutorial/01-basics/index.rst @@ -12,7 +12,11 @@ This will allow use to perform the state estimation of a static robot between so Start a new project ------------------- -Start a new project as explained in :ref:`sec-start-py-project` or :ref:`sec-start-cpp-project`. +Start a new project as explained in: + +* :ref:`sec-start-py-project` +* or :ref:`sec-start-cpp-project` +* or :ref:`sec-start-matlab-project` .. admonition:: Exercise @@ -28,6 +32,10 @@ Start a new project as explained in :ref:`sec-start-py-project` or :ref:`sec-sta cout << x << endl; + .. code-tab:: MATLAB + + x + You should see the following output: .. code-block:: bash @@ -57,6 +65,12 @@ Start a new project as explained in :ref:`sec-start-py-project` or :ref:`sec-sta // .. next questions will be here } + .. code-tab:: MATLAB + + import py.codac.* + + % .. next questions will be here + Using intervals for handling uncertainties ------------------------------------------ From e6cd9086c120a5e9ae6ca9054ed9344f1447124a Mon Sep 17 00:00:00 2001 From: SimonRohou Date: Sun, 15 Oct 2023 15:26:19 +0200 Subject: [PATCH 146/256] [doc] updated conf.py for Sphinx and matlab code tab --- doc/CMakeLists.txt | 2 +- doc/doc/conf.py.in | 10 +++++++++- doc/doc/dev/info_dev.rst | 5 +++++ doc/doc/tutorial/01-basics/index.rst | 4 ++-- 4 files changed, 17 insertions(+), 4 deletions(-) diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt index 85b09f4bd..19e5f7112 100644 --- a/doc/CMakeLists.txt +++ b/doc/CMakeLists.txt @@ -82,7 +82,7 @@ install(FILES ${CMAKE_CURRENT_BINARY_DIR}/mathjax_stmaryrd.js if(WIN32) set(SPHINX_EXECUTABLE "sphinx-build") else() - set(SPHINX_EXECUTABLE "python3" "-msphinx") + set(SPHINX_EXECUTABLE "python3.6" "-msphinx") # todo: replace python3.6 by python3 endif() add_custom_target(doc diff --git a/doc/doc/conf.py.in b/doc/doc/conf.py.in index a1f0ed504..a9266e764 100644 --- a/doc/doc/conf.py.in +++ b/doc/doc/conf.py.in @@ -112,7 +112,7 @@ version = release # # This is also used if you do content translation via gettext catalogs. # Usually you set "language" from the command line for these cases. -language = None +language = 'en' # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: @@ -385,3 +385,11 @@ texinfo_documents = [ # If true, do not generate a @detailmenu in the "Top" node's menu. # # texinfo_no_detailmenu = False + + + +# TO BE IMPROVED: +# Add +# LEXER_MAP['matlab'] = "MATLAB" +# in ~/.local/lib/python3.6/site-packages/sphinx_tabs/tabs.py +# for allowing MATLAB code tabs \ No newline at end of file diff --git a/doc/doc/dev/info_dev.rst b/doc/doc/dev/info_dev.rst index 85c284d68..e42f7ee13 100644 --- a/doc/doc/dev/info_dev.rst +++ b/doc/doc/dev/info_dev.rst @@ -53,7 +53,12 @@ In case you are willing to contribute to Codac, here are some information that m This configuration generates header files containing docstrings for Python, based on the content of XML files made by Doxygen. The documentation of any C++/Python function is then located in the C++ header files of the :file:`/src` directory. + ---------------------- -------------------------------------------------------------------------------------- + PYTHON_EXECUTABLE (optional) Specifies the executable (and version) of Python. For instance: + + .. code-block:: bash + cmake -DPYTHON_EXECUTABLE=/usr/bin/python3.10 .. ====================== ====================================================================================== diff --git a/doc/doc/tutorial/01-basics/index.rst b/doc/doc/tutorial/01-basics/index.rst index 77963deda..02b114262 100644 --- a/doc/doc/tutorial/01-basics/index.rst +++ b/doc/doc/tutorial/01-basics/index.rst @@ -32,7 +32,7 @@ Start a new project as explained in: cout << x << endl; - .. code-tab:: MATLAB + .. code-tab:: matlab x @@ -65,7 +65,7 @@ Start a new project as explained in: // .. next questions will be here } - .. code-tab:: MATLAB + .. code-tab:: matlab import py.codac.* From 58ed839fc8d3bf80cdc4f05aa084770a58db820d Mon Sep 17 00:00:00 2001 From: SimonRohou Date: Tue, 17 Oct 2023 19:16:00 +0200 Subject: [PATCH 147/256] [tuto] minor update, question E9 --- doc/doc/tutorial/05-tubes/index.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/doc/tutorial/05-tubes/index.rst b/doc/doc/tutorial/05-tubes/index.rst index 20cc27a12..3928e8ffb 100644 --- a/doc/doc/tutorial/05-tubes/index.rst +++ b/doc/doc/tutorial/05-tubes/index.rst @@ -245,7 +245,7 @@ Tubes can also be built from trajectories. In this example, we could have define Is the actual trajectory :math:`\mathbf{x}^*(\cdot)` enclosed in :math:`[\mathbf{x}](\cdot)` at any time? - **E.9.** Create a tube :math:`[y](\cdot)` for enclosing the trajectory of distances between the robot and the landmark. + **E.9.** Create a tube :math:`[y](\cdot)` for enclosing the actual trajectory :math:`y^{*}(\cdot)` of distances between the robot and the landmark. For now, we will not consider uncertainties on :math:`y^{*}(\cdot)`. Therefore, the tube :math:`[y](\cdot)` should enclose :math:`y^{*}(\cdot)` in a minimal way according to the discretization step ``dt``. Note that all the tubes of this lesson have to share the same ``tdomain`` and ``dt`` parameters. From 5934288d5bc67f98e440d4c466967640c356ac86 Mon Sep 17 00:00:00 2001 From: SimonRohou Date: Tue, 17 Oct 2023 19:16:35 +0200 Subject: [PATCH 148/256] [graphics] restoring axis limits in VIBesFigMap --- src/robotics/graphics/codac_VIBesFigMap.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/robotics/graphics/codac_VIBesFigMap.cpp b/src/robotics/graphics/codac_VIBesFigMap.cpp index 1bfdd4a73..b8efd5f98 100644 --- a/src/robotics/graphics/codac_VIBesFigMap.cpp +++ b/src/robotics/graphics/codac_VIBesFigMap.cpp @@ -61,7 +61,7 @@ namespace codac for(it_trajs = m_map_trajs.begin(); it_trajs != m_map_trajs.end(); it_trajs++) m_view_box |= draw_trajectory(it_trajs->first); - //axis_limits(m_view_box, true, 0.02); + axis_limits(m_view_box, true, 0.02); } void VIBesFigMap::show(float robot_size) @@ -710,7 +710,7 @@ namespace codac assert(pose.size() == 2 || pose.size() == 3); float robot_size = size == -1 ? m_robot_size : size; double robot_heading = pose.size() == 3 ? pose[2] : 0.; - //axis_limits(m_view_box | pose.subvector(0,1), true); + axis_limits(m_view_box | pose.subvector(0,1), true); //vibes::drawTank(pose[0], pose[1], robot_heading * 180. / M_PI, robot_size, "black[yellow]", params); vibes::drawAUV(pose[0], pose[1], robot_heading * 180. / M_PI, robot_size, "black[yellow]", params); } From c2dbb7cc15d315232cd2bdae204f4d9b8cd199e0 Mon Sep 17 00:00:00 2001 From: SimonRohou Date: Tue, 17 Oct 2023 19:16:49 +0200 Subject: [PATCH 149/256] [py] binding for CtcCN --- python/CMakeLists.txt | 1 + python/codac_py_core.cpp | 2 ++ src/core/cn/codac_Variable.h | 2 +- 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt index 680ce863e..a8aa2a1c0 100644 --- a/python/CMakeLists.txt +++ b/python/CMakeLists.txt @@ -57,6 +57,7 @@ src/core/contractors/static/codac_py_Ctc.cpp src/core/contractors/static/codac_py_CtcBox.cpp + src/core/contractors/static/codac_py_CtcCN.cpp src/core/contractors/static/codac_py_CtcCartProd.cpp src/core/contractors/static/codac_py_CtcDist.cpp src/core/contractors/static/codac_py_CtcFunction.cpp diff --git a/python/codac_py_core.cpp b/python/codac_py_core.cpp index 49b111895..b80934a2b 100644 --- a/python/codac_py_core.cpp +++ b/python/codac_py_core.cpp @@ -33,6 +33,7 @@ void export_IntervalVectorVar(py::module& m); py::class_ export_Ctc(py::module& m); void export_CtcBox(py::module& m, py::class_& ctc); +void export_CtcCN(py::module& m, py::class_& ctc); void export_CtcCartProd(py::module& m, py::class_& ctc); void export_CtcDist(py::module& m, py::class_& ctc); void export_CtcFunction(py::module& m, py::class_& ctc); @@ -100,6 +101,7 @@ PYBIND11_MODULE(core, m) py::class_ ctc = export_Ctc(m); export_CtcBox(m, ctc); + export_CtcCN(m, ctc); export_CtcCartProd(m, ctc); export_CtcDist(m, ctc); export_CtcFunction(m, ctc); diff --git a/src/core/cn/codac_Variable.h b/src/core/cn/codac_Variable.h index 386c2efdc..36af76416 100644 --- a/src/core/cn/codac_Variable.h +++ b/src/core/cn/codac_Variable.h @@ -62,7 +62,7 @@ namespace codac IntervalVectorVar(int n) : IntervalVector(n) { - + } /** From d4d4b67583cf11638a07ec199b6eab22a6329d9e Mon Sep 17 00:00:00 2001 From: SimonRohou Date: Tue, 17 Oct 2023 19:17:52 +0200 Subject: [PATCH 150/256] [py] binding for CtcCN (with file) --- .../contractors/static/codac_py_CtcCN.cpp | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 python/src/core/contractors/static/codac_py_CtcCN.cpp diff --git a/python/src/core/contractors/static/codac_py_CtcCN.cpp b/python/src/core/contractors/static/codac_py_CtcCN.cpp new file mode 100644 index 000000000..8015ab7a7 --- /dev/null +++ b/python/src/core/contractors/static/codac_py_CtcCN.cpp @@ -0,0 +1,42 @@ +/** + * \file + * CtcCN Python binding + * ---------------------------------------------------------------------------- + * \date 2020 + * \author Simon Rohou, Benoît Desrochers + * \copyright Copyright 2021 Codac Team + * \license This program is distributed under the terms of + * the GNU Lesser General Public License (LGPL). + */ + +#include +#include +#include +#include +#include "codac_type_caster.h" + +#include "codac_py_Ctc.h" +#include "codac_CtcCN.h" +// Generated file from Doxygen XML (doxygen2docstring.py): +#include "codac_py_CtcCN_docs.h" + +using namespace std; +using namespace codac; +namespace py = pybind11; +using namespace pybind11::literals; + + +void export_CtcCN(py::module& m, py::class_& ctc) +{ + py::class_ ctc_cn(m, "CtcCN", ctc, CTCCN_MAIN); + ctc_cn + + .def(py::init(), + CTCCN_CTCCN_CONTRACTORNETWORK_INTERVALVECTORVAR, + "cn"_a.noconvert(), "box"_a.noconvert()) + + .def("contract", &CtcCN::contract, + CTCCN_VOID_CONTRACT_INTERVALVECTOR, + "x"_a.noconvert()) + ; +} \ No newline at end of file From 8d28895e8616cbc5197178429707e78fa29d21da Mon Sep 17 00:00:00 2001 From: SimonRohou Date: Thu, 19 Oct 2023 11:33:14 +0200 Subject: [PATCH 151/256] [doc] added JNRR tuto --- doc/doc/conf.py.in | 1 + doc/doc/tmp/jnrr-2023/exercice.py | 26 + doc/doc/tmp/jnrr-2023/exercice_slam.pdf | Bin 0 -> 384722 bytes doc/doc/toctree.rst | 22 +- .../01-basics/img/ctc_dist.png | Bin 0 -> 29521 bytes doc/doc/tutorial-jnrr23/01-basics/index.rst | 462 ++++++++++++++++++ .../02-static-rangeonly/img/final_result.png | Bin 0 -> 44927 bytes .../img/fixedpoint_animation.gif | Bin 0 -> 223843 bytes .../02-static-rangeonly/img/prior_result.png | Bin 0 -> 48530 bytes .../02-static-rangeonly/index.rst | 211 ++++++++ .../03-towards-slam/img/final_result.png | Bin 0 -> 44927 bytes .../img/fixedpoint_animation.gif | Bin 0 -> 223843 bytes .../03-towards-slam/img/prior_result.png | Bin 0 -> 48530 bytes .../tutorial-jnrr23/03-towards-slam/index.rst | 8 + doc/doc/tutorial-jnrr23/index.rst | 15 + doc/doc/tutorial/01-basics/index.rst | 2 +- 16 files changed, 740 insertions(+), 7 deletions(-) create mode 100644 doc/doc/tmp/jnrr-2023/exercice.py create mode 100644 doc/doc/tmp/jnrr-2023/exercice_slam.pdf create mode 100644 doc/doc/tutorial-jnrr23/01-basics/img/ctc_dist.png create mode 100644 doc/doc/tutorial-jnrr23/01-basics/index.rst create mode 100644 doc/doc/tutorial-jnrr23/02-static-rangeonly/img/final_result.png create mode 100644 doc/doc/tutorial-jnrr23/02-static-rangeonly/img/fixedpoint_animation.gif create mode 100644 doc/doc/tutorial-jnrr23/02-static-rangeonly/img/prior_result.png create mode 100644 doc/doc/tutorial-jnrr23/02-static-rangeonly/index.rst create mode 100644 doc/doc/tutorial-jnrr23/03-towards-slam/img/final_result.png create mode 100644 doc/doc/tutorial-jnrr23/03-towards-slam/img/fixedpoint_animation.gif create mode 100644 doc/doc/tutorial-jnrr23/03-towards-slam/img/prior_result.png create mode 100644 doc/doc/tutorial-jnrr23/03-towards-slam/index.rst create mode 100644 doc/doc/tutorial-jnrr23/index.rst diff --git a/doc/doc/conf.py.in b/doc/doc/conf.py.in index a9266e764..6b58c1fe6 100644 --- a/doc/doc/conf.py.in +++ b/doc/doc/conf.py.in @@ -50,6 +50,7 @@ redirects = { "sivia": "manual/11-separators/index.html", "installation": "install/01-installation.html", "brunovsky": "use-cases/brunovsky/index.html", + "jnrr": "tutorial-jnrr23/index.html", } breathe_projects = { diff --git a/doc/doc/tmp/jnrr-2023/exercice.py b/doc/doc/tmp/jnrr-2023/exercice.py new file mode 100644 index 000000000..66ee813f3 --- /dev/null +++ b/doc/doc/tmp/jnrr-2023/exercice.py @@ -0,0 +1,26 @@ +from codac import * +import math +import random +import time +import numpy as np + +dt = 0.02 # temporal discretization +tdomain = Interval(0,15) # [t0,tf] + +# System input +u = Trajectory(tdomain, TFunction("3*(sin(t)^2)+t/100"), dt) + +# Actual state trajectory +# Note that this trajectory is unknown of the resolution +x_truth = TrajectoryVector(3) +x_truth[2] = u.primitive() +x_truth[0] = (10*cos(x_truth[2])).primitive() +x_truth[1] = (10*sin(x_truth[2])).primitive() + +beginDrawing() +fig_map = VIBesFigMap("Top view") +fig_map.set_properties(50, 50, 800, 600) +fig_map.add_trajectory(x_truth, "x*", 0, 1, "black") +fig_map.smooth_tube_drawing(True) +fig_map.show(1.) + diff --git a/doc/doc/tmp/jnrr-2023/exercice_slam.pdf b/doc/doc/tmp/jnrr-2023/exercice_slam.pdf new file mode 100644 index 0000000000000000000000000000000000000000..7ae3cf75269baa447d8c4d62ca37a2af4e70a703 GIT binary patch literal 384722 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%0D#-1D3(Tn1fj+KM02!RQRGIs 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 z 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 z

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 M-BD18DTu@UFCy9}egFUf literal 0 HcmV?d00001 diff --git a/doc/doc/toctree.rst b/doc/doc/toctree.rst index 3a5f68eb7..3e33db033 100644 --- a/doc/doc/toctree.rst +++ b/doc/doc/toctree.rst @@ -37,15 +37,13 @@ Codac: constraint-programming for robotics .. toctree:: - :caption: Use cases + :caption: Tutorial JNRR23 :maxdepth: 2 :titlesonly: - Lie symmetries for guaranteed integ. - Pose estimation with range-only obs. - -.. /use-cases/set-inversion/index -.. /use-cases/loops/index + JNRR-1. Intervals and contractors + JNRR-2. Static range-only localization + JNRR-3. Towards SLAM .. toctree:: @@ -69,6 +67,18 @@ Codac: constraint-programming for robotics H. Range-only SLAM +.. toctree:: + :caption: Use cases + :maxdepth: 2 + :titlesonly: + + Lie symmetries for guaranteed integ. + Pose estimation with range-only obs. + +.. /use-cases/set-inversion/index +.. /use-cases/loops/index + + .. toctree:: :caption: Development :maxdepth: 1 diff --git a/doc/doc/tutorial-jnrr23/01-basics/img/ctc_dist.png b/doc/doc/tutorial-jnrr23/01-basics/img/ctc_dist.png new file mode 100644 index 0000000000000000000000000000000000000000..a6fb379a0a7edcbe40010f2613fa4189275504b8 GIT binary patch literal 29521 zcmeFYg;UjGyEclVAV^4ylytXrDcwkSm*k>5RHUV)OB$rRySux)yZd|A`<;E}oHKjo ze1E`phLKq7_rzV-eO>no{3D^y>{A_)Y)RauE3MmA#;Z z(i`y4 z{9Cbv7!?x+8%8C+AeN(J)acY~_8)Kkug_0cY&Y?b_>SN9+yjH07|N5qV+55>tg`u6slytMK z!bcjS;^*wNROS$=Wh~1lb3jSDJ6gw%mCu&de?ncrl_Z^=<+J&7phH)k-5N$dghsJc z2hS}mW%$URgPwqPI;+!L!XX~Lq3N?oybbW4y!1#j#bpR+%Bo z=ReOVZV3VZ9L-q2q3?(3cF)vn4w4mo`1O43z2(f0C?dU@^YSEvW0|wJDoi&^v;2cn z!D50=#&-Gr!(p>4JN8hF)qt7mjbks8$$@l6a#s5Wh5gIKWiz=%R~_Exv3E;nl3Oin zfx>Ld-4~TaXjqn}yT7<7&*^A&JR?)wN#BniBd1hxwQt3@f2-Z5+26j-N-ysnL$DXy z({Fmum2}(O`c=xbm(5Iz)AsxCl5O1aT9zYcoW!Ku9kT-4KA!=tS^gM?Leupri#MY_ zt?>9vhwCn78c{MQ=7O$qKaIq2}0SYP=Cadd_H;KDgKZ#yT9u zFXVrkxldj-?CKj+&v0hej4n!bffPEhFMGc#HRFB7{Z8vIBH#G{g~>3t?8bfSx$L%{ z<#%?uKdlQTouWnoWIv@U!Rxe4ANkz`czE53l7ax;->I>b^Ldf4p zz1$nvSd+^eyV7d>(}5hE-7jG>X3eFLwOua!$8Pm#*`6m>ux36pD&FiMIjlNf;)&Sr z`L}H5bI0|!O|xCzTB>srO7GskNyS(>SJsB(Q+)Wm$-*yY)+I$BP-2cRvb6g(Fq_VC z7W#_Wh&nr&YmL!JALO6=S%wR@8da;u*A`=47Iv&q!sEYi>zSG+UfNexRdz3JUA>s{ zU=xIl{C#0^e*FA3(FQe7qVZkN3AZ?LFj+qVs{7MbrS9g)r0TJHWbY(V?CGTFoBO{= zzDO)~t|zsLHU$gO*EFzOcu5all+8)}ohgjjkt27?bE?ckI}fYP*LqEL!PM>D2A5-Y zI=RoV_4MKN9J~j&iBZGoIH_&fu?c50m-|}qNee{ldDS^5Ud?kZcpH#=pW(%U%P9Yu zL^ScmC@ELdtJs1W#~00Ya$}X=p9%#-&T&rUo3GUedJj?_8Km-&4DD+u;cgXUH}!{? z`zVtLVJ${>y&+6X+(D8IvA5PkE}^_nsq?xeZk-$^8RE~zb+-uDdgt>z)*9SF#+fM` z$J!@BwXU|5_Pq?Og92Gb8rA@1&DBF58)uI8=4ZI)OI(iw%Dnk; z{>6=Y>_-qUzuO0n;l#(51I&d5UWYZU^Vqli%wfCg;|I7ioe#6CN>cCxY-Ziti9GP^ zOwG+aoh<^#%l)4yA9=?Y$VYasmU)=gG0na7N14y}u$JSH&$z4p_HXR9k~f?xS?yiU ze?LBfsBt{1_u>434Oa202;}nNI>$!9tGBgQ!-N#oep_MnVy!JX()JrfT}^&=%1SbP zam%&bUZ;fG6vWe$9!p5s(T?VhBk<43?Fav)+}UkkqZac4>hiB%qmW9)k9?FKZO?EH z*+Sif52j2UesU;U914+*8eKb#|r z9T9S}%_`I=yb2K-F7vq7|9!tzspK`^6vrpVpCv^NFHM2Lp5<2Zi8y8SGWl)Gyz@XN zMH1Q526@r4rjE*y3syNP=_Xt?lBPL@R%6iGnp)%;v2Xd)_eA_hYVDywT1v+7Z!_CZ z&jY>7i|ht9vdsPO;cU3*A1 zYDc0g5ED_^;o(Gj9D4KfFIv=&QYF3@oOfPInL9pmed@~OVCuP+x;xB(L~|+Rn4ac} z!8c;L)lY zX}tZ&xQ%vA-H&hTSHzg;nQ?Kk4BLL7e_qXQecuc7qMZXY|lPV zWM77xy6$Sp?cHi^ZYI>pMZm|-N2gM1NKC_{-uM|0F7jb2n*YQNm&S&}ftkI+ev-<`6 zi=qEST`jsZ;*#<%9=bggZ%9q(QU-F@8I;Uez87~nP%KQ(jqTbk6}BH}5QJ&EkpHBg z(Tk^?W?9W>{NuJtu7Ta3+{{4{D|wMpiSo?F8ZU59pRIkFC7My>(e#fszS*j)0%W1B z2bS3SfBs07&pFH<8^*91gx9o7x(0U!&G0zxf zX2&{hhr5!=@|x-OdN7Pnot!%8|6*$kEXcMIC0*nSf>++E9^|oK;2FdsrQ10rw?vc$ zq7~M$^4ubH(cL7(dwR^Wr?V{zgnDoIRx9n0kQmrZ&+wr*`LW0P(Vz?b6Jhy zEI%@@e>zWpIHeN!6+(?#-yEO6EVq5@mvw4a);*iU8<9~R{)bIN*7UmH*!GR5FW>I> zTM}B)PZ^@|1}>bl`i9%0pAQKe1nluWN%B{YkG}i3!&JIUZ~I%IE-(3ZuE$~NM#pY@ zh!@Am?B4X{NW09%IIf$N9HT*`l+%I(0$RBguP@)j-4||V{jleXkLbvrzGvbq#~EKT z`&ExHVA5jgwew3Up3-EzaP}?Tpk=Rjvsy0)vk-#)qt7kMKrjAWRp9c=l=&N=wR}E?c+NMin}{2Jh^?tdT~5A}F*opZ z;NcnK_xN2@z91spag$`u%wqCB9}kJLZdo#PxIBHhui9+K=_zN*Rox>)kj?b*t;=Ajz(S_ySNfE1YD7GHijP~>9;X+!9~^ci^WV{a(~8c| zZ~i;IrN$6PD}S-XVH#I#d!$7|k(z$9zechUAWUr^wcYlh-4!vy)UGEK^FjvvH85aq(dDhq0{cg=)suHrV zu(!8wRXk?AqH#b&qkeBqVAx-U!C6~xV|^&du6;*)zLCHb4`aX8T+jb2nZU^?JjM1} zUV3J3)}U{|N$fyd%ov zY*F>XF;1=-r+MumwW36ui(Qi%#7@A)oWlW-b)q@)-Ph8{A?7YeDb7xH-zOCwh4 z-A|r=vRN*bMX=3>+K_fL{4z`zzi+)bc}`*G^J6s~6pd+Ej{1_ioH@GkjnKVg76Lsv zbJYNb$KBX7C*~8}G4E~&f=!4^@^w1PtwD#oW%FsbX>Fp%9E;(&DiSi^z=W5TOM@+0 zG9?jliPx(%LatAfB^HKDy8MYnq5ZgH*<6bhDSxMUq;BcBZz4Ji^K?Z(Bf=%~NCTqk z9dtD`*pe46=Yt$XYNLO@z><)V*ztYU!SUWJ%OX|#U7hn*Hq#_+J78ZoFYom=8N;(4YXu>u)y@_0T)FKInooN$#ZzA5 zKjMqa_ZvkU_MVvY@BOd?xQy22bkod))$1zq^>?Xi8J;GLpLkr8vkU_QdPsRM-q&#* zBErWG=yeTM_%HV-_4ziYvN}#hi0O36fKJM8_!yDThxEMeD|rcLn*91~;>`-dm{*)& zR9PoN(NZv%7@;jP2RK`=vP$#wME%w0Y&x~U>|sCo@yAvkwR;Yei2?G0CC=m}uUZTR z@W7{;V-(N-2dYf-Uuf+sr_*0GCSLqug<`|0Y7hi=Ml^aq0uG7v*_Q!aAI$&0zOPWH zc>f(yTUfXQ2AZu%;7)pQH4FPMO$H~IylsX*G29=}a88cE1Df(mEY6+}Wz2#n)o-!Q~nEYWB0Z&%s!9 z;+!WS%2{ryx*_-==09{?#`cBO4Xi9xTKhcy|7s4U>)llo8B1Na+%5CZ3Fn`uqu*)v zSSlp_nd6JED5G_(hciqLUIqTM!JfbDy{GN|Q4zbqWZbkpch~D#%AT3kSrB+w&!ZQIAjc}9LX;bE_^M;LVDr6!eH(75jy+jR&d75! zdWl_k|L~7}3pqPx43A4uPUw-YtM{foSfed)r#N|LmdhtTz*DM@F=%hAu=_(Vs(A~7 zRo6T8!gL?0Xt}|4cnnFQn9^{6^}E%7{rwoLE{BLb31V|+fUfS}KPaKq6z-?^&9!~PQzB$iJPQb+n{+4>hY=KNfd ztH;J;jS2M4^dH*0n;vhfDf=3y))!Gr6&^wuqUAQ*XZcrE#g_(>d@*!M* z+LRWAMvr0`ox{Feqq=?Nb^Fwcdg0p)69;{zp-pt?VZMkp{+|Tr|3uUOH~+N2tqgK3 zd+3Syz#)UI)N}b6B(byWS(v!NEqE1P%Xsk14H$6`{rdzqEHiIToVb&3G_y$MK~lmrr60n`>VRCP+F<$YVbW%`&(;JrGckC zP$xb==frO$m%A87Fp^G5>wjZ2mHxaxr{j=!PnddW*}wCKjEDnCD-7Lpwj(C4#Mg2~f_mxZ~@l__pEV<_-hrIm^D64Pxa| z&YT{Y^qAR!+ox=3z1Jn|sJ^am&>AM7L{-IigaM9ECRbK_%xX_T5f;RIX^tdMdQ2yI z7o+Moub5FG;Dr5#3S6M~Q4^Y*$-=e+gGy-Zlx}UgJ71L4eC)ZSxe$p3);Mn|c#Z3X zC#gezOV`B#Zwv$J-8%Yo0t@c_&6>A8!iTD#DnI_&gVTYk8B=wZwVTU$L)PcJ$hu_M zwWT?M`-q&1lpC5DnhKJ`hB|w*uAUvOP=2h*$R4>=2e3qhx8VKg{{+PUZ?XPw^1*)> z5Z85H>)1FL3UmZQbS?AauPMcNq`G@JEMc(eHGWlAUV0KWKVISSI3IN^E)p;@GV=2A z9oJWuk8`LC+b!LXv)i2wU=kc8R8tCoq9UE2^ zK8sg0Ek5a55q}8AE_1-_6*TWGX4 z-yk^C3Ov+JOelpC38W~W*xK59 zTrWDSr-C|mj{0_aaq&^t6bs}Jxg%N!+Eg1iq=GLw&yB4v`&2ZwT3hv8>s)6y-Yz+Q zc-dlnlI-?2Hh;Bh?f%fJ9;NLoDku#1m58*=_crefS_bnl56$s}4PW z%Dvc5cAJvrV$6vdEbiR|WpylUAk}_?rsLDLmX=noS}6$@t%}FaUA}7ZlIo2= zDv`^g6fW0HxAR^!*647#l%Ho z$U$?uq+b0>aF^fO-*iuUj4Uwf4hSpeekJ~=QYbn3Pnk$9HaxuL>G6KH*;|0e=|J!9 zV)tOVIklywrNFLP^NL*ZPswM1S0b8^Qv`=MF0RN6>Ym8T>wlr`Sid}=zIEZKV@v5- z!E$#YFT)Z!8~Xf6bb4`-so(DR$#UkTX%jfLrpl=ISuZ6%r_J~C?crtBv+bf8d0wUt z!CsJE2oIak>Y@@Ur#NJ7kL|xX=AOt?g#TWvd-!5tqWCCE`AjN?ZWWZWjg!^( zPv#Q=h*&i5u(3te)Z&)pz{6{|z+QW^&>Kw)+r5XRw}!of%~U<}MssOXi}$40BT;Id zV%&9b-o5+$`7;bK|A0nJO3Dzbz(Y$6y=EK-gz)k1vc0pDJb$6Rt&K{zso}LE$h{G> zyd^CeyoEXs;VO(8B(hJ&hK53ydy{!Ojod65gCg@3R75R5&*Kt~r87k{%@=Lq|enK{C0z6uSz!J@cP)Y6h}GGDd1c%?E? z2S8l}EH?%_XXhM|VC;%-qa+ryMcVZNAt9c69FL0;9^by@zQza+5cf(GtS-^0G?&j; zDTH?#hs;-noUV7uW~~DOn@;U}SQx5d*YDP$p-{?H zkH|d59CREU)7@NPeXyHbt=qFrTz1R%`_skSbCnjn*9-Pg*YiWhQz+8nf-1PMHT*czR zEK;y%BGCQk9j|-ei0tmhJNG@pNlywF8P^fjd{m{LJAue6}#qMmd0O!TD z^+Ma?cQcr9%Ln`U=+)A;HfTEe5y8@GCej(#@Pl!6B~XVdINCjFP1RhIg#KVLUo z&Fyp6+_CW8(16on(kS(eF^dRq5PF+sbK>FABI4Mk+Ysk8KeU*X$9HTffmJ1BWyw#E ztn6r1qpZX24k>6+Qr8zcjTp2fyQb%ni6E*TjoPQDPW8uAGKybS5 zZjLrEdGOv}9U687paCMnCpVXh+vy-6KVK(2FhETsROZo~C7xIG91B%xQi~mlGB)lS_&3Uj+Nrg>cXa7-_qSZvE$h z+l&;Yp2y@vx;2dlZsv2zXphNb*LLK8bKyli7paeOwA;DTB%&xjnT`F5XEiM|pQOQN z&=P-sdNc#anL==Masqq|HHm`WM!euNDf^TP=DA}|T=Bio^m1Lam#L9=&IKcRl+ES` z)-A~PLMNHzjXV0rv4l;_VHo;TN=+^I##uR2Q9VxZ?M9TGT9XIYTO5W=)nXkG?4pO1XC(J5cd&AEH2 ztEsel5bzRzvW_lAckE|onLe%GS3{Olt`6r}O$Oc@7#MsT%S8C{CD=>iEioxl2uaf%ukR@rj(yx7-Lkp?X$jBz&Z>rmCh|+DW!f zj$K1bHLQEujR|d?zy5eXa;BuD{C>~;6OB}~#CBa2tWzd>0FDOW2?O3(Pyi+`%|&y| zY;P!{@xuw#hIB#(B|(bU%kRP4jJ8J;Tti9R{ihj}Fem5Noy9doBEh$7oBPJSGqZa= ztBM`VuA#G8@)@Gg0{Q0;CiD=?O??%6bZgJu*!$r9@o~?Fu9!Qf%GhGX?XyfaRtzg z8h3htc#5<8-n4U!frX*x{Tdznlna(qml4ZiO3vev$05V@^Zb+wMCgtWhXY44no%O} ztlD`x2jD=gho$6uHLpEvzd=(@dlWDooZ;-ptJ z7sAq?pgUPr?il|Wc#fi}sd;y~FV^h!I8$zl%FN7su{-W>n(7&|v-8b-@}Kc;0O<09 zkUwYHi9BBsKr*ub0~NwQP{-Me;@zU_imNeHwVZ7M7xS zIK5vJ?-tw={(YW^{Ne2b^UZdOdnoh^)X;Yr7(V{~@Bs6z_~Ph{C;CE_kPOUf_i{}7p8?AK&N+)qf)Y@;KJ>4Gz zu!9RggJ`|eAsvC7Fxm8G?P`jDJqF>C>>v8w^N~x?%yD|}J~K{~Jx`YxEV>1N0$Upw zE`8beLQPX0O{u|VYT@vO2p{2{+U<|v{I{F=zZ9AzsbEakhTnM~yxMP`Z3)@uc!PxS z@gv#y7W5Z0b65rTm`8hKS#P!L9Q3^~*7 zBJTckCo!}Aw@x0b0X<^mHPrW|BZXaF_n?p9YUB5$K3r$&>fppJSxUmxI5Wezz zg^r2-RZsL7V9Ve5T#*2ou~K$qI?hnxX!JtmZrB(e}uI88v3m_zKJ3YF|6 zV&}1@*V+Kqnyk*&P2ejP2zO5@1_D`5rh$cpj9)aY7%D+KNNTb}ve=SO+xW@&)T*UO zn=~;mRu=NkJBUI|heJKJI6YG$)5xiDHPBOEDjYcvhw9eak!Og7ne2?DA>pzA25pHt zcS3-871fCE6BY2EH&lCH9yN&7`>ivxnI8^|xl%2+s#8(QT`yGtcs;qi&e}JsHv$5dc9}Wc_YuSuLZ4ZaO^UqJI7!a>Rf}%bB1iMCb*jB%M$H#hE{ji zMdZDg=k2R;uG?qHA&1${j2ohr+{$?>IW($_ly)1jqy>*Auv@K;8&h|ILoVdkTP&v>w?=cNV%3m-1XU0(XrMpy>75H|TUv@6jFF8i_d_A* z8%Yz22d&*O-!(oVAtAKt0<5nFz0Xw8y_Ar6iBPSnzA{&N`El1=VT$UhWhqsnESl6)$vJKWfQLnZ*iW#j);#CeO7JG^g4_bo&9hKV!4eMo&b;Xr3fg{D~&mtzcVY*C5X zrAlNG1={;HxzB;0Ukbgnv8kxGF*u=H1J%+vPyoL>dHwy9o~iE1o$HTP*A#{FCx?-{?Mt9jg1Ot!T<*}Txwc!xp!0KEBIhmJr)2S4|4xZtrZzkA^ZVeI#|gh=>Rp7FK6Ys+XjT3#T0K z=_^R{BRfBTj6l4+@NdTBK`EM9fLbUg_mr|^wx=B2(^y438fVK7-AzKDe+^nx*==p8 z2XCC6I%VA}vfZv_5Pq`w8XAq~y%qmtf8#LftR*d8DxQ4+nlFyE_4PdEeC%ji)h&QB zR)CRWLdi+sa?spf=s_TIxBt@q6n~K1yHr9U;%ys}sjqS4ickQlTe-++%2(4)@v!@6 z?xWf}&~cRS&HEs^S|GgWTGqX@4>wr_t@wpzja$uV24L%1z}!{Ded) zE+ssv3jh8>dhxQWwf9HRB+W>X1zyV0C8o8n)@&7X%xn}tsZ+ZX@a*vYc;1ENor&xZ zsWLl~^(N7?6^~2=DbI20x(6Erw;fu;QHuZ>3~%_Km%3bF=Qm&k zNS~{JKFex!U&G68xy2l?8}+;c43S@bJrC3v>)m114rFM|@387%R@U|eCpDsXXUh2w zx%mQ zQzbGU`#T~cIY3;|suop19}I(6OPs)Z|&J_iSyljv#Uttb5O8rw|(EZ)F8I0z<#}cgi@Oj|j zumYmvi!w-SXqD`(8|8Jg>W}oE>H7;g}`5 zeywJ&Wc6|r+w(0Mz{sDTo!#|U6B2ecIyrroM_te+__mmt4z^Oqx{V&s&VkKzdUeC* zf@zm-65XPe{F))xMcMV1KPg`P*hV$iPZ zm@PNuvRQ#aK&TdIc2A6pyBR5C1UwWdMXz4JMgvgl>(_Ve?6=2c?#`<4|Kaz4YsG?Q zAvu{%drmn=s(vqt)6P_NvAxO7($<>B|*epkt?{1?z(FYyTT(49PEDLu>(h)0(-u6WwnpXdSg*Q3$W7Mpoi=vPj zbDR;`V64pQNBkZ`DVS2eXUxqX-IDlj~QCih?#4-3e+)x*P(>gwxMufyH(obtZM zo6~hbuCh2EEffsH!om`%-#k4%dEfU+4%Ikg*Fe*4aV&q1pqT*JltPB&k+ioPN4uQ= zP}xq(`oSQc?^fScc~q~F0l=^L+zvZU>-#@`bj8Wb3EOoM9i0#HRwrEREXoF-=DR)= zSKKZu6{s->VbO|$v_Mz~_1d61rf zL?+g)aj~Noe7H=sop=d<^m`66ym+*`nzW8f^XD`}5LZApRm$kJkLcUH1;t^aYMSSH zA1C+B*3DJ9Vmynn8YrgyG4!M)BtHRB$>emP1~nM(-sqsItjYC(v$}@y`^0uW=1qg# z`j_AaxM+aD^pSxfR53@k)cIJ49kMi#DG@aTNOy9nn1(lVT^hR40RGXMk*I>_dAllD(GlFYi!aQt{*Ky9JZxrI&Ei4$tC%i00<6Y*TI z_qd-0*;l2)d@=~&GkpkT7_73hSYMrG7${D7-Xka%raP2r#@Z}{6+~To>7GT?D7Q^cP6`hH)jPSc8RCwxz7C^QyF6t4 zo$Bg7u~eW>cenoUShY-`1@fQ@nw+fyUBkvo07L%J5}ZL0M(U; z7ONJhML?Y#3^x7e&eqVBlly*}@>U z7bkKcX1Qj3bvg<$yv+9TsB+<0O!+4<8~$L=_KAmwCtEIc2=HMJ%cqy=BJ}owT4=pI zgc>_r8^`KbyZR)493*6@p+nCIs;a7OZEbJfzO9G20*UPY`gptL2P~aVLmbDFiw`K= zV`)P09v&WLN9)_$rfC~p>sxD|mGb`ySN`imZBA<9GjF!{^T_WOIz8{5t$w}KNHwsO zD$9I0uC|~>u(Om#0eUg6y2S&fG{M(RK;UV*+oLs~$o*1nz4)HhdB}+S83uFs7By z%j5%wEYmYz&qdexO_SDH6OqvM58|!gmt-|Fv*5L_?#se;PqJ5v*y&mzXxns8oPuiN z6JZvla{kZGWF-;`�y)%K>h|l9hXK~?m-44oYTkkoe7`*13 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 literal 0 HcmV?d00001 diff --git a/doc/doc/tutorial-jnrr23/01-basics/index.rst b/doc/doc/tutorial-jnrr23/01-basics/index.rst new file mode 100644 index 000000000..32033e2e5 --- /dev/null +++ b/doc/doc/tutorial-jnrr23/01-basics/index.rst @@ -0,0 +1,462 @@ +.. _sec-tuto-jnrr23-01: + +Lesson JNRR-1: Getting started with intervals and contractors +============================================================= + +The goal of this first lesson is to install Codac on your computer (or use it online), and get used to intervals, constraints and networks of contractors. +This will allow you to perform the state estimation of a static robot between some landmarks by the end of :ref:`Lesson JNRR-2 `. + +.. contents:: Content of this lesson + + +Getting started +--------------- + +To get ready for the tutorial, you need to install the Codac library on your computer. +Please follow the related page of the manual to see how to do it: + +.. toctree:: + :maxdepth: 1 + + /install/01-installation + +Then, depending on your preference between C++ or Python (Matlab now available), you can run some *Hello World!* program to be sure everything is working well: + +.. toctree:: + :maxdepth: 1 + + /install/02-start-py-project + /install/03-start-cpp-project + /install/04-start-matlab-project + + +Start a new project +------------------- + +Start a new project as explained in: + +* :ref:`sec-start-py-project` +* or :ref:`sec-start-cpp-project` +* or :ref:`sec-start-matlab-project` + +.. admonition:: Exercise + + **1.0.** Check that it is displaying a tube in a graphical view and print the tube in the terminal using: + + .. tabs:: + + .. code-tab:: py + + print(x) + + .. code-tab:: c++ + + cout << x << endl; + + .. code-tab:: matlab + + x + + You should see the following output: + + .. code-block:: bash + + Tube [0, 10]↦([-1.448469806203122, 1.500000000000001]), 1000 slices + + | This was to be sure that everything is working well in your environment. + | Now, we will start from the following code: + + .. tabs:: + + .. code-tab:: py + + from codac import * + + # .. next questions will be here + + .. code-tab:: c++ + + #include + + using namespace std; + using namespace codac; + + int main() + { + // .. next questions will be here + } + + .. code-tab:: matlab + + import py.codac.* + + % .. next questions will be here + + +Using intervals for handling uncertainties +------------------------------------------ + +The values involved in robotic problems will be represented by **sets**. This allows to hold in the very same structure both the value (a measurement, or a model parameter) together with the related uncertainty. Therefore, a measurement :math:`x` will be handled by a set, more precisely an **interval**, denoted between brackets: :math:`[x]`. :math:`[x]` is made of two real bounds, :math:`x^-` and :math:`x^+`, and we say that a value :math:`x\in\mathbb{R}` belongs to :math:`[x]=[x^-,x^+]` iff :math:`x^-\leqslant x\leqslant x^+`. + +This can be extended to other types of values such as vectors, matrices or trajectories. Then, + +* reals :math:`x` of :math:`\mathbb{R}` will be enclosed in intervals: :math:`[x]` +* vectors :math:`\mathbf{x}` of :math:`\mathbb{R}^n` will be enclosed in interval-vectors (also called boxes): :math:`[\mathbf{x}]` +* later on, trajectories :math:`x(t)` will belong to tubes: :math:`[x](t)` + +The initial definition of the bounds of these sets will be done according to the **amount of uncertainties** we are considering. For measurements, we will rely on the datasheet of the sensor to define for instance that a measurement :math:`y` will be represented by the interval :math:`[y − 2\sigma, y + 2\sigma]`, where :math:`\sigma` is the standard deviation coming from sensors specifications. In this case, we assume that the interval :math:`[y]` is **guaranteed to contain** the actual but unknown value with a 95% confidence rate. + +The main advantage of this representation is that we will be able to apply lot of **reliable** operations on these sets while preserving the actual but unknown values. This means that we will never lose a feasible solution in the initial sets throughout the operations we will perform. This is done by performing the computations on the bounds of the sets. For instance, the difference of two intervals is also an interval defined by: :math:`[x]-[y]=[x^--y^+,x^+-y^-]`. Mathematically, we can prove that :math:`\forall x\in[x]` and :math:`\forall y\in[y]`, we have :math:`(x-y)\in([x]-[y])`. + +| *Example:* :math:`[3,4]-[2,6]=[-3,2]`. +| If we take :math:`x=3\in[3,4]` and :math:`y=5\in[2,6]`, we check that :math:`-2\in[-3,2]`. + +These simple operations on intervals can be extended to elementary functions such as :math:`\cos`, :math:`\exp`, :math:`\tan`, *etc*. +It must be emphasized that there is no need to make linearizations when dealing with **non-linear functions**. +Sometimes, when functions are monotonic, the computation is simple: :math:`\exp([x])=[\exp(x^-),\exp(x^+)]`. Otherwise, several algorithms and libraries exist to allow any mathematical operations on intervals such as :math:`\sin([x])`, :math:`\sqrt{([x])}`, *etc*. + +The asset of reliability coming with interval analysis will help us to estimate difficult solutions and **make proofs**. + + +Hello Interval Analysis! +------------------------ + +Codac is using C++/Python objects to represent intervals and boxes [#f1]_: + +* ``Interval(lb, ub)`` will be used to create an interval :math:`[x]=[\textrm{lb},\textrm{ub}]`. There exists predefined values for intervals. Here are some examples of ``Interval`` objects: + + .. tabs:: + + .. code-tab:: py + + x = Interval() # [-∞,∞] (default value) + x = Interval(0, 10) # [0,10] + x = Interval(1, oo) # [1,∞] + x = Interval(-oo,3) # [-∞,3] + x = Interval.EMPTY_SET # ∅ + # ... + + .. code-tab:: c++ + + Interval x; // [-∞,∞] (default value) + Interval x(0, 10); // [0,10] + Interval x(1, oo); // [1,∞] + Interval x(-oo, 3); // [-∞,3] + Interval x = Interval::EMPTY_SET; // ∅ + // ... + + +* | ``IntervalVector(n)`` is used for :math:`n`-d vectors of intervals, also called *boxes*. + | For instance: + + .. tabs:: + + .. code-tab:: py + + x = IntervalVector(2, [-1,3]) # creates [x]=[-1,3]×[-1,3]=[-1,3]^2 + y = IntervalVector([[3,4],[4,6]]) # creates [y]= [3,4]×[4,6] + z = IntervalVector(3, [0,oo]) # creates [z]=[0,∞]^3 + w = IntervalVector(y) # creates a copy: [w]=[y] + + v = (0.42,0.42,0.42) # one vector (0.42;0.42;0.42) + iv = IntervalVector(v) # creates one box that wraps v: + # [0.42,0.42]×[0.42,0.42]×[0.42,0.42] + + .. code-tab:: c++ + + IntervalVector x(2, Interval(-1,3)); // creates [x]=[-1,3]×[-1,3]=[-1,3]^2 + IntervalVector y{{3,4},{4,6}}; // creates [y]= [3,4]×[4,6] + IntervalVector z(3, Interval(0,oo)); // creates [z]=[0,∞]^3 + IntervalVector w(y); // creates a copy: [w]=[y] + + Vector v(3, 0.42); // one vector (0.42;0.42;0.42) + IntervalVector iv(v); // creates one box that wraps v: + // [0.42,0.42]×[0.42,0.42]×[0.42,0.42] + + One can access vector components as we do classically: + + .. tabs:: + + .. code-tab:: py + + x[1] = Interval(0,10) # updates to [x]=[-1,3]×[0,10] + + .. code-tab:: c++ + + x[1] = Interval(0,10); // updates to [x]=[-1,3]×[0,10] + + +.. admonition:: Technical documentation + + For full details about ``Interval`` and ``IntervalVector`` objects, please read the :ref:`sec-manual-intervals` page of the user manual. + + +.. admonition:: Exercise + + **1.1.** Let us consider two intervals :math:`[x]=[8,10]` and :math:`[y]=[1,2]`. Without coding the operation, what would be the result of :math:`[x]/[y]` (:math:`[x]` divided by :math:`[y]`)? Remember that the result of this interval-division is also an interval enclosing all feasible divisions. + + **1.2.** In your new project, compute and print the following simple operations on intervals: + + * :math:`[-2,4]\cdot[1,3]` + * :math:`[8,10]/[-1,0]` + * :math:`[-2,4]\sqcup[6,7]` with operator ``|`` + * :math:`\max([2,7],[1,9])` + * :math:`\max(\varnothing,[1,2])` + * :math:`\cos([-\infty,\infty])` + * :math:`[-1,4]^2` with function ``sqr()`` + * :math:`([1,2]\cdot[-1,3]) + \max([1,3]\cap[6,7],[1,2])` + + | Note that :math:`\sqcup` is the hull union (``|``), *i.e.*, :math:`[x]\sqcup[y] = [[x]\cup[y]]`. + | *For instance:* :math:`[-1,2]\sqcup[4,6]=[-1,6]` + + + **1.3.** Create a 2d box :math:`[\mathbf{y}]=[0,\pi]\times[-\pi/6,\pi/6]` and print the result of :math:`|[\mathbf{y}]|` with ``abs()``. + +.. hint:: + + .. rubric:: How to use :math:`\pi`? + + .. tabs:: + + .. code-tab:: py + + # In Python, you can use the math module: + import math + x = math.pi + + .. code-tab:: c++ + + // In C++, you can use : + #include + double x = M_PI; + + Note that in this code, the variable ``x`` is not the exact :math:`\pi`! Of course, the mathematical one cannot be represented in a computer. But with intervals, we can manage reliable representations of floating point numbers. :ref:`See more `. + + +Functions, :math:`f([x])` +------------------------- + +Custom functions can be used on sets. For instance, to compute: + +.. math:: + + f(x)=x^2+2x-\exp(x), + +a ``Function`` object can be created by ``Function("", "", ..., "")`` and then evaluated over the set :math:`[x]`: + +.. tabs:: + + .. code-tab:: py + + x = Interval(-2,2) + f = Function("x", "x^2+2*x-exp(x)") + y = f.eval(x) + + .. code-tab:: c++ + + Interval x(-2,2); + Function f("x", "x^2+2*x-exp(x)"); + Interval y = f.eval(x); + +The first arguments of the function (only one in the above example) are its input variables. The last argument is the expression of the output. The result is the set of images of all defined inputs through the function: :math:`[f]([x])=[\{f(x)\mid x\in[x]\}]`. + +We can also define vector input variables and access their components in the function definition: + +.. tabs:: + + .. code-tab:: py + + f = Function("x[2]", "cos(x[0])^2+sin(x[1])^2") # the input x is a 2d vector + + .. code-tab:: c++ + + Function f("x[2]", "cos(x[0])^2+sin(x[1])^2"); // the input x is a 2d vector + +.. admonition:: Exercise + + **1.4.** For our robotic applications, we often need to define the distance function :math:`g`: + + .. math:: + + g(\mathbf{x},\mathbf{b})=\sqrt{\displaystyle(x_1-b_1)^2+(x_2-b_2)^2}, + + where :math:`\mathbf{x}\in\mathbb{R}^2` would represent for instance the 2d position of a robot, and :math:`\mathbf{b}\in\mathbb{R}^2` the 2d location of some landmark. Create :math:`g` and compute the interval distance :math:`[d]` between the boxes :math:`[\mathbf{x}]=[0,0]\times[0,0]` and :math:`[\mathbf{b}]=[3,4]\times[2,3]`. Note that in the library, the ``.eval()`` of functions only takes one argument: we have to concatenate the boxes :math:`[\mathbf{x}]` and :math:`[\mathbf{b}]` into one 4d interval-vector :math:`[\mathbf{c}]` and then compute :math:`g([\mathbf{c}])`. The concatenation can be done with the ``cart_prod`` function, :ref:`see an example here `. + + Print the result that you obtain for :math:`[d]=g([\mathbf{x}],[\mathbf{b}])`. + + +Graphics +-------- + +The graphical tool `VIBes `_ has been created to Visualize Intervals and BoxES. It is compatible with simple objects such as ``Interval`` and ``IntervalVector``. Its features have been extended in the Codac library with objects such as ``VIBesFigMap``. + +.. admonition:: Exercise + + **1.5.** Create a view with: + + .. tabs:: + + .. code-tab:: py + + beginDrawing() + fig = VIBesFigMap("Map") + fig.set_properties(50, 50, 400, 400) # position and size + + # ... draw objects here + + fig.show() # display all items of the figure + endDrawing() + + .. code-tab:: c++ + + vibes::beginDrawing(); + VIBesFigMap fig("Map"); + fig.set_properties(50, 50, 400, 400); // position and size + + // ... draw objects here + + fig.show(); // display all items of the figure + vibes::endDrawing(); + + | **1.6.** Before the ``.show()`` method, draw the boxes :math:`[\mathbf{x}]` and :math:`[\mathbf{b}]` with the ``fig.draw_box(..)`` method. The computed interval range :math:`[d]` can be displayed as a ring centered on :math:`\mathbf{x}=(0,0)`. The ring will contain the set of all positions that are :math:`d`-distant from :math:`\mathbf{x}=(0,0)`, with :math:`d\in[d]`. + + To display each bound of the ring, you can use ``fig.draw_circle(x, y, rad)`` where ``x``, ``y``, ``rad`` are *double* values. + + .. hint:: + + To access *double* bounds of an interval object ``x``, you can use the ``x.lb()``/``x.ub()`` methods for lower and upper bounds. + + | **1.7.** Now, repeat the operation with :math:`[\mathbf{x}]=[-0.1,0.1]\times[-0.1,0.1]`. You can for instance use the ``.inflate(0.1)`` method on ``x``. + | Is the result reliable, according to the sets :math:`[\mathbf{x}]` and :math:`[\mathbf{b}]`? You may display the box :math:`([\mathbf{x}]+[\mathbf{b}])` to understand how the reliable interval distance is computed. + + +.. from codac import * +.. +.. x = IntervalVector([[0,0],[0,0]]) +.. b = IntervalVector([[3,4],[2,3]]) +.. print(b) +.. +.. x.inflate(0.1) +.. +.. f = Function("a[2]", "b[2]", "sqrt((a[0]-b[0])^2+(a[1]-b[1])^2)") +.. +.. box = cart_prod(x,b) +.. r = f.eval(box) +.. +.. beginDrawing() +.. fig = VIBesFigMap("Map") +.. fig.set_properties(50, 50, 400, 400) # position and size +.. fig.draw_box(x, "red") +.. fig.draw_box(b) +.. fig.draw_box(b+x, "blue") +.. fig.draw_ring(0,0,r) +.. fig.show() # display all items of the figure +.. endDrawing() + + +.. admonition:: Technical documentation + + For full details about graphical features, please read the :ref:`sec-manual-vibes` page of the user manual. + + .. rubric:: Want to use colors? Here is an example you can try: + + .. tabs:: + + .. code-tab:: py + + fig.draw_box(x, "red[yellow]") # red: edge color of the box, yellow: fill color + + .. code-tab:: c++ + + fig.draw_box(x, "red[yellow]"); // red: edge color of the box, yellow: fill color + + +Contractors, :math:`\mathcal{C}([x])` +------------------------------------- + +This was an initial overview of what is Interval Analysis. Now, we will introduce concepts from Constraint Programming and see how the two approaches can be coupled for solving problems. + +In robotics, **constraints** are coming from the equations of the robot. They can be for instance the evolution function :math:`\mathbf{f}` or the observation equation with :math:`\mathbf{g}`. In the case of :abbr:`SLAM (Simultaneous Localization And Mapping)`, we may also define a constraint to express the inter-temporal relation between different states :math:`\mathbf{x}_1`, :math:`\mathbf{x}_2` at times :math:`t_1`, :math:`t_2`, for instance when a landmark has been seen two times. + +Now, we want to apply the constraints in order to solve our problem. In the Constraint Programming community, we apply constraints on **domains** that represent sets of feasible values. The previously mentioned sets (intervals, boxes, tubes) will be used as domains. + +We will use **contractors** to implement constraints on sets. They are mathematical operators used to *contract* (reduce) a set, for instance a box, without losing any feasible solution. This way, contractors can be applied safely any time we want on our domains. + +In Codac, the contractors are also defined by C++/Python objects and are prefixed with ``Ctc``. For this lesson, we will use the ``CtcFunction`` class to define a contractor according to a function :math:`f`. Note that the resulting contractor will aim at solving a constraint in the form :math:`f(\mathbf{x})=0`. This contractor has to be instantiated from a ``Function`` object defining the constraint. For instance, the simple constraint :math:`(x+y=a)` is expressed as :math:`f(x,y,a)=x+y-a=0`, and can be implemented as a contractor :math:`\mathcal{C}_+` with: + +.. tabs:: + + .. code-tab:: py + + ctc_add = CtcFunction(Function("x", "y", "a", "x+y-a")) + + .. code-tab:: c++ + + CtcFunction ctc_add(Function("x", "y", "a", "x+y-a")); + +.. admonition:: Exercise + + **1.8.** Define a contractor :math:`\mathcal{C}_\textrm{dist}` related to the distance constraint between two 2d positions :math:`\mathbf{x}` and :math:`\mathbf{b}\in\mathbb{R}^2`. We will use the distance function previously defined, but in the form :math:`f(\mathbf{x},\mathbf{b},d)=0`. + +| The contractor is then simply added to a **Contractor Network** (CN) that will manage the constraints on the different variables for solving the problem. +| For instance, we can use the previously defined :math:`\mathcal{C}_+` as: + +.. tabs:: + + .. code-tab:: py + + x = Interval(0,1) + y = Interval(-2,3) + a = Interval(1,20) + + cn = ContractorNetwork() # Creating a Contractor Network + cn.add(ctc_add, [x, y, a]) # Adding the C+ contractor to the network, + # applied on three domains listed between braces + cn.contract() + + # The three domains are then contracted as: + # x=[0, 1], y=[0, 3], a=[1, 4] + + .. code-tab:: c++ + + Interval x(0,1), y(-2,3), a(1,20); + + ContractorNetwork cn; // Creating a Contractor Network + cn.add(ctc_add, {x, y, a}); // Adding the C+ contractor to the network, + // applied on three domains listed between braces + cn.contract(); + + // The three domains are then contracted as: + // x=[0, 1], y=[0, 3], a=[1, 4] + +Note that one contractor can be added several times in the CN. This is useful to apply several constraints implemented by the same operator, on different sets of variables. + + +.. admonition:: Exercise + + | **1.9.** Define a Contractor Network to implement three distance constraints. + | Check the results with :math:`\mathcal{C}_\textrm{dist}([\mathbf{x}],[\mathbf{b}^i],[d])`, :math:`i\in\{1,2,3\}` and + + * :math:`[d]=[7,8]` + * :math:`[\mathbf{x}]=[0,0]^2` + * :math:`[\mathbf{b}^1]=[1.5,2.5]\times[4,11]` + * :math:`[\mathbf{b}^2]=[3,4]\times[4,6.5]` + * :math:`[\mathbf{b}^3]=[5,7]\times[5.5,8]` + + We recall that the same :math:`\mathcal{C}_\textrm{dist}` object can appear several times in the CN. + + Draw the :math:`[\mathbf{b}^i]` boxes (``.draw_box()``) and :math:`[d]` (``.draw_circle()``) before and after the contractions, in order to assess the contracting effects. + You should obtain this figure: + + .. figure:: img/ctc_dist.png + :width: 500px + + As you can see, the four domains have been contracted after the ``.contract()`` method: even the bounded range :math:`[d]` has been reduced thanks to the knowledge provided by the boxes. In Constraint Programming, we only define the constraints of the problem and let the resolution propagate the information as much as possible. + + +We now have all the material to compute a solver for state estimation in the next section. + + +.. rubric:: Footnotes + +.. [#f1] C++ objects originates from the `IBEX library `_. \ No newline at end of file diff --git a/doc/doc/tutorial-jnrr23/02-static-rangeonly/img/final_result.png b/doc/doc/tutorial-jnrr23/02-static-rangeonly/img/final_result.png new file mode 100644 index 0000000000000000000000000000000000000000..edbc192fb72819f3f0cf894338c183d7dd343bdb GIT binary patch literal 44927 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 literal 0 HcmV?d00001 diff --git a/doc/doc/tutorial-jnrr23/02-static-rangeonly/img/fixedpoint_animation.gif b/doc/doc/tutorial-jnrr23/02-static-rangeonly/img/fixedpoint_animation.gif new file mode 100644 index 0000000000000000000000000000000000000000..fd701742438057837770da5265e7db1c84c49187 GIT binary patch literal 223843 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 z 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�?xx7FA;BSRe_;Z0l@T_aOI7W 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 H(`HYWRt7!n9N)h$$|!0Z8gl)x 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 + + using namespace std; + using namespace codac; + + int main() + { + // Truth (unknown pose) + Vector x_truth({0.,0.,M_PI/6.}); // (x,y,heading) + + // Creating random map of landmarks + int nb_landmarks = 3; + IntervalVector map_area(2, Interval(-8.,8.)); + vector v_b = + DataLoader::generate_landmarks_boxes(map_area, nb_landmarks); + + // The following function generates a set of [range]x[bearing] values + vector v_obs = + DataLoader::generate_static_observations(x_truth, v_b, false); + + // We keep range-only observations from v_obs, and add uncertainties + vector v_d; + for(auto& obs : v_obs) + v_d.push_back(obs[0].inflate(0.1)); // adding uncertainties: [-0.1,0.1] + + // Set of feasible positions for x: x ϵ [-∞,∞]×[-∞,∞] + IntervalVector x(2); + + // ... + + +Finally, the graphical functions are given by: + +.. tabs:: + + .. code-tab:: py + + # ... + + beginDrawing() + + fig = VIBesFigMap("Map") + fig.set_properties(50, 50, 600, 600) + + for b in v_b: + fig.add_beacon(b.mid(), 0.2) + + for i in range(0,len(v_d)): + fig.draw_ring(v_b[i][0].mid(), v_b[i][1].mid(), v_d[i], "gray") + + fig.draw_vehicle(x_truth, size=0.7) + fig.draw_box(x) # estimated position + fig.show() + + endDrawing() + + .. code-tab:: c++ + + // ... + + vibes::beginDrawing(); + + VIBesFigMap fig("Map"); + fig.set_properties(50, 50, 600, 600); + + for(const auto& b : v_b) + fig.add_beacon(b.mid(), 0.2); + + for(int i = 0 ; i < nb_landmarks ; i++) + fig.draw_ring(v_b[i][0].mid(), v_b[i][1].mid(), v_d[i], "gray"); + + fig.draw_vehicle(x_truth, 0.7); // last param: vehicle size + fig.draw_box(x); // estimated position + fig.show(); + + vibes::endDrawing(); + } + + +.. admonition:: Exercise + + **2.1.** Before the code related to the graphical part, compute the state estimation of the robot by contracting the box :math:`[\mathbf{x}]` initialized to :math:`[-\infty,\infty]^2` with a Contractor Network: + + * :math:`[\mathbf{x}]` represents the unknown 2d position of the robot + * ``v_d`` is the set of bounded measurements :math:`\{[d^{1}],[d^{2}],[d^{3}]\}` + * ``v_b`` is the set of landmarks with bounded positions :math:`\{[\mathbf{b}^{1}],[\mathbf{b}^{2}],[\mathbf{b}^{3}]\}` + + For this, you can use the :math:`\mathcal{C}_{\textrm{dist}}` contractor you defined in the previous section. + + You should obtain a figure similar to this: + + .. figure:: img/final_result.png + :width: 500px + + Range-only localization: expected result. The black painted box represents the set of feasible positions for our robot. + + + Due to the randomness of the landmarks, the geometry is sometimes bad and does not allow an accurate contraction: symmetrical solutions are possible, and the box :math:`[\mathbf{x}]` encloses them all. You can execute the code several times to see how the geometry influences the result. + + +How does it work? +----------------- + +.. rubric:: Combining the constraints + +The Contractor Network you have defined managed the contractions provided by the three :math:`\mathcal{C}_{\textrm{dist}}` contractors. + +Each constraint alone would not allow a good contraction, since it would contract :math:`[\mathbf{x}]` to the box enclosing the circle corresponding to :math:`d^k`. It is the intersection of the three constraints that makes the approach powerful. + +.. rubric:: Fixed point resolution + +There are **dependencies between the constraints** that all act on the same variable :math:`\mathbf{x}`. +The Contractor Network has then made a **fixed point resolution method** for solving the problem. + +When a :math:`\mathcal{C}_{\textrm{dist}}` contractor reduces the box :math:`[\mathbf{x}]`, it may raise new contraction possibilities coming from the other constraints. It becomes interesting to call again the previous contractors (start another iteration) in order to take benefit from any contraction. An iterative resolution process is then used, where the contractors are called until a fixed point has been reached. By *fixed point* we mean that none of the domains :math:`[\mathbf{x}]` and :math:`[d^{k}]` has been contracted during a complete iteration. + +The following figure provides the synoptic of this state estimation, performed by the Contractor Network. In this example, constraints have been propagated over 7 iterations in a very short amount of time. + +.. figure:: img/fixedpoint_animation.gif + :width: 500px + + + + +End of first step! +------------------ + +That's about all for static localization! + +.. You can submit your answers for the questions of Lessons A and B to the `MOOC platform `_ so that we can .. evaluate them for the diploma. + +Next lesson will introduce other concepts related to trajectories and differential equations. Our goal will be to build a SLAM solver. \ No newline at end of file diff --git a/doc/doc/tutorial-jnrr23/03-towards-slam/img/final_result.png b/doc/doc/tutorial-jnrr23/03-towards-slam/img/final_result.png new file mode 100644 index 0000000000000000000000000000000000000000..edbc192fb72819f3f0cf894338c183d7dd343bdb GIT binary patch literal 44927 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 literal 0 HcmV?d00001 diff --git a/doc/doc/tutorial-jnrr23/03-towards-slam/img/fixedpoint_animation.gif b/doc/doc/tutorial-jnrr23/03-towards-slam/img/fixedpoint_animation.gif new file mode 100644 index 0000000000000000000000000000000000000000..fd701742438057837770da5265e7db1c84c49187 GIT binary patch literal 223843 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 z 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�?xx7FA;BSRe_;Z0l@T_aOI7W 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 H(`HYWRt7!n9N)h$$|!0Z8gl)x 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. +This will allow you to perform the state estimation of a static robot between some landmarks by the end of :ref:`Lesson B `. .. contents:: Content of this lesson From 864f624bc670c36973de6c03772137a6401a2603 Mon Sep 17 00:00:00 2001 From: SimonRohou Date: Fri, 20 Oct 2023 12:00:23 +0200 Subject: [PATCH 152/256] [doc] updated Lesson A --- doc/doc/_static/custom-sphinx.css | 11 +++++++++++ doc/doc/conf.py.in | 2 ++ doc/doc/tutorial/01-basics/index.rst | 29 ++++++++++++++++------------ 3 files changed, 30 insertions(+), 12 deletions(-) diff --git a/doc/doc/_static/custom-sphinx.css b/doc/doc/_static/custom-sphinx.css index 46f62eb30..37441c72e 100644 --- a/doc/doc/_static/custom-sphinx.css +++ b/doc/doc/_static/custom-sphinx.css @@ -97,6 +97,17 @@ } +/* Right alignment + gray color (for right-align notes) */ + + .right-aligned-note + { + margin-right: 10px; + display: block; + float: right; + color: gray; + } + + /* */ .wy-table-responsive table td diff --git a/doc/doc/conf.py.in b/doc/doc/conf.py.in index 6b58c1fe6..211ece693 100644 --- a/doc/doc/conf.py.in +++ b/doc/doc/conf.py.in @@ -61,6 +61,8 @@ breathe_projects = { rst_prolog = """ .. role:: underline :class: underline +.. role:: right-aligned-note + :class: right-aligned-note """ # GitHub repo diff --git a/doc/doc/tutorial/01-basics/index.rst b/doc/doc/tutorial/01-basics/index.rst index b3886ba35..86262497c 100644 --- a/doc/doc/tutorial/01-basics/index.rst +++ b/doc/doc/tutorial/01-basics/index.rst @@ -1,5 +1,10 @@ .. _sec-tuto-01: +.. # define a hard line break for HTML +.. |br| raw:: html + +
+ Lesson A: Getting started with intervals and contractors ======================================================== @@ -169,21 +174,21 @@ Codac is using C++/Python objects to represent intervals and boxes [#f1]_: For full details about ``Interval`` and ``IntervalVector`` objects, please read the :ref:`sec-manual-intervals` page of the user manual. - .. admonition:: Exercise **A.1.** Let us consider two intervals :math:`[x]=[8,10]` and :math:`[y]=[1,2]`. Without coding the operation, what would be the result of :math:`[x]/[y]` (:math:`[x]` divided by :math:`[y]`)? Remember that the result of this interval-division is also an interval enclosing all feasible divisions. - **A.2.** In your new project, compute and print the following simple operations on intervals: + **A.2.** In your new project, compute and print the following simple operations on intervals: |br| + :right-aligned-note:`Solutions are given below` |br| - * :math:`[-2,4]\cdot[1,3]` - * :math:`[8,10]/[-1,0]` - * :math:`[-2,4]\sqcup[6,7]` with operator ``|`` - * :math:`\max([2,7],[1,9])` - * :math:`\max(\varnothing,[1,2])` - * :math:`\cos([-\infty,\infty])` - * :math:`[-1,4]^2` with function ``sqr()`` - * :math:`([1,2]\cdot[-1,3]) + \max([1,3]\cap[6,7],[1,2])` + * :math:`[-2,4]\cdot[1,3]` :right-aligned-note:`[-6,12]` + * :math:`[8,10]/[-1,0]` :right-aligned-note:`[-∞,-8]` + * :math:`[-2,4]\sqcup[6,7]` with operator ``|`` :right-aligned-note:`[-2,7]` + * :math:`\max([2,7],[1,9])` :right-aligned-note:`[2,9]` + * :math:`\max(\varnothing,[1,2])` :right-aligned-note:`∅` + * :math:`\cos([-\infty,\infty])` :right-aligned-note:`[-1,1]` + * :math:`[-1,4]^2` with function ``sqr()`` :right-aligned-note:`[0,16]` + * :math:`([1,2]\cdot[-1,3]) + \max([1,6]\cap[5,7],[1,2])` :right-aligned-note:`[3,12]` | Note that :math:`\sqcup` is the hull union (``|``), *i.e.*, :math:`[x]\sqcup[y] = [[x]\cup[y]]`. | *For instance:* :math:`[-1,2]\sqcup[4,6]=[-1,6]` @@ -299,11 +304,11 @@ The graphical tool `VIBes `_ has | **A.6.** Before the ``.show()`` method, draw the boxes :math:`[\mathbf{x}]` and :math:`[\mathbf{b}]` with the ``fig.draw_box(..)`` method. The computed interval range :math:`[d]` can be displayed as a ring centered on :math:`\mathbf{x}=(0,0)`. The ring will contain the set of all positions that are :math:`d`-distant from :math:`\mathbf{x}=(0,0)`, with :math:`d\in[d]`. - To display each bound of the ring, you can use ``fig.draw_circle(x, y, rad)`` where ``x``, ``y``, ``rad`` are *double* values. + To display each bound of the ring, you can use ``fig.draw_circle(x, y, rad)`` where ``x``, ``y``, ``rad`` are real values. .. hint:: - To access *double* bounds of an interval object ``x``, you can use the ``x.lb()``/``x.ub()`` methods for lower and upper bounds. + To access real bounds of an ``Interval`` object ``x``, you can use the ``x.lb()``/``x.ub()`` methods for lower and upper bounds. This also works with ``IntervalVector``, returning vector items. | **A.7.** Now, repeat the operation with :math:`[\mathbf{x}]=[-0.1,0.1]\times[-0.1,0.1]`. You can for instance use the ``.inflate(0.1)`` method on ``x``. | Is the result reliable, according to the sets :math:`[\mathbf{x}]` and :math:`[\mathbf{b}]`? You may display the box :math:`([\mathbf{x}]+[\mathbf{b}])` to understand how the reliable interval distance is computed. From 88d860fb1a078ca1adddae73811054bef93b6c94 Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Sat, 21 Oct 2023 16:15:37 +0200 Subject: [PATCH 153/256] Attempt to solve intermittent download problems when installing doxygen --- .github/workflows/vcmatrix.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/vcmatrix.yml b/.github/workflows/vcmatrix.yml index 88b6bb5ce..80a8eb5a6 100644 --- a/.github/workflows/vcmatrix.yml +++ b/.github/workflows/vcmatrix.yml @@ -56,7 +56,7 @@ jobs: if: runner.os=='Windows' - run: choco install -y -r --no-progress eigen --version=3.4.0 ${{ matrix.cfg.choco_flags }} if: runner.os=='Windows' - - run: choco install -y -r --no-progress doxygen.install --version=1.9.6 & choco install -y -r --no-progress graphviz & pip install --upgrade wheel setuptools sphinx breathe sphinx-issues sphinx-tabs sphinx_rtd_theme sphinx-reredirects + - run: wget https://packages.ensta-bretagne.fr/choco/doxygen.install.1.9.6.nupkg --no-check-certificate -nv & choco upgrade -y -r --no-progress -s . doxygen.install --version=1.9.6 & del /f /q doxygen.install.1.9.6.nupkg & choco install -y -r --no-progress graphviz & pip install --upgrade wheel setuptools sphinx breathe sphinx-issues sphinx-tabs sphinx_rtd_theme sphinx-reredirects if: runner.os=='Windows' - run: | wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20231007/ibex.2.8.9.20231007.nupkg --no-check-certificate -nv From 75b652ecc87ad38eb9948ec2658f53c6de292779 Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Sun, 22 Oct 2023 16:34:56 +0200 Subject: [PATCH 154/256] Attempt to force the selection of a compatible version of doxygen --- doc/CMakeLists.txt | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt index 19e5f7112..58ba02ac2 100644 --- a/doc/CMakeLists.txt +++ b/doc/CMakeLists.txt @@ -10,8 +10,8 @@ install(FILES ${CMAKE_CURRENT_BINARY_DIR}/mathjax_stmaryrd.js # Technical API documentation (Doxygen) if(WITH_PYTHON) - - find_package(Doxygen) + + find_package(Doxygen 1.0.0...1.9.6) if(NOT DOXYGEN_FOUND) @@ -19,6 +19,12 @@ install(FILES ${CMAKE_CURRENT_BINARY_DIR}/mathjax_stmaryrd.js else() + if(NOT DOXYGEN_DIR) + message(STATUS "Doxygen ${DOXYGEN_VERSION} found.") + else() + message(STATUS "Doxygen ${DOXYGEN_VERSION} found in ${DOXYGEN_DIR}.") + endif() + # Includes CMake commands in config file: configure_file(api/Doxyfile.in api/Doxyfile) From 9d99334a1aa232084a1c934b19a29f41fa06b227 Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Tue, 24 Oct 2023 21:30:31 +0200 Subject: [PATCH 155/256] Move documentation about Rosetta for macOS (arm64) --- doc/doc/install/01-installation-python.rst | 5 ----- doc/doc/install/01-installation.rst | 5 +++++ 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/doc/doc/install/01-installation-python.rst b/doc/doc/install/01-installation-python.rst index 5313a1790..e11e65ba5 100644 --- a/doc/doc/install/01-installation-python.rst +++ b/doc/doc/install/01-installation-python.rst @@ -53,11 +53,6 @@ The :gbg:`✓` configurations are officially supported at the moment: If a configuration in this table does not work, please `contact us `_. -.. warning:: - - | **macOS Big Sur and later (arm64):** - | `Rosetta `_ might be necessary to run `VIBes `_. - .. warning:: | **macOS Big Sur and later (x86_64):** diff --git a/doc/doc/install/01-installation.rst b/doc/doc/install/01-installation.rst index 0fb0dae8d..ad8893491 100644 --- a/doc/doc/install/01-installation.rst +++ b/doc/doc/install/01-installation.rst @@ -74,6 +74,11 @@ Some methods have been implemented in Codac in order to extend VIBes' features t You can `download the VIBes viewer directly from the official page `_ (click on the *Last Release* link). +.. warning:: + + | **macOS (arm64):** + | `Rosetta `_ might be necessary to run `VIBes `_. + .. admonition:: (optional) Get the very last version of VIBes from the sources You can also install the last version from the sources available on `the GitHub development repository `_. From 790acb290aca2beeb53639b212e954374ba068ff Mon Sep 17 00:00:00 2001 From: SimonRohou Date: Thu, 26 Oct 2023 21:58:38 +0200 Subject: [PATCH 156/256] [action] minor updates --- src/core/2/actions/codac2_Action.cpp | 8 ++++++-- src/core/2/actions/codac2_Action.h | 1 + 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/core/2/actions/codac2_Action.cpp b/src/core/2/actions/codac2_Action.cpp index fe0eb44e6..33749d50e 100644 --- a/src/core/2/actions/codac2_Action.cpp +++ b/src/core/2/actions/codac2_Action.cpp @@ -26,7 +26,7 @@ namespace codac2 return (a > 0) ? 1 : ((a < 0) ? -1 : 0); } - OctaSym::OctaSym(const std::vector& s) : std::vector(s) + OctaSym::OctaSym(const vector& s) : vector(s) { for(const auto& i : s) { @@ -34,6 +34,10 @@ namespace codac2 } } + OctaSym::OctaSym(initializer_list s) + : OctaSym(vector(s)) + { } + IntervalVector OctaSym::operator()(const IntervalVector& x) const { assert((size_t)x.size() == size()); @@ -64,7 +68,7 @@ namespace codac2 return s3; } - std::ostream& operator<<(std::ostream& str, const OctaSym& s) + ostream& operator<<(ostream& str, const OctaSym& s) { str << "("; for(size_t i = 0 ; i < s.size() ; i++) diff --git a/src/core/2/actions/codac2_Action.h b/src/core/2/actions/codac2_Action.h index 163b24806..eb3f57efa 100644 --- a/src/core/2/actions/codac2_Action.h +++ b/src/core/2/actions/codac2_Action.h @@ -35,6 +35,7 @@ namespace codac2 { public: + OctaSym(std::initializer_list s); OctaSym(const std::vector& s); CtcAction operator()(codac::Ctc& ctc) const; codac::IntervalVector operator()(const codac::IntervalVector& x) const; From 4d0c819d6caff90227856ce62efa53caaf2e37f1 Mon Sep 17 00:00:00 2001 From: SimonRohou Date: Thu, 26 Oct 2023 21:58:49 +0200 Subject: [PATCH 157/256] [codac2] restoring simple example --- examples/codac2/01/CMakeLists.txt | 2 +- examples/codac2/01/main.cpp | 20 ++++++++++---------- examples/codac2/01/main_test_sample.cpp | 6 +++--- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/examples/codac2/01/CMakeLists.txt b/examples/codac2/01/CMakeLists.txt index 143ab46eb..586755219 100644 --- a/examples/codac2/01/CMakeLists.txt +++ b/examples/codac2/01/CMakeLists.txt @@ -38,7 +38,7 @@ # Compilation - add_executable(${PROJECT_NAME} main_test_sample.cpp) + add_executable(${PROJECT_NAME} main.cpp) target_compile_options(${PROJECT_NAME} PUBLIC ${CODAC_CXX_FLAGS}) target_include_directories(${PROJECT_NAME} SYSTEM PUBLIC ${CODAC_INCLUDE_DIRS} ${EIGEN3_INCLUDE_DIRS}) target_link_libraries(${PROJECT_NAME} PUBLIC ${CODAC_LIBRARIES} Ibex::ibex ${CODAC_LIBRARIES} Ibex::ibex) \ No newline at end of file diff --git a/examples/codac2/01/main.cpp b/examples/codac2/01/main.cpp index 4e9f2dc15..a35723753 100644 --- a/examples/codac2/01/main.cpp +++ b/examples/codac2/01/main.cpp @@ -5,24 +5,24 @@ using namespace codac; int main() { - codac2::TDomain tdomain(Interval(0,10), 0.01, true); // last argument creates "gates" (degenerated slices at scalar timesteps) - codac2::Tube x(2, tdomain, + auto tdomain = codac2::create_tdomain(Interval(0,10), 0.01, true); // last argument creates "gates" (degenerated slices at scalar timesteps) + codac2::Tube x(tdomain, TFunction("(sin(sqrt(t)+((t-5)^2)*[-0.01,0.01]) ; cos(t)+sin(t/0.2)*[-0.1,0.1])")); - codac2::Tube u(2, tdomain); - - TFunction tf("x[2]", "u[2]", "(sin(x[1]) ; -sin(x[0]))"); - codac2::CtcDiffInclusion ctc_diffincl(tf); - ctc_diffincl.contract(x,u); + codac2::Tube u(tdomain); + // If you want to iterate each slice (including gates = degenerate slices) + for(auto& sx : x) + { + //cout << sx << endl; + } vibes::beginDrawing(); - codac::TubeVector x_codac1 = x.to_codac1(); // may take time - codac::Tube xi_codac1 = x[1].to_codac1(); // may take time + codac::TubeVector x_codac1 = codac2::to_codac1(x); // may take time VIBesFigTube fig("Tube"); fig.set_properties(100, 100, 600, 300); - fig.add_tube(&xi_codac1, "x"); + fig.add_tube(&x_codac1[1], "x"); fig.show(true); vibes::endDrawing(); diff --git a/examples/codac2/01/main_test_sample.cpp b/examples/codac2/01/main_test_sample.cpp index e88470fea..e7ce7c264 100644 --- a/examples/codac2/01/main_test_sample.cpp +++ b/examples/codac2/01/main_test_sample.cpp @@ -6,15 +6,15 @@ using namespace codac; int main() { { - codac2::TDomain tdomain(Interval(0,10), 1., false); - codac2::Tube x(2, tdomain); + auto tdomain = codac2::create_tdomain(Interval(0,10), 1., false); + codac2::Tube x(tdomain, IntervalVector(2)); for(auto& sx : x) { if(sx.t0_tf().contains(5.2)) { cout << "sample" << endl; - tdomain.sample(5.2); + tdomain->sample(5.2); } cout << sx << endl; } From fe42c5c5e7339baa2fe2915d92706e0a41d2ae7e Mon Sep 17 00:00:00 2001 From: SimonRohou Date: Sun, 29 Oct 2023 23:28:33 +0100 Subject: [PATCH 158/256] [graphics] no axis limits option on VIBesFigMap --- src/robotics/graphics/codac_VIBesFigMap.cpp | 11 +++++++++-- src/robotics/graphics/codac_VIBesFigMap.h | 3 +++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/robotics/graphics/codac_VIBesFigMap.cpp b/src/robotics/graphics/codac_VIBesFigMap.cpp index b8efd5f98..a2bfc8b86 100644 --- a/src/robotics/graphics/codac_VIBesFigMap.cpp +++ b/src/robotics/graphics/codac_VIBesFigMap.cpp @@ -50,6 +50,11 @@ namespace codac m_draw_tubes_backgrounds = enable; } + void VIBesFigMap::no_axis_limits() + { + _no_axis_limits = true; + } + void VIBesFigMap::show() { typename map::const_iterator it_tubes; @@ -61,7 +66,8 @@ namespace codac for(it_trajs = m_map_trajs.begin(); it_trajs != m_map_trajs.end(); it_trajs++) m_view_box |= draw_trajectory(it_trajs->first); - axis_limits(m_view_box, true, 0.02); + if(!_no_axis_limits) + axis_limits(m_view_box, true, 0.02); } void VIBesFigMap::show(float robot_size) @@ -710,7 +716,8 @@ namespace codac assert(pose.size() == 2 || pose.size() == 3); float robot_size = size == -1 ? m_robot_size : size; double robot_heading = pose.size() == 3 ? pose[2] : 0.; - axis_limits(m_view_box | pose.subvector(0,1), true); + if(!_no_axis_limits) + axis_limits(m_view_box | pose.subvector(0,1), true); //vibes::drawTank(pose[0], pose[1], robot_heading * 180. / M_PI, robot_size, "black[yellow]", params); vibes::drawAUV(pose[0], pose[1], robot_heading * 180. / M_PI, robot_size, "black[yellow]", params); } diff --git a/src/robotics/graphics/codac_VIBesFigMap.h b/src/robotics/graphics/codac_VIBesFigMap.h index 5663e682a..42f067694 100644 --- a/src/robotics/graphics/codac_VIBesFigMap.h +++ b/src/robotics/graphics/codac_VIBesFigMap.h @@ -77,6 +77,8 @@ namespace codac */ void enable_tubes_backgrounds(bool enable = true); + void no_axis_limits(); + /** * \brief Displays this figure */ @@ -559,6 +561,7 @@ namespace codac bool m_draw_tubes_backgrounds = true; //!< if `true`, will highlight tubes contractions bool m_smooth_drawing = false; //!< if `true`, a smooth rendering of tubes will be done float m_robot_size = 5.5; //!< if `0`, no robot display + bool _no_axis_limits = false; unsigned int m_tube_max_nb_disp_slices = TUBE_MAX_NB_DISPLAYED_SLICES; //!< limit for slices display unsigned int m_traj_max_nb_disp_points = TRAJ_MAX_NB_DISPLAYED_POINTS; //!< limit for traj points display From c8fe9ec79476d06ce5f22e0ed3bf90bcae5975e8 Mon Sep 17 00:00:00 2001 From: SimonRohou Date: Sun, 29 Oct 2023 23:29:42 +0100 Subject: [PATCH 159/256] [py] corrected bug in IntervalMatrix from Numpy --- python/codac/tests/test_numpy.py | 56 ++++++++++++++++++ .../interval/codac_py_IntervalMatrix.cpp | 36 ++++++++++-- .../domains/interval/codac2_IntervalMatrix.h | 6 +- .../domains/interval/codac2_IntervalVector.h | 6 +- src/core/2/variables/codac2_Matrix.h | 57 ++++++++++++++++--- src/core/2/variables/codac2_Vector.h | 4 +- 6 files changed, 143 insertions(+), 22 deletions(-) create mode 100644 python/codac/tests/test_numpy.py diff --git a/python/codac/tests/test_numpy.py b/python/codac/tests/test_numpy.py new file mode 100644 index 000000000..99f2a7d00 --- /dev/null +++ b/python/codac/tests/test_numpy.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python + +# Codac tests +# --------------------------------------------------------------------------- +# \date 2023 +# \author Simon Rohou +# \copyright Copyright 2023 Codac Team +# \license This program is distributed under the terms of +# the GNU Lesser General Public License (LGPL). + +import unittest +import numpy as np +from codac import * + +class TestNumpyMatrices(unittest.TestCase): + + def test_numpytocodac(self): + + cdA=IntervalMatrix(2,6) + cdA[0][0]=Interval(1) + cdA[0][1]=Interval(3) + cdA[0][2]=Interval(5) + cdA[0][3]=Interval(7) + cdA[0][4]=Interval(9) + cdA[0][5]=Interval(11) + cdA[1][0]=Interval(2) + cdA[1][1]=Interval(4) + cdA[1][2]=Interval(6) + cdA[1][3]=Interval(8) + cdA[1][4]=Interval(10) + cdA[1][5]=Interval(12) + + npA=np.array([[1.,3.,5.,7.,9.,11.],[2.,4.,6.,8.,10.,12.]]) + self.assertEqual(IntervalMatrix(npA), cdA) + + def test_numpytocodac_withtranspose(self): + + cdA=IntervalMatrix(2,6) + cdA[0][0]=Interval(1) + cdA[0][1]=Interval(3) + cdA[0][2]=Interval(5) + cdA[0][3]=Interval(7) + cdA[0][4]=Interval(9) + cdA[0][5]=Interval(11) + cdA[1][0]=Interval(2) + cdA[1][1]=Interval(4) + cdA[1][2]=Interval(6) + cdA[1][3]=Interval(8) + cdA[1][4]=Interval(10) + cdA[1][5]=Interval(12) + + npA=np.array([[1.,2.],[3.,4.],[5.,6.],[7.,8.],[9.,10.],[11.,12.]]).T + self.assertEqual(IntervalMatrix(npA), cdA) + +if __name__ == '__main__': + unittest.main() \ No newline at end of file diff --git a/python/src/core/domains/interval/codac_py_IntervalMatrix.cpp b/python/src/core/domains/interval/codac_py_IntervalMatrix.cpp index abac3d269..6892720c7 100644 --- a/python/src/core/domains/interval/codac_py_IntervalMatrix.cpp +++ b/python/src/core/domains/interval/codac_py_IntervalMatrix.cpp @@ -69,6 +69,16 @@ string to_string(const IntervalMatrix& x) return ss.str(); } +// Inspired by Numpy documentation +// See: https://pybind11.readthedocs.io/en/stable/advanced/pycpp/numpy.html + + #include "codac2_IntervalMatrix.h" + /* Bind MatrixXd (or some other Eigen type) to Python */ + typedef Eigen::MatrixXd Matrix; + + //typedef Eigen::Matrix::Scalar Scalar; + constexpr bool rowMajor = !true;//Eigen::Matrix::Flags & Eigen::RowMajorBit; + void export_IntervalMatrix(py::module& m) { py::class_ interval_matrix(m, "IntervalMatrix"); @@ -79,20 +89,34 @@ void export_IntervalMatrix(py::module& m) .def(py::init()) .def(py::init(&create_from_list)) - .def(py::init([](py::buffer b) // create for instance an IntervalMatrix from a NumPy matrix + .def(py::init([](py::buffer b) { + // Inspired by Numpy documentation + // See: https://pybind11.readthedocs.io/en/stable/advanced/pycpp/numpy.html + + // Note: use raw data does not work because of strides, + // see for instance transpose operations on numpy matrices + + typedef Eigen::Stride Strides; + // Request a buffer descriptor from Python py::buffer_info info = b.request(); - // Some sanity checks... + // Some basic validation checks... if(info.format != py::format_descriptor::format()) - throw std::runtime_error("Incompatible format: expected a double array"); + throw std::runtime_error("Incompatible format: expected a double array!"); if(info.ndim != 2) - throw std::runtime_error("Incompatible buffer dimension"); + throw std::runtime_error("Incompatible buffer dimension!"); + + auto strides = Strides( + info.strides[rowMajor ? 0 : 1] / (py::ssize_t)sizeof(double), + info.strides[rowMajor ? 1 : 0] / (py::ssize_t)sizeof(double)); + + auto map = Eigen::Map( + static_cast(info.ptr), info.shape[0], info.shape[1], strides); - ibex::Matrix m((int)info.shape[0], (int)info.shape[1], static_cast(info.ptr)); - return IntervalMatrix(m); + return codac2::to_codac1(codac2::Matrix(map)); })) .def("__getitem__", [](IntervalMatrix& s, size_t index) -> IntervalVector& diff --git a/src/core/2/domains/interval/codac2_IntervalMatrix.h b/src/core/2/domains/interval/codac2_IntervalMatrix.h index fc2121e15..19a5038fd 100644 --- a/src/core/2/domains/interval/codac2_IntervalMatrix.h +++ b/src/core/2/domains/interval/codac2_IntervalMatrix.h @@ -51,7 +51,7 @@ namespace codac2 init(x); } - explicit IntervalMatrix_(size_t nb_rows, size_t nb_cols, double bounds[][2]) + explicit IntervalMatrix_(size_t nb_rows, size_t nb_cols, const double bounds[][2]) : IntervalMatrix_(nb_rows, nb_cols) { size_t k = 0; @@ -67,7 +67,7 @@ namespace codac2 assert(k == this->size()); } - explicit IntervalMatrix_(double bounds[][2]) + explicit IntervalMatrix_(const double bounds[][2]) : IntervalMatrix_(R, C, bounds) { } @@ -596,7 +596,7 @@ namespace codac2 : IntervalMatrix_<>(nb_rows, nb_cols, x) { } - explicit IntervalMatrix(size_t nb_rows, size_t nb_cols, double bounds[][2]) + explicit IntervalMatrix(size_t nb_rows, size_t nb_cols, const double bounds[][2]) : IntervalMatrix_<>(nb_rows, nb_cols, bounds) { } diff --git a/src/core/2/domains/interval/codac2_IntervalVector.h b/src/core/2/domains/interval/codac2_IntervalVector.h index 1e3d1db51..613987269 100644 --- a/src/core/2/domains/interval/codac2_IntervalVector.h +++ b/src/core/2/domains/interval/codac2_IntervalVector.h @@ -68,11 +68,11 @@ namespace codac2 (*this)[i] = Interval(v[i]); } - explicit IntervalVector_(size_t n, double bounds[][2]) + explicit IntervalVector_(size_t n, const double bounds[][2]) : IntervalMatrix_(n,1,bounds) { } - explicit IntervalVector_(double bounds[][2]) + explicit IntervalVector_(const double bounds[][2]) : IntervalVector_(this->size(), bounds) { } @@ -278,7 +278,7 @@ namespace codac2 : IntervalVector_<>(v) { } - explicit IntervalVector(size_t n, double bounds[][2]) + explicit IntervalVector(size_t n, const double bounds[][2]) : IntervalVector_<>(n, bounds) { } diff --git a/src/core/2/variables/codac2_Matrix.h b/src/core/2/variables/codac2_Matrix.h index 3cf9c142d..b3e8fa032 100644 --- a/src/core/2/variables/codac2_Matrix.h +++ b/src/core/2/variables/codac2_Matrix.h @@ -19,7 +19,7 @@ namespace codac2 { using Eigen::Dynamic; - template + template class Matrix_ : public Eigen::Matrix { public: @@ -28,14 +28,14 @@ namespace codac2 : Eigen::Matrix() { } - Matrix_(size_t nb_rows, size_t nb_cols) + Matrix_(int nb_rows, int nb_cols) : Eigen::Matrix(nb_rows, nb_cols) { assert(R == Dynamic || R == (int)nb_rows); assert(C == Dynamic || C == (int)nb_cols); } - Matrix_(size_t nb_rows, size_t nb_cols, double x) + Matrix_(int nb_rows, int nb_cols, double x) : Eigen::Matrix(nb_rows, nb_cols) { assert(R == Dynamic || R == (int)nb_rows); @@ -43,12 +43,12 @@ namespace codac2 init(x); } - explicit Matrix_(size_t nb_rows, size_t nb_cols, double values[]) + explicit Matrix_(int nb_rows, int nb_cols, const double values[]) : Matrix_(nb_rows, nb_cols) { - size_t k = 0; - for(size_t i = 0 ; i < nb_rows ; i++) - for(size_t j = 0 ; j < nb_cols ; j++) + int k = 0; + for(int i = 0 ; i < nb_rows ; i++) + for(int j = 0 ; j < nb_cols ; j++) { if(values == 0) // in case the user called Matrix_(r,c,0) and 0 is interpreted as NULL! (*this)(i,j) = 0.; @@ -58,7 +58,7 @@ namespace codac2 } } - explicit Matrix_(double values[]) + explicit Matrix_(const double values[]) : Matrix_(R, C, values) { } @@ -184,6 +184,17 @@ namespace codac2 os << ")"; return os; } + + #include "ibex_Matrix.h" + template + ibex::Matrix to_codac1(const Matrix_& x) + { + ibex::Matrix x_(x.rows(), x.cols()); + for(size_t i = 0 ; i < x.rows() ; i++) + for(size_t j = 0 ; j < x.cols() ; j++) + x_[i][j] = x(i,j); + return x_; + } template Matrix_ floor(const Matrix_& x) @@ -221,6 +232,36 @@ namespace codac2 return f; } + class Matrix : public Matrix_<> + { + public: + + explicit Matrix(size_t nb_rows, size_t nb_cols) + : Matrix_<>(nb_rows, nb_cols) + { } + + + explicit Matrix(size_t nb_rows, size_t nb_cols, double x) + : Matrix_<>(nb_rows, nb_cols, x) + { } + + explicit Matrix(size_t nb_rows, size_t nb_cols, const double values[]) + : Matrix_<>(nb_rows, nb_cols, values) + { } + + Matrix(const Matrix_<>& x) + : Matrix_<>(x) + { } + + Matrix(std::initializer_list> l) + : Matrix_<>(l) + { } + + template + explicit Matrix(const Matrix_& v) + : Matrix_<>(v) + { } + }; } // namespace codac #endif \ No newline at end of file diff --git a/src/core/2/variables/codac2_Vector.h b/src/core/2/variables/codac2_Vector.h index fe9bb8476..7c30cd759 100644 --- a/src/core/2/variables/codac2_Vector.h +++ b/src/core/2/variables/codac2_Vector.h @@ -55,11 +55,11 @@ namespace codac2 static_assert(M == Dynamic || M == N); } - explicit Vector_(size_t n, double values[]) + explicit Vector_(size_t n, const double values[]) : Matrix_(n,1,values) { } - explicit Vector_(double values[]) + explicit Vector_(const double values[]) : Matrix_(N,1,values) { } From 9721e5f56175a00922da1bf1d2064e3a9d73079a Mon Sep 17 00:00:00 2001 From: SimonRohou Date: Thu, 7 Dec 2023 16:28:00 +0100 Subject: [PATCH 160/256] [py] added binding for Ctc3BCid --- python/CMakeLists.txt | 1 + python/codac_py_core.cpp | 2 + .../contractors/static/codac_py_Ctc3BCid.cpp | 45 +++++++++++++++++++ 3 files changed, 48 insertions(+) create mode 100644 python/src/core/contractors/static/codac_py_Ctc3BCid.cpp diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt index a8aa2a1c0..f1105a619 100644 --- a/python/CMakeLists.txt +++ b/python/CMakeLists.txt @@ -56,6 +56,7 @@ src/core/cn/codac_py_Variable.cpp src/core/contractors/static/codac_py_Ctc.cpp + src/core/contractors/static/codac_py_Ctc3BCid.cpp src/core/contractors/static/codac_py_CtcBox.cpp src/core/contractors/static/codac_py_CtcCN.cpp src/core/contractors/static/codac_py_CtcCartProd.cpp diff --git a/python/codac_py_core.cpp b/python/codac_py_core.cpp index b80934a2b..9bb39c72c 100644 --- a/python/codac_py_core.cpp +++ b/python/codac_py_core.cpp @@ -32,6 +32,7 @@ void export_IntervalVar(py::module& m); void export_IntervalVectorVar(py::module& m); py::class_ export_Ctc(py::module& m); +void export_Ctc3BCid(py::module& m, py::class_& ctc); void export_CtcBox(py::module& m, py::class_& ctc); void export_CtcCN(py::module& m, py::class_& ctc); void export_CtcCartProd(py::module& m, py::class_& ctc); @@ -100,6 +101,7 @@ PYBIND11_MODULE(core, m) export_IntervalVectorVar(m); py::class_ ctc = export_Ctc(m); + export_Ctc3BCid(m, ctc); export_CtcBox(m, ctc); export_CtcCN(m, ctc); export_CtcCartProd(m, ctc); diff --git a/python/src/core/contractors/static/codac_py_Ctc3BCid.cpp b/python/src/core/contractors/static/codac_py_Ctc3BCid.cpp new file mode 100644 index 000000000..376125fcf --- /dev/null +++ b/python/src/core/contractors/static/codac_py_Ctc3BCid.cpp @@ -0,0 +1,45 @@ +/** + * \file + * Ctc3BCid Python binding + * ---------------------------------------------------------------------------- + * \date 2023 + * \author Simon Rohou, Benoît Desrochers + * \copyright Copyright 2023 Codac Team + * \license This program is distributed under the terms of + * the GNU Lesser General Public License (LGPL). + */ + +#include +#include +#include +#include +#include "codac_type_caster.h" + +#include "codac_py_Ctc.h" +#include "codac_Ctc.h" +#include "ibex_Ctc3BCid.h" +// Generated file from Doxygen XML (doxygen2docstring.py): +//#include "codac_py_Ctc3BCid_docs.h" + +using namespace std; +using namespace codac; +namespace py = pybind11; +using namespace pybind11::literals; + + +void export_Ctc3BCid(py::module& m, py::class_& ctc) +{ + py::class_ ctc_3bcid(m, "Ctc3BCid", ctc, "todo"); + ctc_3bcid + + .def(py::init(), + py::keep_alive<1,2>(), + "ctc"_a.noconvert(), + "s3b"_a.noconvert()=ibex::Ctc3BCid::default_s3b, + "scid"_a.noconvert()=ibex::Ctc3BCid::default_scid, + "vhandled"_a.noconvert()=-1, + "var_min_width"_a.noconvert()=ibex::Ctc3BCid::default_var_min_width) + + .def("contract", (void (Ctc::*) (IntervalVector&)) &ibex::Ctc3BCid::contract, "todo") + ; +} \ No newline at end of file From d5ef1fa55a446798b7c212b631c16f061020409b Mon Sep 17 00:00:00 2001 From: SimonRohou Date: Mon, 11 Dec 2023 14:27:16 +0100 Subject: [PATCH 161/256] [py] added predefined ColorMap constants --- python/src/core/graphics/codac_py_ColorMap.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/python/src/core/graphics/codac_py_ColorMap.cpp b/python/src/core/graphics/codac_py_ColorMap.cpp index 7de0db222..cb55a7ed0 100644 --- a/python/src/core/graphics/codac_py_ColorMap.cpp +++ b/python/src/core/graphics/codac_py_ColorMap.cpp @@ -32,5 +32,11 @@ void export_ColorMap(py::module& m) .def("is_opaque", &ColorMap::is_opaque, COLORMAP_BOOL_IS_OPAQUE) + + .def_property_readonly_static("HAXBY", [](py::object) { return ColorMap::HAXBY; }) + .def_property_readonly_static("DEFAULT", [](py::object) { return ColorMap::DEFAULT; }) + .def_property_readonly_static("BLUE_TUBE", [](py::object) { return ColorMap::BLUE_TUBE; }) + .def_property_readonly_static("RED_TUBE", [](py::object) { return ColorMap::RED_TUBE; }) + .def_property_readonly_static("RAINBOW", [](py::object) { return ColorMap::RAINBOW; }) ; } \ No newline at end of file From d7e18aa012db020b307b3001be797863e440361c Mon Sep 17 00:00:00 2001 From: SimonRohou Date: Mon, 11 Dec 2023 14:51:06 +0100 Subject: [PATCH 162/256] [doc] minor update in doc (IBEX for Python binding) --- doc/doc/dev/info_dev.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/doc/dev/info_dev.rst b/doc/doc/dev/info_dev.rst index e42f7ee13..0de057c8d 100644 --- a/doc/doc/dev/info_dev.rst +++ b/doc/doc/dev/info_dev.rst @@ -180,7 +180,7 @@ This configuration generates header files containing docstrings for Python, base the content of XML files made by Doxygen. The documentation of any C++/Python function is then located in the C++ header files of the :file:`/src` directory. -Note that you also have to configure IBEX with the ``-DCMAKE_CXX_FLAGS="-fPIC"`` flag. +Note that you also have to configure IBEX with the ``-DCMAKE_CXX_FLAGS="-fPIC" -DCMAKE_C_FLAGS="-fPIC"`` flag. Finally, after the compilation of Codac (and IBEX): From 876d7b5b61627d59f8a2551c8a5d9b1d534ad992 Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Sun, 21 Jan 2024 17:47:07 +0100 Subject: [PATCH 163/256] Windows 2022 GitHub hosted runner has changed its default gcc version --- .github/workflows/unixmatrix.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/unixmatrix.yml b/.github/workflows/unixmatrix.yml index 2d0dc1479..b3c4b5b89 100644 --- a/.github/workflows/unixmatrix.yml +++ b/.github/workflows/unixmatrix.yml @@ -77,7 +77,7 @@ jobs: - run: choco install -y -r --no-progress mingw --version=8.1.0 --force ${{ matrix.cfg.choco_flags }} if: (runner.os=='Windows')&&(matrix.cfg.runtime=='mingw8')&&(matrix.cfg.arch=='x86') - run: choco install -y -r --no-progress mingw --version=11.2.0.07112021 --force ${{ matrix.cfg.choco_flags }} - if: (runner.os=='Windows')&&(matrix.cfg.runtime=='mingw11')&&(matrix.cfg.arch=='x86') + if: (runner.os=='Windows')&&(matrix.cfg.runtime=='mingw11') - run: echo C:\ProgramData\chocolatey\lib\mingw\tools\install\mingw32\bin>>%GITHUB_PATH% if: startsWith(matrix.cfg.runtime, 'mingw')&&(matrix.cfg.arch=='x86') - run: echo C:\ProgramData\chocolatey\lib\mingw\tools\install\mingw64\bin>>%GITHUB_PATH% From 1b8489f3a36d6ffee62b7cf23347b251d3abb897 Mon Sep 17 00:00:00 2001 From: Teusner Date: Mon, 29 Jan 2024 10:44:54 +0100 Subject: [PATCH 164/256] Adding more parameters in Function definition --- python/src/core/functions/codac_py_Function.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/python/src/core/functions/codac_py_Function.cpp b/python/src/core/functions/codac_py_Function.cpp index a550bddf6..a6da93115 100644 --- a/python/src/core/functions/codac_py_Function.cpp +++ b/python/src/core/functions/codac_py_Function.cpp @@ -61,6 +61,18 @@ void export_Function(py::module& m) .def(py::init()) .def(py::init()) .def(py::init()) + .def(py::init()) + .def(py::init()) + .def(py::init()) + .def(py::init()) + .def(py::init()) + .def(py::init()) + .def(py::init()) + .def(py::init()) + .def(py::init()) + .def(py::init()) + .def(py::init()) + .def(py::init()) .def("nb_arg", &Function::nb_arg) .def("diff", &Function::diff) From 0c6ab997ae3333e7dd927444c347d95721131e75 Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Sat, 3 Feb 2024 19:57:14 +0100 Subject: [PATCH 165/256] Update workflows --- .github/workflows/dockercentos.yml | 2 +- .github/workflows/dockerpi.yml | 2 +- .github/workflows/macosmatrix.yml | 4 ++-- .github/workflows/tests.yml | 4 ++-- .github/workflows/unixmatrix.yml | 2 +- .github/workflows/vcmatrix.yml | 4 ++-- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/dockercentos.yml b/.github/workflows/dockercentos.yml index bd5933c43..ff756faad 100644 --- a/.github/workflows/dockercentos.yml +++ b/.github/workflows/dockercentos.yml @@ -10,7 +10,7 @@ jobs: name: CentOS Docker runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: submodules: true fetch-depth: 0 diff --git a/.github/workflows/dockerpi.yml b/.github/workflows/dockerpi.yml index a07c4e506..5f74ccacf 100644 --- a/.github/workflows/dockerpi.yml +++ b/.github/workflows/dockerpi.yml @@ -10,7 +10,7 @@ jobs: name: Raspbian Buster pi Docker runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: submodules: true fetch-depth: 0 diff --git a/.github/workflows/macosmatrix.yml b/.github/workflows/macosmatrix.yml index ee8a629a7..807256ec2 100644 --- a/.github/workflows/macosmatrix.yml +++ b/.github/workflows/macosmatrix.yml @@ -35,12 +35,12 @@ jobs: - { os: macos-11, shell: bash, arch: x86_64, runtime: bigsur, cmake_flags: '-fPIC', trgt: '10.14', cpcfg: '-macosx_10_14_x86_64', py_v_maj: 3, py_v_min: 6, desc: 'macOS Big Sur Python 3.6 x86_64' } # 10.14 because of error $MACOSX_DEPLOYMENT_TARGET mismatch: now "10.9" but "10.14" during configure. name: ${{ matrix.cfg.desc }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: submodules: true fetch-depth: 0 clean: false - - uses: actions/setup-python@v4 + - uses: actions/setup-python@v5 with: python-version: ${{ matrix.cfg.py_v_maj }}.${{ matrix.cfg.py_v_min }} architecture: x64 diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index b713797c7..a26b2b890 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -15,12 +15,12 @@ jobs: - { os: ubuntu-20.04, gcc_v: 8, py_v_maj: 3, py_v_min: 6, desc: 'Ubuntu 20.04 GCC 8 Python 3.6 tests' } name: ${{ matrix.cfg.desc }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: submodules: true fetch-depth: 0 clean: false - - uses: actions/setup-python@v4 + - uses: actions/setup-python@v5 with: python-version: ${{ matrix.cfg.py_v_maj }}.${{ matrix.cfg.py_v_min }} - run: | diff --git a/.github/workflows/unixmatrix.yml b/.github/workflows/unixmatrix.yml index b3c4b5b89..348fb9601 100644 --- a/.github/workflows/unixmatrix.yml +++ b/.github/workflows/unixmatrix.yml @@ -50,7 +50,7 @@ jobs: - { os: macos-11, shell: bash, arch: x86_64, runtime: bigsur, cmake_params: '-D CMAKE_SYSTEM_NAME=Darwin -D CMAKE_OSX_ARCHITECTURES=x86_64', cmake_flags: '-fPIC', desc: 'macOS Big Sur x86_64' } name: ${{ matrix.cfg.desc }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: submodules: true fetch-depth: 0 diff --git a/.github/workflows/vcmatrix.yml b/.github/workflows/vcmatrix.yml index 80a8eb5a6..4fbf9bcfc 100644 --- a/.github/workflows/vcmatrix.yml +++ b/.github/workflows/vcmatrix.yml @@ -33,12 +33,12 @@ jobs: - { os: windows-2022, shell: cmd, arch: x64, runtime: vc15, cmake_params: '-G "Visual Studio 17" -T v141 -A x64', cpcfg: 'm-win_amd64', py_v_maj: 3, py_v_min: 6, desc: 'Windows Visual Studio 2017 x64 Python 3.6' } name: ${{ matrix.cfg.desc }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: submodules: true fetch-depth: 0 clean: false - - uses: actions/setup-python@v4 + - uses: actions/setup-python@v5 with: python-version: ${{ matrix.cfg.py_v_maj }}.${{ matrix.cfg.py_v_min }} architecture: ${{ matrix.cfg.arch }} From 5db4c8aa58a0e7b946ffe5b8f52f45ca488e8045 Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Sun, 11 Feb 2024 00:30:26 +0100 Subject: [PATCH 166/256] Attempt to correct doxygen 1.9.6 installation problems on macOS --- .github/workflows/macosmatrix.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/macosmatrix.yml b/.github/workflows/macosmatrix.yml index 807256ec2..43a00a56a 100644 --- a/.github/workflows/macosmatrix.yml +++ b/.github/workflows/macosmatrix.yml @@ -54,7 +54,7 @@ jobs: - run: brew install eigen if: runner.os=='macOS' # doxygen v1.9.7 causes issues... - - run: wget https://github.com/Homebrew/homebrew-core/raw/d2267b9f2ad247bc9c8273eb755b39566a474a70/Formula/doxygen.rb ; brew reinstall ./doxygen.rb ; brew pin doxygen ; brew install graphviz ; python -m pip install --upgrade pip ; pip install --upgrade wheel setuptools sphinx breathe sphinx_rtd_theme sphinx-tabs sphinx-issues sphinx-reredirects + - run: brew install graphviz ; wget https://github.com/Homebrew/homebrew-core/raw/d2267b9f2ad247bc9c8273eb755b39566a474a70/Formula/doxygen.rb ; brew reinstall ./doxygen.rb ; brew pin doxygen ; python -m pip install --upgrade pip ; pip install --upgrade wheel setuptools sphinx breathe sphinx_rtd_theme sphinx-tabs sphinx-issues sphinx-reredirects if: runner.os=='macOS' - run: | wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20231007/ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip --no-check-certificate -nv From 4169f2e61728d83ec82da7685f82c88cc97f1809 Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Sat, 3 Feb 2024 23:09:08 +0100 Subject: [PATCH 167/256] Change registry key name --- .github/workflows/unixmatrix.yml | 2 ++ packages/choco/codac/tools/chocolateybeforemodify.ps1 | 10 ++++++---- packages/choco/codac/tools/chocolateyinstall.ps1 | 4 ++-- packages/choco/codac/tools/chocolateyuninstall.ps1 | 10 ++++++---- packages/choco/codac/tools/sharedVars.ps1 | 1 + 5 files changed, 17 insertions(+), 10 deletions(-) diff --git a/.github/workflows/unixmatrix.yml b/.github/workflows/unixmatrix.yml index 56c8dd34b..212d89277 100644 --- a/.github/workflows/unixmatrix.yml +++ b/.github/workflows/unixmatrix.yml @@ -140,6 +140,8 @@ jobs: sed_param=s/download\\/v1\\/codac/download\\/v${PACKAGE_VERSION}\\/codac/ sed -i "$sed_param" codac/codac.nuspec sed -i "$sed_param" codac/tools/chocolateyinstall.ps1 + sed_param=s/\\$CMakePackageVer\ =\ \"1\"/\\$CMakePackageVer\ =\ \"${SOFTWARE_VERSION}\"/ + sed -i "$sed_param" codac/tools/sharedVars.ps1 mv -f codac codac.$PACKAGE_VERSION cd codac.$PACKAGE_VERSION choco pack diff --git a/packages/choco/codac/tools/chocolateybeforemodify.ps1 b/packages/choco/codac/tools/chocolateybeforemodify.ps1 index 2ed694ebb..1a4397bef 100644 --- a/packages/choco/codac/tools/chocolateybeforemodify.ps1 +++ b/packages/choco/codac/tools/chocolateybeforemodify.ps1 @@ -15,10 +15,12 @@ Write-Host "Codac is going to be uninstalled from '$installDir'" $root = Join-Path $installDir "codac" -if (Test-Path $CMakeRegistryPath) { - if (Test-Path $CMakeSystemRepositoryPath\$CMakePackageName) { - Remove-Item "$CMakeSystemRepositoryPath\$CMakePackageName" - } +try { + Get-ItemProperty -Path $CMakeSystemRepositoryPath\$CMakePackageName | Select-Object -ExpandProperty $CMakePackageName$CMakePackageVer -ErrorAction Stop | Out-Null + Remove-ItemProperty -Path $CMakeSystemRepositoryPath\$CMakePackageName -Name $CMakePackageName$CMakePackageVer +} +catch { + } if (Test-Path $root) { diff --git a/packages/choco/codac/tools/chocolateyinstall.ps1 b/packages/choco/codac/tools/chocolateyinstall.ps1 index bf3284425..b84d21b64 100644 --- a/packages/choco/codac/tools/chocolateyinstall.ps1 +++ b/packages/choco/codac/tools/chocolateyinstall.ps1 @@ -91,7 +91,7 @@ else { if (!$pp['NoRegistry']) { New-Item "$CMakeSystemRepositoryPath\$CMakePackageName" -ItemType directory -Force - New-ItemProperty -Name "CMakePackageDir" -PropertyType String -Value "$root\share\$CMakePackageName\cmake" -Path "$CMakeSystemRepositoryPath\$CMakePackageName" -Force + New-ItemProperty -Name "$CMakePackageName$CMakePackageVer" -PropertyType String -Value "$root\share\$CMakePackageName\cmake" -Path "$CMakeSystemRepositoryPath\$CMakePackageName" -Force } #$pathtoadd = "$root\bin" #if (!($pp['NoPath']) -and !([environment]::GetEnvironmentVariable("Path","Machine") -match [regex]::escape($pathtoadd))) { @@ -155,7 +155,7 @@ for ($i = 1; $i -le 99; $i++) { if (!$pp['NoRegistry']) { New-Item "$CMakeSystemRepositoryPath\$CMakePackageName" -ItemType directory -Force - New-ItemProperty -Name "CMakePackageDir" -PropertyType String -Value "$root\share\$CMakePackageName\cmake" -Path "$CMakeSystemRepositoryPath\$CMakePackageName" -Force + New-ItemProperty -Name "$CMakePackageName$CMakePackageVer" -PropertyType String -Value "$root\share\$CMakePackageName\cmake" -Path "$CMakeSystemRepositoryPath\$CMakePackageName" -Force } #$pathtoadd = "$root\bin" #if (!($pp['NoPath']) -and !([environment]::GetEnvironmentVariable("Path","Machine") -match [regex]::escape($pathtoadd))) { diff --git a/packages/choco/codac/tools/chocolateyuninstall.ps1 b/packages/choco/codac/tools/chocolateyuninstall.ps1 index 2ed694ebb..1a4397bef 100644 --- a/packages/choco/codac/tools/chocolateyuninstall.ps1 +++ b/packages/choco/codac/tools/chocolateyuninstall.ps1 @@ -15,10 +15,12 @@ Write-Host "Codac is going to be uninstalled from '$installDir'" $root = Join-Path $installDir "codac" -if (Test-Path $CMakeRegistryPath) { - if (Test-Path $CMakeSystemRepositoryPath\$CMakePackageName) { - Remove-Item "$CMakeSystemRepositoryPath\$CMakePackageName" - } +try { + Get-ItemProperty -Path $CMakeSystemRepositoryPath\$CMakePackageName | Select-Object -ExpandProperty $CMakePackageName$CMakePackageVer -ErrorAction Stop | Out-Null + Remove-ItemProperty -Path $CMakeSystemRepositoryPath\$CMakePackageName -Name $CMakePackageName$CMakePackageVer +} +catch { + } if (Test-Path $root) { diff --git a/packages/choco/codac/tools/sharedVars.ps1 b/packages/choco/codac/tools/sharedVars.ps1 index 98e579210..c401315a9 100644 --- a/packages/choco/codac/tools/sharedVars.ps1 +++ b/packages/choco/codac/tools/sharedVars.ps1 @@ -4,3 +4,4 @@ $MinGWMVer = "11" $CMakeRegistryPath = "HKCU:\SOFTWARE\Kitware\CMake" $CMakeSystemRepositoryPath = "HKLM:\SOFTWARE\Kitware\CMake\Packages" $CMakePackageName = "Codac" +$CMakePackageVer = "1" From a9a15b76ae5a9a2e6854ff9f214db346d512a2b4 Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Sat, 3 Feb 2024 23:22:49 +0100 Subject: [PATCH 168/256] Attempt to correct mandatory checksums in Chocolatey --- packages/choco/codac/codac.nuspec | 2 ++ .../choco/codac/tools/chocolateyinstall.ps1 | 18 ++++++++++++------ 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/packages/choco/codac/codac.nuspec b/packages/choco/codac/codac.nuspec index b1f861fee..2f7ed607e 100644 --- a/packages/choco/codac/codac.nuspec +++ b/packages/choco/codac/codac.nuspec @@ -28,7 +28,9 @@ Codac is a library providing tools for constraint programming over reals, trajec ## Package parameters The following package parameters can be set: - `/url:URL` - Will install the specified binary package (e.g. built for Visual Studio), see versions from https://github.com/codac-team/codac/releases (the Windows `PATH` might need to be updated manually with e.g. `C:\ProgramData\chocolatey\lib\ibex\bin`, etc.). By default, only the MinGW libraries compatible with the corresponding MinGW Chocolatey package dependency are installed. Use the standard parameter `choco install --ignore-dependencies ...` to avoid installing the default MinGW and IBEX Chocolatey package dependencies if needed (you might want to install manually [IBEX](https://community.chocolatey.org/packages/ibex) package with the corresponding parameters, as well as the corresponding compiler and the Eigen package). +- `/checksum:SHA256` - SHA256 checksum of the binary package specified by the `/url` parameter. If needed, use the standard parameter `choco install --ignore-checksums ...` for trusted sources. - `/urlX:URL` - Same as above, with X in [1,99], except this will not disable the installation of the MinGW libraries compatible with the corresponding MinGW Chocolatey package dependency. +- `/checksumX:SHA256` - SHA256 checksum of the binary package specified by the `/urlX` parameter. If needed, use the standard parameter `choco install --ignore-checksums ...` for trusted sources. - `/InstallDir:INSTALLDIR` - Installation directory. - `/NoPath` - Will not try to update Windows `PATH`. - `/NoRegistry` - Will not try to update Windows registry. diff --git a/packages/choco/codac/tools/chocolateyinstall.ps1 b/packages/choco/codac/tools/chocolateyinstall.ps1 index b84d21b64..6f08af63e 100644 --- a/packages/choco/codac/tools/chocolateyinstall.ps1 +++ b/packages/choco/codac/tools/chocolateyinstall.ps1 @@ -37,13 +37,16 @@ if (!$pp['url']) { } else { $url = $pp['url'] - #$checksum = $pp['sha256'] + $checksum = $pp['checksum'] $packageArgs = @{ packageName = $env:ChocolateyPackageName unzipLocation = Join-Path "$root" ".." url = $url - #checksum = $checksum - #checksumType = 'sha256' + url64bit = $url + checksum = $checksum + checksumType = 'sha256' + checksum64 = $checksum + checksumType64= 'sha256' } Install-ChocolateyZipPackage @packageArgs @@ -102,13 +105,16 @@ if (!$pp['NoRegistry']) { for ($i = 1; $i -le 99; $i++) { if ($pp['url'+$i]) { $url = $pp['url'+$i] - #$checksum = $pp['sha256'] + $checksum = $pp['checksum'+$i] $packageArgs = @{ packageName = $env:ChocolateyPackageName unzipLocation = Join-Path "$root" ".." url = $url - #checksum = $checksum - #checksumType = 'sha256' + url64bit = $url + checksum = $checksum + checksumType = 'sha256' + checksum64 = $checksum + checksumType64= 'sha256' } Install-ChocolateyZipPackage @packageArgs From de13dcb8885fe32cef3dfeba9731a91c5d58a4ac Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Sun, 11 Feb 2024 21:10:28 +0100 Subject: [PATCH 169/256] macOS Big Sur EOL --- .github/workflows/macosmatrix.yml | 28 ++++++++++++++-------------- .github/workflows/unixmatrix.yml | 2 -- 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/.github/workflows/macosmatrix.yml b/.github/workflows/macosmatrix.yml index 43a00a56a..f97273c39 100644 --- a/.github/workflows/macosmatrix.yml +++ b/.github/workflows/macosmatrix.yml @@ -19,20 +19,20 @@ jobs: fail-fast: false matrix: cfg: - - { os: macos-11, shell: bash, arch: arm64, runtime: bigsur, cmake_flags: '-fPIC', trgt: '11.0', cpcfg: '-macosx_11_0_arm64', py_v_maj: 3, py_v_min: 12, desc: 'macOS Big Sur Python 3.12 arm64' } - - { os: macos-11, shell: bash, arch: arm64, runtime: bigsur, cmake_flags: '-fPIC', trgt: '11.0', cpcfg: '-macosx_11_0_arm64', py_v_maj: 3, py_v_min: 11, desc: 'macOS Big Sur Python 3.11 arm64' } - - { os: macos-11, shell: bash, arch: arm64, runtime: bigsur, cmake_flags: '-fPIC', trgt: '11.0', cpcfg: '-macosx_11_0_arm64', py_v_maj: 3, py_v_min: 10, desc: 'macOS Big Sur Python 3.10 arm64' } - - { os: macos-11, shell: bash, arch: arm64, runtime: bigsur, cmake_flags: '-fPIC', trgt: '11.0', cpcfg: '-macosx_11_0_arm64', py_v_maj: 3, py_v_min: 9, desc: 'macOS Big Sur Python 3.9 arm64' } - - { os: macos-11, shell: bash, arch: arm64, runtime: bigsur, cmake_flags: '-fPIC', trgt: '11.0', cpcfg: '-macosx_11_0_arm64', py_v_maj: 3, py_v_min: 8, desc: 'macOS Big Sur Python 3.8 arm64' } - - { os: macos-11, shell: bash, arch: arm64, runtime: bigsur, cmake_flags: '-fPIC', trgt: '11.0', cpcfg: '-macosx_11_0_arm64', py_v_maj: 3, py_v_min: 7, desc: 'macOS Big Sur Python 3.7 arm64' } - - { os: macos-11, shell: bash, arch: arm64, runtime: bigsur, cmake_flags: '-fPIC', trgt: '11.0', cpcfg: '-macosx_11_0_arm64', py_v_maj: 3, py_v_min: 6, desc: 'macOS Big Sur Python 3.6 arm64' } - - { os: macos-11, shell: bash, arch: x86_64, runtime: bigsur, cmake_flags: '-fPIC', trgt: '10.9', cpcfg: '-macosx_10_9_x86_64', py_v_maj: 3, py_v_min: 12, desc: 'macOS Big Sur Python 3.12 x86_64' } - - { os: macos-11, shell: bash, arch: x86_64, runtime: bigsur, cmake_flags: '-fPIC', trgt: '10.9', cpcfg: '-macosx_10_9_x86_64', py_v_maj: 3, py_v_min: 11, desc: 'macOS Big Sur Python 3.11 x86_64' } - - { os: macos-11, shell: bash, arch: x86_64, runtime: bigsur, cmake_flags: '-fPIC', trgt: '10.9', cpcfg: '-macosx_10_9_x86_64', py_v_maj: 3, py_v_min: 10, desc: 'macOS Big Sur Python 3.10 x86_64' } - - { os: macos-11, shell: bash, arch: x86_64, runtime: bigsur, cmake_flags: '-fPIC', trgt: '10.9', cpcfg: '-macosx_10_9_x86_64', py_v_maj: 3, py_v_min: 9, desc: 'macOS Big Sur Python 3.9 x86_64' } - - { os: macos-11, shell: bash, arch: x86_64, runtime: bigsur, cmake_flags: '-fPIC', trgt: '10.9', cpcfg: '-macosx_10_9_x86_64', py_v_maj: 3, py_v_min: 8, desc: 'macOS Big Sur Python 3.8 x86_64' } - - { os: macos-11, shell: bash, arch: x86_64, runtime: bigsur, cmake_flags: '-fPIC', trgt: '10.9', cpcfg: '-macosx_10_9_x86_64', py_v_maj: 3, py_v_min: 7, desc: 'macOS Big Sur Python 3.7 x86_64' } - - { os: macos-11, shell: bash, arch: x86_64, runtime: bigsur, cmake_flags: '-fPIC', trgt: '10.14', cpcfg: '-macosx_10_14_x86_64', py_v_maj: 3, py_v_min: 6, desc: 'macOS Big Sur Python 3.6 x86_64' } # 10.14 because of error $MACOSX_DEPLOYMENT_TARGET mismatch: now "10.9" but "10.14" during configure. + - { os: macos-12, shell: bash, arch: arm64, runtime: monterey, cmake_flags: '-fPIC', trgt: '11.0', cpcfg: '-macosx_11_0_arm64', py_v_maj: 3, py_v_min: 12, desc: 'macOS Monterey Python 3.12 arm64' } + - { os: macos-12, shell: bash, arch: arm64, runtime: monterey, cmake_flags: '-fPIC', trgt: '11.0', cpcfg: '-macosx_11_0_arm64', py_v_maj: 3, py_v_min: 11, desc: 'macOS Monterey Python 3.11 arm64' } + - { os: macos-12, shell: bash, arch: arm64, runtime: monterey, cmake_flags: '-fPIC', trgt: '11.0', cpcfg: '-macosx_11_0_arm64', py_v_maj: 3, py_v_min: 10, desc: 'macOS Monterey Python 3.10 arm64' } + - { os: macos-12, shell: bash, arch: arm64, runtime: monterey, cmake_flags: '-fPIC', trgt: '11.0', cpcfg: '-macosx_11_0_arm64', py_v_maj: 3, py_v_min: 9, desc: 'macOS Monterey Python 3.9 arm64' } + - { os: macos-12, shell: bash, arch: arm64, runtime: monterey, cmake_flags: '-fPIC', trgt: '11.0', cpcfg: '-macosx_11_0_arm64', py_v_maj: 3, py_v_min: 8, desc: 'macOS Monterey Python 3.8 arm64' } + - { os: macos-12, shell: bash, arch: arm64, runtime: monterey, cmake_flags: '-fPIC', trgt: '11.0', cpcfg: '-macosx_11_0_arm64', py_v_maj: 3, py_v_min: 7, desc: 'macOS Monterey Python 3.7 arm64' } + - { os: macos-12, shell: bash, arch: arm64, runtime: monterey, cmake_flags: '-fPIC', trgt: '11.0', cpcfg: '-macosx_11_0_arm64', py_v_maj: 3, py_v_min: 6, desc: 'macOS Monterey Python 3.6 arm64' } + - { os: macos-12, shell: bash, arch: x86_64, runtime: monterey, cmake_flags: '-fPIC', trgt: '10.9', cpcfg: '-macosx_10_9_x86_64', py_v_maj: 3, py_v_min: 12, desc: 'macOS Monterey Python 3.12 x86_64' } + - { os: macos-12, shell: bash, arch: x86_64, runtime: monterey, cmake_flags: '-fPIC', trgt: '10.9', cpcfg: '-macosx_10_9_x86_64', py_v_maj: 3, py_v_min: 11, desc: 'macOS Monterey Python 3.11 x86_64' } + - { os: macos-12, shell: bash, arch: x86_64, runtime: monterey, cmake_flags: '-fPIC', trgt: '10.9', cpcfg: '-macosx_10_9_x86_64', py_v_maj: 3, py_v_min: 10, desc: 'macOS Monterey Python 3.10 x86_64' } + - { os: macos-12, shell: bash, arch: x86_64, runtime: monterey, cmake_flags: '-fPIC', trgt: '10.9', cpcfg: '-macosx_10_9_x86_64', py_v_maj: 3, py_v_min: 9, desc: 'macOS Monterey Python 3.9 x86_64' } + - { os: macos-12, shell: bash, arch: x86_64, runtime: monterey, cmake_flags: '-fPIC', trgt: '10.9', cpcfg: '-macosx_10_9_x86_64', py_v_maj: 3, py_v_min: 8, desc: 'macOS Monterey Python 3.8 x86_64' } + - { os: macos-12, shell: bash, arch: x86_64, runtime: monterey, cmake_flags: '-fPIC', trgt: '10.9', cpcfg: '-macosx_10_9_x86_64', py_v_maj: 3, py_v_min: 7, desc: 'macOS Monterey Python 3.7 x86_64' } + - { os: macos-12, shell: bash, arch: x86_64, runtime: monterey, cmake_flags: '-fPIC', trgt: '10.14', cpcfg: '-macosx_10_14_x86_64', py_v_maj: 3, py_v_min: 6, desc: 'macOS Monterey Python 3.6 x86_64' } # 10.14 because of error $MACOSX_DEPLOYMENT_TARGET mismatch: now "10.9" but "10.14" during configure. name: ${{ matrix.cfg.desc }} steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/unixmatrix.yml b/.github/workflows/unixmatrix.yml index 348fb9601..56c8dd34b 100644 --- a/.github/workflows/unixmatrix.yml +++ b/.github/workflows/unixmatrix.yml @@ -46,8 +46,6 @@ jobs: - { os: macos-13, shell: bash, arch: x86_64, runtime: ventura, cmake_params: '-D CMAKE_SYSTEM_NAME=Darwin -D CMAKE_OSX_ARCHITECTURES=x86_64', cmake_flags: '-fPIC', desc: 'macOS Ventura x86_64' } - { os: macos-12, shell: bash, arch: arm64, runtime: monterey, cmake_params: '-D CMAKE_SYSTEM_NAME=Darwin -D CMAKE_OSX_ARCHITECTURES=arm64', cmake_flags: '-fPIC', desc: 'macOS Monterey arm64' } - { os: macos-12, shell: bash, arch: x86_64, runtime: monterey, cmake_params: '-D CMAKE_SYSTEM_NAME=Darwin -D CMAKE_OSX_ARCHITECTURES=x86_64', cmake_flags: '-fPIC', desc: 'macOS Monterey x86_64' } - - { os: macos-11, shell: bash, arch: arm64, runtime: bigsur, cmake_params: '-D CMAKE_SYSTEM_NAME=Darwin -D CMAKE_OSX_ARCHITECTURES=arm64', cmake_flags: '-fPIC', desc: 'macOS Big Sur arm64' } - - { os: macos-11, shell: bash, arch: x86_64, runtime: bigsur, cmake_params: '-D CMAKE_SYSTEM_NAME=Darwin -D CMAKE_OSX_ARCHITECTURES=x86_64', cmake_flags: '-fPIC', desc: 'macOS Big Sur x86_64' } name: ${{ matrix.cfg.desc }} steps: - uses: actions/checkout@v4 From 7652195bac4f0dd3bb002bb3f3edd7bc0c84dfb6 Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Thu, 8 Feb 2024 22:31:36 +0100 Subject: [PATCH 170/256] Attempt to correct uninstallation of Chocolatey package --- packages/choco/codac/tools/chocolateybeforemodify.ps1 | 4 +++- packages/choco/codac/tools/chocolateyuninstall.ps1 | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/choco/codac/tools/chocolateybeforemodify.ps1 b/packages/choco/codac/tools/chocolateybeforemodify.ps1 index 1a4397bef..c3cb63574 100644 --- a/packages/choco/codac/tools/chocolateybeforemodify.ps1 +++ b/packages/choco/codac/tools/chocolateybeforemodify.ps1 @@ -24,5 +24,7 @@ catch { } if (Test-Path $root) { - Remove-Item -Recurse -Force $root + if ((Resolve-Path $root).Path -notcontains (Resolve-Path $packageDir).Path) { + Remove-Item -Recurse -Force $root + } } diff --git a/packages/choco/codac/tools/chocolateyuninstall.ps1 b/packages/choco/codac/tools/chocolateyuninstall.ps1 index 1a4397bef..c3cb63574 100644 --- a/packages/choco/codac/tools/chocolateyuninstall.ps1 +++ b/packages/choco/codac/tools/chocolateyuninstall.ps1 @@ -24,5 +24,7 @@ catch { } if (Test-Path $root) { - Remove-Item -Recurse -Force $root + if ((Resolve-Path $root).Path -notcontains (Resolve-Path $packageDir).Path) { + Remove-Item -Recurse -Force $root + } } From 21a277c9410ee3a12be65fb6da30335987f428eb Mon Sep 17 00:00:00 2001 From: SimonRohou Date: Mon, 12 Feb 2024 12:53:21 +0100 Subject: [PATCH 171/256] [tuto] added Wall class + CtcUnion binding --- .../core/contractors/static/codac_py_Ctc.cpp | 34 +++++++- .../src/core/graphics/codac_py_VIBesFig.cpp | 4 + python/src/robotics/codac_py_Wall.cpp | 42 ++++++++++ .../contractors/static/codac_CtcSegment.cpp | 8 +- .../contractors/static/codac_CtcSegment.h | 8 +- src/core/contractors/static/codac_CtcUnion.h | 69 +++++++++++++++- src/robotics/CMakeLists.txt | 2 + src/robotics/objects/codac_Wall.cpp | 82 +++++++++++++++++++ src/robotics/objects/codac_Wall.h | 37 +++++++++ 9 files changed, 275 insertions(+), 11 deletions(-) create mode 100644 python/src/robotics/codac_py_Wall.cpp create mode 100644 src/robotics/objects/codac_Wall.cpp create mode 100644 src/robotics/objects/codac_Wall.h diff --git a/python/src/core/contractors/static/codac_py_Ctc.cpp b/python/src/core/contractors/static/codac_py_Ctc.cpp index d798bf5ee..8d2c1ec39 100644 --- a/python/src/core/contractors/static/codac_py_Ctc.cpp +++ b/python/src/core/contractors/static/codac_py_Ctc.cpp @@ -49,8 +49,11 @@ py::class_ export_Ctc(py::module& m) .def("__or__", [](Ctc& c1, Ctc& c2) { - return new CtcUnion(c1, c2); + CtcUnion *cu = new CtcUnion(2); + cu->add_raw_ptr(&c1); + cu->add_raw_ptr(&c2); // todo: manage delete + return cu; }, DOC_CTC_OR, py::return_value_policy::take_ownership, @@ -77,9 +80,34 @@ py::class_ export_Ctc(py::module& m) ; // Export CtcUnion - py::class_(m, "CtcUnion", ctc, DOC_CTCUNION_TYPE) - .def(py::init>(), py::keep_alive<1,2>(), "list"_a) + py::class_(m, "CtcUnion", ctc, "todo") + .def(py::init(), "nb_var"_a) + .def(py::init([](Ctc& c1) + { + CtcUnion *cu = new CtcUnion(c1.nb_var); + cu->add_raw_ptr(&c1); + return cu; + }), + py::keep_alive<0,1>(), "c1"_a) + .def(py::init([](Ctc& c1, Ctc& c2) + { + CtcUnion *cu = new CtcUnion(c1.nb_var); + cu->add_raw_ptr(&c1); + cu->add_raw_ptr(&c2); + return cu; + }), + py::keep_alive<0,1>(), py::keep_alive<0,2>(), "c1"_a, "c2"_a) + .def(py::init([](Ctc& c1, Ctc& c2, Ctc& c3) + { + CtcUnion *cu = new CtcUnion(c1.nb_var); + cu->add_raw_ptr(&c1); + cu->add_raw_ptr(&c2); + cu->add_raw_ptr(&c3); + return cu; + }), + py::keep_alive<0,1>(), py::keep_alive<0,2>(), py::keep_alive<0,3>(), "c1"_a, "c2"_a, "c3"_a) .def("contract", (void (Ctc::*)(IntervalVector&)) &CtcUnion::contract) + .def("__ior__", [](CtcUnion& cu, Ctc& c) { return cu.add_raw_ptr(&c); }, py::keep_alive<1,2>(), py::return_value_policy::take_ownership) ; // Export CtcCompo diff --git a/python/src/core/graphics/codac_py_VIBesFig.cpp b/python/src/core/graphics/codac_py_VIBesFig.cpp index 61a69a21c..eb895a904 100644 --- a/python/src/core/graphics/codac_py_VIBesFig.cpp +++ b/python/src/core/graphics/codac_py_VIBesFig.cpp @@ -92,5 +92,9 @@ void export_VIBesFig(py::module& m) .def("draw_vehicle", (void (VIBesFig::*)(double,double,double,double,const string&,const vibes::Params &))&VIBesFig::draw_vehicle, VIBESFIG_VOID_DRAW_VEHICLE_DOUBLE_DOUBLE_DOUBLE_DOUBLE_STRING_VIBESPARAMS, "x"_a, "y"_a, "heading"_a, "size"_a, "color"_a="", "params"_a=vibes::Params()) + + .def("draw_line", (void (VIBesFig::*)(const std::vector&,const std::vector&,const string&,const vibes::Params &))&VIBesFig::draw_line, + "todo", + "v_x"_a, "v_y"_a, "color"_a="", "params"_a=vibes::Params()) ; } \ No newline at end of file diff --git a/python/src/robotics/codac_py_Wall.cpp b/python/src/robotics/codac_py_Wall.cpp new file mode 100644 index 000000000..56b304b86 --- /dev/null +++ b/python/src/robotics/codac_py_Wall.cpp @@ -0,0 +1,42 @@ +/** + * \file + * Wall Python binding + * ---------------------------------------------------------------------------- + * \date 2024 + * \author Simon Rohou, Benoît Desrochers + * \copyright Copyright 2024 Codac Team + * \license This program is distributed under the terms of + * the GNU Lesser General Public License (LGPL). + */ + +#include +#include +#include +#include +#include "codac_type_caster.h" + +#include "codac_Wall.h" +// Generated file from Doxygen XML (doxygen2docstring.py): +#include "codac_py_Wall_docs.h" + +using namespace std; +using namespace codac; +namespace py = pybind11; +using namespace pybind11::literals; + + +void export_Wall(py::module& m) +{ + py::class_ wall(m, "Wall", WALL_MAIN); + wall + + .def(py::init(), "todo") + .def("contains", &Wall::contains, "todo", "p"_a) + .def(py::self & py::self) + .def_readwrite("c1", &Wall::c1) + .def_readwrite("c2", &Wall::c2) + ; + + m.def("shorter_dist_to_walls", &shorter_dist_to_walls, "todo", "v_walls"_a, "p"_a, "bearing"_a); + m.def("shorter_contact_to_walls", &shorter_contact_to_walls, "todo", "v_walls"_a, "p"_a); +} \ No newline at end of file diff --git a/src/core/contractors/static/codac_CtcSegment.cpp b/src/core/contractors/static/codac_CtcSegment.cpp index 2a2000a68..2721281ae 100644 --- a/src/core/contractors/static/codac_CtcSegment.cpp +++ b/src/core/contractors/static/codac_CtcSegment.cpp @@ -14,7 +14,8 @@ using namespace std; namespace codac { CtcSegment::CtcSegment(double ax, double ay, double bx, double by) : Ctc(2), - X_with_params(2+4) { + X_with_params(2+4) +{ init(); @@ -24,6 +25,11 @@ CtcSegment::CtcSegment(double ax, double ay, double bx, double by) : Ctc(2), X_with_params[5] = Interval(by); } +CtcSegment::CtcSegment(const CtcSegment& ctc) : CtcSegment(ctc.X_with_params[2].lb(),ctc.X_with_params[3].lb(),ctc.X_with_params[4].lb(),ctc.X_with_params[5].lb()) +{ + +} + CtcSegment::CtcSegment() : Ctc(6), X_with_params(0 /* unused */) { init(); } diff --git a/src/core/contractors/static/codac_CtcSegment.h b/src/core/contractors/static/codac_CtcSegment.h index 45659e471..5d6ea535a 100644 --- a/src/core/contractors/static/codac_CtcSegment.h +++ b/src/core/contractors/static/codac_CtcSegment.h @@ -15,15 +15,15 @@ #include "ibex_CtcFwdBwd.h" +namespace codac { + + using ibex::Interval; using ibex::IntervalVector; using ibex::Ctc; using ibex::NumConstraint; using ibex::CtcFwdBwd; - -namespace codac { - /** * \ingroup geometry * @@ -58,6 +58,8 @@ class CtcSegment : public Ctc { */ CtcSegment(); + CtcSegment(const CtcSegment& ctc); + /** * \brief Contract a box. * \param box to be contracted diff --git a/src/core/contractors/static/codac_CtcUnion.h b/src/core/contractors/static/codac_CtcUnion.h index b9c73b414..336162dbd 100644 --- a/src/core/contractors/static/codac_CtcUnion.h +++ b/src/core/contractors/static/codac_CtcUnion.h @@ -2,9 +2,9 @@ * \file * CtcUnion class * ---------------------------------------------------------------------------- - * \date 2022 + * \date 2024 * \author Simon Rohou - * \copyright Copyright 2022 Codac Team + * \copyright Copyright 2024 Codac Team * \license This program is distributed under the terms of * the GNU Lesser General Public License (LGPL). */ @@ -12,11 +12,72 @@ #ifndef __CODAC_CTCUNION_H__ #define __CODAC_CTCUNION_H__ -#include "ibex_CtcUnion.h" +#include +//#include "ibex_CtcUnion.h" +#include "codac_Ctc.h" namespace codac { - using ibex::CtcUnion; + class CtcUnion : public Ctc + { + public: + + CtcUnion(int nb_var) : Ctc(nb_var) + { } + + template + CtcUnion(const C1& c1) : Ctc(c1.nb_var) + { + _v_ctc.push_back(std::make_shared(c1)); + } + + template + CtcUnion(const C1& c1, const C&... c) : CtcUnion(c1) + { + (_v_ctc.push_back(std::make_shared(c)), ...); + for(const auto& ci : _v_ctc) { assert(ci->nb_var == nb_var); } + } + + void contract(IntervalVector& x) + { + IntervalVector result(nb_var, Interval::empty_set()); + + for(auto& ci : _v_ctc) + { + IntervalVector saved_x = x; + ci->contract(saved_x); + result |= saved_x; + } + + for(auto& ci : _v_ctc_ptrs) + { + IntervalVector saved_x = x; + ci->contract(saved_x); + result |= saved_x; + } + + x = result; + } + + template + CtcUnion& operator|=(const C& c) + { + assert(c.nb_var == nb_var); + _v_ctc.push_back(std::make_shared(c)); + return *this; + } + + CtcUnion& add_raw_ptr(Ctc *c) + { + _v_ctc_ptrs.push_back(c); + return *this; + } + + protected: + + std::vector> _v_ctc; + std::vector _v_ctc_ptrs; + }; } #endif \ No newline at end of file diff --git a/src/robotics/CMakeLists.txt b/src/robotics/CMakeLists.txt index c75f3049f..6d551d21e 100644 --- a/src/robotics/CMakeLists.txt +++ b/src/robotics/CMakeLists.txt @@ -6,6 +6,8 @@ ${CMAKE_CURRENT_SOURCE_DIR}/graphics/codac_VIBesFigMap.h ${CMAKE_CURRENT_SOURCE_DIR}/objects/codac_Beacon.cpp ${CMAKE_CURRENT_SOURCE_DIR}/objects/codac_Beacon.h + ${CMAKE_CURRENT_SOURCE_DIR}/objects/codac_Wall.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/objects/codac_Wall.h ${CMAKE_CURRENT_SOURCE_DIR}/loops/codac_TPlane.h ${CMAKE_CURRENT_SOURCE_DIR}/loops/codac_TPlane.cpp ${CMAKE_CURRENT_SOURCE_DIR}/data/codac_DataLoaderLissajous.cpp diff --git a/src/robotics/objects/codac_Wall.cpp b/src/robotics/objects/codac_Wall.cpp new file mode 100644 index 000000000..4842c4e6d --- /dev/null +++ b/src/robotics/objects/codac_Wall.cpp @@ -0,0 +1,82 @@ +/** + * Wall class + * ---------------------------------------------------------------------------- + * \date 2024 + * \author Simon Rohou + * \copyright Copyright 2024 Codac Team + * \license This program is distributed under the terms of + * the GNU Lesser General Public License (LGPL). + */ + +#include +#include "codac_Wall.h" +#include "codac_predef_values.h" + +using namespace std; +using namespace ibex; + +namespace codac +{ + Wall::Wall(const Vector& c1_, const Vector& c2_) : c1(c1_), c2(c2_) + { + } + + bool Wall::contains(const Vector& p) const + { + Vector ab { c2[0]-c1[0], c2[1]-c1[1] }; + Vector ac { p[0]-c1[0], p[1]-c1[1] }; + + double dp_AB = ab[0]*ab[0] + ab[1]*ab[1]; + double dp_AC = ab[0]*ac[0] + ab[1]*ac[1]; + return Interval(0.,dp_AB).contains(dp_AC); + } + + Vector operator&(const Wall& w1, const Wall& w2) + { + const double& x1 = w1.c1[0]; const double& x2 = w1.c2[0]; + const double& x3 = w2.c1[0]; const double& x4 = w2.c2[0]; + + const double& y1 = w1.c1[1]; const double& y2 = w1.c2[1]; + const double& y3 = w2.c1[1]; const double& y4 = w2.c2[1]; + + return { + ((x1*y2-y1*x2)*(x3-x4)-(x1-x2)*(x3*y4-y3*x4))/((x1-x2)*(y3-y4)-(y1-y2)*(x3-x4)), + ((x1*y2-y1*x2)*(y3-y4)-(y1-y2)*(x3*y4-y3*x4))/((x1-x2)*(y3-y4)-(y1-y2)*(x3-x4)) + }; + } + + double shorter_dist_to_walls(const std::vector& v_walls, const Vector& p, double bearing) + { + Wall w0 { p, { p[0]+999999.*cos(bearing), p[1]+999999.*sin(bearing) }}; + double min_dist = oo; + + for(const auto& wi : v_walls) + { + Vector pi = w0 & wi; + if(!wi.contains(pi) || !w0.contains(pi)) + continue; + double dist = pow(p[0]-pi[0],2) + pow(p[1]-pi[1],2); + min_dist = dist < min_dist ? dist : min_dist; + } + + return sqrt(min_dist); + } + + Vector shorter_contact_to_walls(const std::vector& v_walls, const Vector& p) + { + double min_dist = oo; + double bearing; + + for(double a = 0. ; a < 2.*M_PI ; a+=0.1) + { + double dist = shorter_dist_to_walls(v_walls, p, a); + if(dist < min_dist) + { + min_dist = dist; + bearing = a; + } + } + + return { min_dist, bearing }; + } +} \ No newline at end of file diff --git a/src/robotics/objects/codac_Wall.h b/src/robotics/objects/codac_Wall.h new file mode 100644 index 000000000..af7de4cad --- /dev/null +++ b/src/robotics/objects/codac_Wall.h @@ -0,0 +1,37 @@ +/** + * Beacon class + * ---------------------------------------------------------------------------- + * \date 2024 + * \author Simon Rohou + * \copyright Copyright 2024 Codac Team + * \license This program is distributed under the terms of + * the GNU Lesser General Public License (LGPL). + */ + +#ifndef __CODAC_WALL_H__ +#define __CODAC_WALL_H__ + +#include "codac_Vector.h" +#include "codac_IntervalVector.h" + +namespace codac +{ + class Wall + { + public: + + Wall(const Vector& c1, const Vector& c2); + bool contains(const Vector& p) const; + + //protected: + + Vector c1, c2; + }; + + Vector operator&(const Wall& w1, const Wall& w2); + + double shorter_dist_to_walls(const std::vector& v_walls, const Vector& p, double bearing); + Vector shorter_contact_to_walls(const std::vector& v_walls, const Vector& p); +} + +#endif \ No newline at end of file From 3643ea985ecf77c1f1eeaf159362db82faa72d9f Mon Sep 17 00:00:00 2001 From: SimonRohou Date: Mon, 12 Feb 2024 14:49:09 +0100 Subject: [PATCH 172/256] [win] corrected bug, _USE_MATH_DEFINES --- src/robotics/objects/codac_Wall.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/robotics/objects/codac_Wall.cpp b/src/robotics/objects/codac_Wall.cpp index 4842c4e6d..83e482011 100644 --- a/src/robotics/objects/codac_Wall.cpp +++ b/src/robotics/objects/codac_Wall.cpp @@ -8,6 +8,14 @@ * the GNU Lesser General Public License (LGPL). */ +#ifdef _MSC_VER +// Enable additional features in math.h. +#ifndef _USE_MATH_DEFINES +#define _USE_MATH_DEFINES +#endif // _USE_MATH_DEFINES +#include +#endif // _MSC_VER + #include #include "codac_Wall.h" #include "codac_predef_values.h" From 97ae62b886a90f4f0cfcd92880f064cb4ab799a9 Mon Sep 17 00:00:00 2001 From: SimonRohou Date: Mon, 12 Feb 2024 15:06:26 +0100 Subject: [PATCH 173/256] [py] CMake config --- python/CMakeLists.txt | 1 + python/codac_py_core.cpp | 2 ++ 2 files changed, 3 insertions(+) diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt index f1105a619..b14384514 100644 --- a/python/CMakeLists.txt +++ b/python/CMakeLists.txt @@ -113,6 +113,7 @@ src/robotics/codac_py_DataLoader.cpp src/robotics/codac_py_TPlane.cpp + src/robotics/codac_py_Wall.cpp ) target_include_directories(core diff --git a/python/codac_py_core.cpp b/python/codac_py_core.cpp index 9bb39c72c..810b90be3 100644 --- a/python/codac_py_core.cpp +++ b/python/codac_py_core.cpp @@ -87,6 +87,7 @@ void export_ConnectedSubset(py::module& m); void export_DataLoader(py::module& m); void export_TPlane(py::module& m); +void export_Wall(py::module& m); void export_sivia(py::module& m, py::class_& ctc, py::class_& sep); @@ -155,6 +156,7 @@ PYBIND11_MODULE(core, m) export_DataLoader(m); export_TPlane(m); + export_Wall(m); export_sivia(m, ctc, sep); From 3322f8f0ce9da6c1020b434a2382b6ce070b5d9f Mon Sep 17 00:00:00 2001 From: SimonRohou Date: Mon, 12 Feb 2024 18:40:49 +0100 Subject: [PATCH 174/256] [ex] added new robotics example --- examples/robotics/12_walls/CMakeLists.txt | 45 +++++++ examples/robotics/12_walls/main.cpp | 155 ++++++++++++++++++++++ examples/robotics/12_walls/main.py | 138 +++++++++++++++++++ 3 files changed, 338 insertions(+) create mode 100644 examples/robotics/12_walls/CMakeLists.txt create mode 100644 examples/robotics/12_walls/main.cpp create mode 100644 examples/robotics/12_walls/main.py diff --git a/examples/robotics/12_walls/CMakeLists.txt b/examples/robotics/12_walls/CMakeLists.txt new file mode 100644 index 000000000..d9ae42641 --- /dev/null +++ b/examples/robotics/12_walls/CMakeLists.txt @@ -0,0 +1,45 @@ +# ================================================================== +# codac / basics example - cmake configuration file +# ================================================================== + + cmake_minimum_required(VERSION 3.0.2) + project(codac_robotics_12 LANGUAGES CXX) + + set(CMAKE_CXX_STANDARD 17) + set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# Adding IBEX + + # In case you installed IBEX in a local directory, you need + # to specify its path with the CMAKE_PREFIX_PATH option. + # set(CMAKE_PREFIX_PATH "~/ibex-lib/build_install") + + find_package(IBEX REQUIRED) + ibex_init_common() # IBEX should have installed this function + message(STATUS "Found IBEX version ${IBEX_VERSION}") + +# Adding Eigen3 + + # In case you installed Eigen3 in a local directory, you need + # to specify its path with the CMAKE_PREFIX_PATH option, e.g. + # set(CMAKE_PREFIX_PATH "~/eigen/build_install") + + find_package(Eigen3 REQUIRED NO_MODULE) + message(STATUS "Found Eigen3 version ${Eigen3_VERSION}") + +# Adding Codac + + # In case you installed Codac in a local directory, you need + # to specify its path with the CMAKE_PREFIX_PATH option. + # set(CMAKE_PREFIX_PATH "~/codac/build_install") + + set(CMAKE_PREFIX_PATH "~/codac-lille/build_install") + find_package(CODAC REQUIRED) + message(STATUS "Found Codac version ${CODAC_VERSION}") + +# Compilation + + add_executable(${PROJECT_NAME} main.cpp) + target_compile_options(${PROJECT_NAME} PUBLIC ${CODAC_CXX_FLAGS}) + target_include_directories(${PROJECT_NAME} SYSTEM PUBLIC ${CODAC_INCLUDE_DIRS} ${EIGEN3_INCLUDE_DIRS}) + target_link_libraries(${PROJECT_NAME} PUBLIC ${CODAC_LIBRARIES} Ibex::ibex ${CODAC_LIBRARIES}) \ No newline at end of file diff --git a/examples/robotics/12_walls/main.cpp b/examples/robotics/12_walls/main.cpp new file mode 100644 index 000000000..b72adcd5e --- /dev/null +++ b/examples/robotics/12_walls/main.cpp @@ -0,0 +1,155 @@ +/** + * Codac - Examples + * Localization of a robot in a map of walls, based on two kinds of observations: + * - range-and-bearing: the robot is equipPed with a forward looking sonar + * - range-only: the robot only measures its distance to the closest wall + * ---------------------------------------------------------------------------- + * + * \date 2024 + * \author Simon Rohou + * \copyright Copyright 2024 Codac Team + * \license This program is distributed under the terms of + * the GNU Lesser General Public License (LGPL). + */ + +#include + +using namespace std; +using namespace codac; + +int main() +{ + /* =================== Parameters, truth and data =================== */ + + vector v_corners { + {-4,6},{2.5,6},{2.5,-1},{4.5,-1},{4.5,4}, + {11.5,6},{19,4},{19,-1},{25,-1},{25,-5}, + {16.5,-5},{16.5,1},{14.5,1},{14.5,-5},{8.5,-5}, + {8.5,1},{6.5,1},{6.5,-5},{3,-5},{-4,1} + }; + + vector v_walls; // building walls from corners + for(size_t i = 0 ; i < v_corners.size() ; i++) + v_walls.push_back({v_corners[i],v_corners[(i+1)%v_corners.size()]}); + + float dt = 0.01; // timestep for tubes accuracy + float dt_traj = 0.1*dt; // timestep for simulation + Interval tdomain(0,10); // temporal limits [t_0,t_f] + + // Analytical true trajectory (not used for resolution) + TrajectoryVector x_truth(tdomain, TFunction("( \ + 2*t+(1+cos(t*pi))/2 ; \ + 3*tanh(4*(1+cos((t+0.5)/2*pi))/2-2) ; \ + atan2( \ + -3*pi*(1-sqr(tanh(2*cos((2*t+1)*pi/4))))*sin((2*t+1)*pi/4), \ + 2-pi*sin(pi*t)/2) ; \ + sqrt( \ + sqr( -3*pi*(1-sqr(tanh(2*cos((2*t+1)*pi/4))))*sin((2*t+1)*pi/4) ) + \ + sqr( 2-pi*sin(pi*t)/2 ) \ + ))")); + + // Continuous measurements coming from the truth + Trajectory& measured_psi = x_truth[2].sample(dt_traj).make_continuous(); + measured_psi += RandTrajectory(tdomain, dt_traj, Interval(-0.01,0.01)); // adding some noise + Trajectory& measured_speed = x_truth[3].sample(dt_traj); + measured_speed += RandTrajectory(tdomain, dt_traj, Interval(-0.01,0.01)); // adding some noise + + /* =============== Defining domains for the variables =============== */ + + TubeVector x(tdomain, dt, 4); // 4d tube for state vectors + TubeVector v(tdomain, dt, 4); // 4d tube for derivatives of the states + TubeVector u(tdomain, dt, 2); // 2d tube for inputs of the system + + x[2] = Tube(measured_psi, dt).inflate(0.05); // measured_psi/speed are continuous measurements.. + x[3] = Tube(measured_speed, dt).inflate(0.1); // ..of the headings and speeds + + /* =============== Defining contractors to deal with equations =============== */ + + CtcFunction ctc_f( + Function("v[4]", "x[4]", "u[2]", + "(v[0]-x[3]*cos(x[2]) ; v[1]-x[3]*sin(x[2]) ; v[2]-u[0] ; v[3]-u[1])")); + + CtcFunction ctc_plus(Function("a", "b", "c", "a+b-c")); // constraint a+b-c=0 <=> a+c=c + CtcFunction ctc_minus(Function("a", "b", "c", "a-b-c")); // constraint a-b-c=0 <=> a-b=c + + CtcUnion ctc_walls(2); // contractor for the constraint \mathbf{q} \in \matbb{W} with \mathbb{W} the set of walls + for(const auto& wi : v_walls) + ctc_walls |= CtcSegment(wi.c1[0],wi.c1[1],wi.c2[0],wi.c2[1]); + + // We also use the predefined contractors ctc::polar, ctc::eval, ctc::dist, no need to build them + + /* =============== Adding the contractors to a network =============== */ + + ContractorNetwork cn; // creating a network + cn.add(ctc_f, {v, x, u}); // adding the f contractor, that is common over [t0,tf] + + vibes::beginDrawing(); + VIBesFigMap fig("Map"); + fig.set_properties(50, 50, 900, 500); + fig.add_tube(&x, "x", 0, 1); + fig.add_trajectory(&x_truth, "x*", 0, 1, 2); + fig.smooth_tube_drawing(true); + fig.show(0.); + fig.axis_limits(-5,26,-8.5,8.5); + + for(const auto& wi : v_walls) // drawing the map of walls + fig.draw_line({wi.c1[0],wi.c2[0]}, {wi.c1[1],wi.c2[1]}, "lightGray"); + + bool dist_only = !true; // in order to test two scenarii + + if(dist_only) // For this scenario we assume that \mathbf{x}(0) is known + { + Interval& t0 = cn.create_interm_var(0.); + IntervalVector& p0 = cn.create_interm_var(x_truth(0.)); + cn.add(ctc::eval, {t0, p0, x, v}); // <=> x(t0)=p0 + } + + // For each observation of the environment: + for(double t = tdomain.lb() ; t < tdomain.ub() ; t += 0.8) + { + Vector pi_truth = x_truth(t).subvector(0,1); + + Interval& ti = cn.create_interm_var(Interval(t)); + IntervalVector& pi = cn.create_interm_var(IntervalVector(4)); + IntervalVector& qi = cn.create_interm_var(IntervalVector(2)); + + cn.add(ctc::eval, {ti, pi, x, v}); // <=> x(ti)=pi + cn.add(ctc_walls, {qi}); // <=> \mathbf{q]_i \in \mathbb{W} + + if(!dist_only) // range-and-bearing observation (forward-looking sonar) + { + IntervalVector& di = cn.create_interm_var(IntervalVector(2)); + IntervalVector& yi = cn.create_interm_var(IntervalVector(2)); + yi[0] = shorter_dist_to_walls(v_walls, pi_truth, x_truth(t)[2]); + yi[1] = x_truth(t)[2]; // bearing is zero, azimuth is the heading + + yi[0].inflate(0.02); yi[1].inflate(0.05); + + cn.add(ctc_minus, {qi, cn.subvector(pi,0,1), di}); // <=> qi-pi[0,1]=di + cn.add(ctc::polar, {di, yi}); // di[0]=y1[0]*cos(yi[1]), di[1]=y1[0]*sin(yi[1]) + + fig.draw_pie(pi_truth[0], pi_truth[1], yi[0] | 0., yi[1], "lightGray[#FFFFFF88]"); + fig.draw_pie(pi_truth[0], pi_truth[1], yi[0], yi[1]); + } + + else // range-only observation + { + Interval& yi = cn.create_interm_var(shorter_contact_to_walls(v_walls, pi_truth)[0]); + yi.inflate(0.02); + + cn.add(ctc::dist, {cn.subvector(pi,0,1), qi, yi}); // <=> (pi[0]-qi[0])^2+(pi[1]-qi[1])^2=yi^2 + + fig.draw_circle(pi_truth[0], pi_truth[1], yi.mid(), "black[#80808088]"); + } + + vibes::drawVehicle(x_truth(t)[0], x_truth(t)[1], x_truth(t)[2]*180/M_PI, 0.7, "black[yellow]"); + } + + cn.contract(true); + + fig.show(0.); + fig.axis_limits(-5,26,-8.5,8.5); + + vibes::endDrawing(); + return x.contains(x_truth) != BoolInterval::NO ? EXIT_SUCCESS : EXIT_FAILURE; +} \ No newline at end of file diff --git a/examples/robotics/12_walls/main.py b/examples/robotics/12_walls/main.py new file mode 100644 index 000000000..e515ed312 --- /dev/null +++ b/examples/robotics/12_walls/main.py @@ -0,0 +1,138 @@ +# +# Codac - Examples +# Localization of a robot in a map of walls, based on two kinds of observations: +# - range-and-bearing: the robot is equipPed with a forward looking sonar +# - range-only: the robot only measures its distance to the closest wall +# ---------------------------------------------------------------------------- +# +# \date 2024 +# \author Simon Rohou +# \copyright Copyright 2024 Codac Team +# \license This program is distributed under the terms of +# the GNU Lesser General Public License (LGPL). + +from codac import * +import numpy as np + +# =================== Parameters, truth and data =================== + +v_corners = [ + [-4,6],[2.5,6],[2.5,-1],[4.5,-1],[4.5,4], + [11.5,6],[19,4],[19,-1],[25,-1],[25,-5], + [16.5,-5],[16.5,1],[14.5,1],[14.5,-5],[8.5,-5], + [8.5,1],[6.5,1],[6.5,-5],[3,-5],[-4,1] +] + +v_walls = [] # building walls from corners +for i in range(len(v_corners)): + v_walls.append(Wall(v_corners[i],v_corners[(i+1)%len(v_corners)])) + +dt = 0.01 # timestep for tubes accuracy +dt_traj = 0.1*dt # timestep for simulation +tdomain = Interval(0,10) # temporal limits [t_0,t_f] + +# Analytical true trajectory (not used for resolution) +x_truth = TrajectoryVector(tdomain, TFunction("( \ + 2*t+(1+cos(t*pi))/2 ; \ + 3*tanh(4*(1+cos((t+0.5)/2*pi))/2-2) ; \ + atan2( \ + -3*pi*(1-sqr(tanh(2*cos((2*t+1)*pi/4))))*sin((2*t+1)*pi/4), \ + 2-pi*sin(pi*t)/2) ; \ + sqrt( \ + sqr( -3*pi*(1-sqr(tanh(2*cos((2*t+1)*pi/4))))*sin((2*t+1)*pi/4) ) + \ + sqr( 2-pi*sin(pi*t)/2 ) \ + ))")) + +# Continuous measurements coming from the truth +measured_psi = x_truth[2].sample(dt_traj).make_continuous() +measured_psi += RandTrajectory(tdomain, dt_traj, Interval(-0.05,0.05)) # adding some noise +measured_speed = x_truth[3].sample(dt_traj) +measured_speed += RandTrajectory(tdomain, dt_traj, Interval(-0.1,0.1)) # adding some noise + +# =============== Defining domains for the variables =============== + +x = TubeVector(tdomain, dt, 4) # 4d tube for state vectors +v = TubeVector(tdomain, dt, 4) # 4d tube for derivatives of the states +u = TubeVector(tdomain, dt, 2) # 2d tube for inputs of the system + +x[2] = Tube(measured_psi, dt).inflate(0.05) # measured_psi/speed are continuous measurements.. +x[3] = Tube(measured_speed, dt).inflate(0.1) # ..of the headings and speeds + +# =============== Defining contractors to deal with equations =============== + +ctc_f = CtcFunction( + Function("v[4]", "x[4]", "u[2]", + "(v[0]-x[3]*cos(x[2]) ; v[1]-x[3]*sin(x[2]) ; v[2]-u[0] ; v[3]-u[1])")) + +ctc_plus = CtcFunction(Function("a", "b", "c", "a+b-c")) # constraint a+b-c=0 <=> a+c=c +ctc_minus = CtcFunction(Function("a", "b", "c", "a-b-c")) # constraint a-b-c=0 <=> a-b=c + +v_ctc_segm = [] +ctc_walls = CtcUnion(2) # contractor for the constraint \mathbf{q} \in \matbb{W} with \mathbb{W} the set of walls +for wi in v_walls: + v_ctc_segm.append(CtcSegment(wi.c1[0],wi.c1[1],wi.c2[0],wi.c2[1])) + ctc_walls |= v_ctc_segm[-1] + +# We also use the predefined contractors ctc::polar, ctc::eval, ctc::dist, no need to build them + +# =============== Adding the contractors to a network =============== + +cn = ContractorNetwork() # creating a network +cn.add(ctc_f, [v, x, u]) # adding the f contractor, that is common over [t0,tf] + +beginDrawing() +fig = VIBesFigMap("Map") +fig.set_properties(50, 50, 900, 500) +fig.add_tube(x, "x", 0, 1) +fig.add_trajectory(x_truth, "x*", 0, 1, 2) +fig.smooth_tube_drawing(True) +fig.show(0.) +fig.axis_limits(-5,26,-8.5,8.5) + +for wi in v_walls: # drawing the map of walls + fig.draw_line([wi.c1[0],wi.c2[0]], [wi.c1[1],wi.c2[1]], "lightGray") + +dist_only = not True # in order to test two scenarii + +if dist_only: # For this scenario we assume that \mathbf{x}(0) is known + t0 = Interval(0.) + p0 = IntervalVector(x_truth(0.)) + cn.add(ctc.eval, [t0, p0, x, v]) # <=> x(t0)=p0 + +# For each observation of the environment: +for t in np.arange(tdomain.lb(), tdomain.ub(), 0.8): + pi_truth = x_truth(t)[0:2] + ti = Interval(t) + pi = IntervalVector(4) + qi = IntervalVector(2) + cn.add(ctc.eval, [ti, pi, x, v]) # <=> x(ti)=pi + cn.add(ctc_walls, [qi]) # <=> \mathbf{q]_i \in \mathbb{W} + + if not dist_only: # range-and-bearing observation (forward-looking sonar) + di = IntervalVector(2) + yi = IntervalVector(2) + yi[0] = Interval(shorter_dist_to_walls(v_walls, pi_truth, x_truth(t)[2])) + yi[1] = Interval(x_truth(t)[2]) # bearing is zero, azimuth is the heading + + yi[0].inflate(0.02) + yi[1].inflate(0.05) + + cn.add(ctc_minus, [qi, cn.subvector(pi,0,1), di]) # <=> qi-pi[0,1]=di + cn.add(ctc.polar, [di, yi]) # di[0]=y1[0]*cos(yi[1]), di[1]=y1[0]*sin(yi[1]) + + fig.draw_pie(pi_truth[0], pi_truth[1], yi[0] | Interval(0), yi[1], "lightGray[#FFFFFF88]") + fig.draw_pie(pi_truth[0], pi_truth[1], yi[0], yi[1]) + + else: # range-only observation + yi = Interval(shorter_contact_to_walls(v_walls, pi_truth)[0]) + yi.inflate(0.02) + cn.add(ctc.dist, [cn.subvector(pi,0,1), qi, yi]) # <=> (pi[0]-qi[0])^2+(pi[1]-qi[1])^2=yi^2 + fig.draw_circle(pi_truth[0], pi_truth[1], yi.mid(), "black[#80808088]") + + fig.draw_vehicle([x_truth(t)[0], x_truth(t)[1], x_truth(t)[2]], 0.7) + +cn.contract(True) + +fig.show(0.) +fig.axis_limits(-5,26,-8.5,8.5) +endDrawing() \ No newline at end of file From 4260327736e311cd5065573b5674f2e9724bfb61 Mon Sep 17 00:00:00 2001 From: SimonRohou Date: Thu, 15 Feb 2024 13:43:06 +0100 Subject: [PATCH 175/256] [doc] added examples for Set4MOST --- doc/doc/toctree.rst | 8 ++ .../tutorial-set4most/01-examples/index.rst | 10 ++ .../01-examples/range-only-loc.py | 74 +++++++++++ doc/doc/tutorial-set4most/01-examples/slam.py | 112 +++++++++++++++++ .../01-examples/triskelion.py | 53 ++++++++ .../01-examples/walls-loc.py | 117 ++++++++++++++++++ doc/doc/tutorial-set4most/index.rst | 10 ++ 7 files changed, 384 insertions(+) create mode 100644 doc/doc/tutorial-set4most/01-examples/index.rst create mode 100644 doc/doc/tutorial-set4most/01-examples/range-only-loc.py create mode 100644 doc/doc/tutorial-set4most/01-examples/slam.py create mode 100644 doc/doc/tutorial-set4most/01-examples/triskelion.py create mode 100644 doc/doc/tutorial-set4most/01-examples/walls-loc.py create mode 100644 doc/doc/tutorial-set4most/index.rst diff --git a/doc/doc/toctree.rst b/doc/doc/toctree.rst index 3e33db033..5825ce4d2 100644 --- a/doc/doc/toctree.rst +++ b/doc/doc/toctree.rst @@ -36,6 +36,14 @@ Codac: constraint-programming for robotics .. /manual/09-extensions/index +.. toctree:: + :caption: Set4MOST + :maxdepth: 2 + :titlesonly: + + Examples + + .. toctree:: :caption: Tutorial JNRR23 :maxdepth: 2 diff --git a/doc/doc/tutorial-set4most/01-examples/index.rst b/doc/doc/tutorial-set4most/01-examples/index.rst new file mode 100644 index 000000000..9c54f8433 --- /dev/null +++ b/doc/doc/tutorial-set4most/01-examples/index.rst @@ -0,0 +1,10 @@ +.. _sec-tuto-set4most-01-main-page: + +############################# +Tutorial: Set4MOST - Examples +############################# + +* `Triskelion `_ +* `Range-only localization `_ +* `Walls localization `_ +* `SLAM `_ \ No newline at end of file diff --git a/doc/doc/tutorial-set4most/01-examples/range-only-loc.py b/doc/doc/tutorial-set4most/01-examples/range-only-loc.py new file mode 100644 index 000000000..25b85a289 --- /dev/null +++ b/doc/doc/tutorial-set4most/01-examples/range-only-loc.py @@ -0,0 +1,74 @@ +from codac import * +import math +import random +import time +import numpy as np + + +dt = 0.02 +iteration_dt = 0.2 +tdomain = Interval(0,15) # [t0,tf] + +# System input +u = Trajectory(tdomain, TFunction("3*(sin(t)^2)+t/100"), dt) + +# Actual trajectories (state + derivative) +# Note that these trajectories are unknown of the resolution +v_truth = TrajectoryVector(3) +x_truth = TrajectoryVector(3) +v_truth[2] = u +x_truth[2] = v_truth[2].primitive() +v_truth[0] = 10*cos(x_truth[2]) +v_truth[1] = 10*sin(x_truth[2]) +x_truth[0] = v_truth[0].primitive() +x_truth[1] = v_truth[1].primitive() + +beginDrawing() +fig_map = VIBesFigMap("slam") +fig_map.set_properties(50, 50, 1200, 600) +#fig_map.axis_limits(IntervalVector([[-34.8, 40.2],[-17.3,20.2]])) +fig_map.add_trajectory(x_truth, "truth", 0, 1, "black") +fig_map.smooth_tube_drawing(True) +fig_map.show(1.) + +########################################### +# Section C: Deadreckoning # +########################################### + +# Bounded trajectories (dead reckoning) +v = TubeVector(tdomain, dt, 3) +x = TubeVector(tdomain, dt, 3) + +# Heading measurement with bounded uncertainties +x[2] = Tube(x_truth[2], dt) + Interval(-0.03,0.03) +x.set([0,0,0], 0.) # setting a vector value at t=0 + +ctc_f = CtcFunction(Function("x[3]", "v[3]", "(v[0]-10*cos(x[2]) ; v[1]-10*sin(x[2]))")) +ctc.deriv # object already instanciated in the library + +cn = ContractorNetwork() +cn.add(ctc_f, [x,v]) +cn.add(ctc.deriv, [x,v]) + +cn.contract(True) + +fig_map.add_tube(x, "x", 0, 1) +fig_map.show(1.) + +########################################### +# Section D: Range-only localisation # +########################################### + +v_b = [ (6,12), (-2,-5), (-3,20), (3,4), (-10,0) ] + +for ti in np.arange(0,15): + k = random.randint(0,len(v_b)-1) # a random landmark is perceived + d = sqrt(sqr(x_truth(ti)[0]-v_b[k][0])+sqr(x_truth(ti)[1]-v_b[k][1])) + d += Interval(-0.03,0.03) + pi = IntervalVector(3) + cn.add(ctc.eval, [ti,pi,x,v]) + cn.add(ctc.dist, [pi[0],pi[1],v_b[k][0],v_b[k][1],d]) + +cn.contract(True) +fig_map.add_landmarks(v_b, 0.4) +fig_map.show(1.) \ No newline at end of file diff --git a/doc/doc/tutorial-set4most/01-examples/slam.py b/doc/doc/tutorial-set4most/01-examples/slam.py new file mode 100644 index 000000000..63352a61d --- /dev/null +++ b/doc/doc/tutorial-set4most/01-examples/slam.py @@ -0,0 +1,112 @@ +from codac import * +import math +import random +import time + + +# =========== CREATING DATA =========== + +dt = 0.02 +iteration_dt = 0.2 +tdomain = Interval(0,15) # [t0,tf] + +# Initial pose x0=(0,0,2) +x0 = (0,0,2) + +# System input +u = Trajectory(tdomain, TFunction("3*(sin(t)^2)+t/100"), dt) + +# Noise +#i_n = Interval(-0.03,0.03) # the noises are known to be bounded by i_n + +# Actual trajectories (state + derivative) +v_truth = TrajectoryVector(3) +x_truth = TrajectoryVector(3) +v_truth[2] = u +x_truth[2] = v_truth[2].primitive() + x0[2] +v_truth[0] = 10*cos(x_truth[2]) +v_truth[1] = 10*sin(x_truth[2]) +x_truth[0] = v_truth[0].primitive() + x0[0] +x_truth[1] = v_truth[1].primitive() + x0[1] + +# Bounded trajectories (dead reckoning) +v = TubeVector(tdomain, dt, 3) +x = TubeVector(tdomain, dt, 3) +#v[2] = Tube(u, dt).inflate(i_n.rad()) # command u with bounded uncertainties +x[2] = Tube(x_truth[2], dt) + Interval(-0.03,0.03) # heading measurement with bounded uncertainties +#v[0] = 10*cos(x[2]) +#v[1] = 10*sin(x[2]) +#x = v.primitive()+IntervalVector(x0) # dead reckoning +x.set([0,0,2],0.) +print(x_truth(0.)) +# Set of landmarks +v_m = [ (6,12), (-2,-5), (-3,20), (3,4), (-10,0) ] + +ctc_f = CtcFunction( + Function("v[3]", "x[3]", + "(v[0]-10*cos(x[2]) ; v[1]-10*sin(x[2]) )")) + +# =========== GRAPHICS =========== + +beginDrawing() + +fig_map = VIBesFigMap("slam") +fig_map.set_properties(50, 50, 1200, 600) +fig_map.add_tube(x, "x", 0, 1) +fig_map.add_trajectory(x_truth, "truth", 0, 1)#, "white") +fig_map.smooth_tube_drawing(True) +fig_map.add_landmarks(v_m, 0.4) +fig_map.show(1.) + + +# =========== CONTRACTOR NETWORK =========== + +v_m_boxes = [IntervalVector(2) for _ in v_m] + +# Contractor Network: + +cn = ContractorNetwork() +cn.add(ctc_f, [v,x]) +cn.add(ctc.deriv, [x,v]) + +t = tdomain.lb() +prev_t_obs = t + +while t < tdomain.ub(): + + if t-prev_t_obs > 5*dt: # new observation each 2*delta + + # Creating new observation to a random landmark + + landmark_id = random.randint(0,len(v_m)-1) # a random landmark is perceived + + pos_x = x_truth(t)[0:2] + pos_b = v_m[landmark_id] + + yi = Interval(sqrt(pow(pos_x[0]-pos_b[0],2)+pow(pos_x[1]-pos_b[1],2))) + yi.inflate(0.03) # adding range bounded uncertainty + + prev_t_obs = t + + # Adding related observation constraints to the network + + # Alias (for ease of reading) + b = v_m_boxes[landmark_id] + + # Intermediate variables + ti = Interval(t) + xi = IntervalVector(3) + + # Contractors + cn.add(ctc.eval, [ti, xi, x, v]) + cn.add(ctc.dist, [xi[0], xi[1], b[0], b[1], yi]) + + t+=dt + +cn.contract(True) # lets the solver run the remaining contractions + +fig_map.show() +for b in v_m_boxes: + fig_map.draw_box(b) + +endDrawing() \ No newline at end of file diff --git a/doc/doc/tutorial-set4most/01-examples/triskelion.py b/doc/doc/tutorial-set4most/01-examples/triskelion.py new file mode 100644 index 000000000..0ba38714e --- /dev/null +++ b/doc/doc/tutorial-set4most/01-examples/triskelion.py @@ -0,0 +1,53 @@ +from codac import * +from codac.unsupported import * +from math import pi +from vibes import vibes + +class SepDist(SepFwdBwd): + + def __init__(self, c, intv_r): + SepFwdBwd.__init__(self, + Function('x[2]', 'sqrt(sqr(x[0]-'+str(c[0])+')+sqr(x[1]-'+str(c[1])+'))'), + intv_r) + +class Sep2dRot(SepInverse): + + def __init__(self, sep, a): + SepInverse.__init__(self, sep, + Function('x[2]', '(x[0]*cos('+str(a)+')-x[1]*sin('+str(a)+');'+ + 'x[0]*sin('+str(a)+')+x[1]*cos('+str(a)+'))')) + +class SepTranslate(SepInverse): + + def __init__(self, sep, c): + SepInverse.__init__(self, sep, + Function('x[2]', '(x[0]-'+str(c[0])+';x[1]-'+str(c[1])+')')) + + +a1,a2 = 0, 2*pi/3 +c1 = [cos(a1)*3.9,sin(a1)*3.9] +c2 = [cos(a1)*5.3,sin(a1)*5.3] +c3 = [cos(a2)*3.9,sin(a2)*3.9] + +s_legs = SepDist(c1, Interval(0,4.8)) +s_legs &= SepDist(c2, Interval(3,oo)) +s_ = SepPolarXY(Interval(4.8,6), Interval(a1).inflate(0.5)) +s_legs &= SepNot(Sep2dRot(SepTranslate(s_,c3), a1)) +s_legs &= SepNot(SepDist(c3, Interval(0,5))) + +s_heart = SepDist(c3, Interval(0,4.8)) + +for i in range(2,5,2): + s_legs |= Sep2dRot(s_legs,i*pi/3) + s_heart &= Sep2dRot(s_heart,i*pi/3) + +s_triskel = s_heart | s_legs + + +x = IntervalVector([[-10,10],[-10,10]]) + +vibes.beginDrawing() +vibes.newFigure("Triskelion") +SIVIA(x, s_triskel, 0.05, fig_name="Triskelion") +vibes.setFigureProperties({"x":100, "y":100, "width":900, "height":900}) +endDrawing() \ No newline at end of file diff --git a/doc/doc/tutorial-set4most/01-examples/walls-loc.py b/doc/doc/tutorial-set4most/01-examples/walls-loc.py new file mode 100644 index 000000000..2e2a5aebd --- /dev/null +++ b/doc/doc/tutorial-set4most/01-examples/walls-loc.py @@ -0,0 +1,117 @@ +from codac import * +import numpy as np + +# =================== Parameters, truth and data =================== + +beginDrawing() + +v_corners = [ + [-4.5,6],[2.5,6.5],[2.8,-1.2],[4.5,-0.8],[4.8,4], + [11.5,6],[19,4],[19.5,-1],[25,-1.5],[24.5,-4.5], + [16.8,-5],[16.5,1.2],[14.7,1],[14.3,-5],[8.7,-4.5], + [8.5,1.2],[6.5,1],[6.3,-4.8],[3,-5],[-4,1] +] + +v_walls = [] # building walls from corners +for i in range(len(v_corners)): + v_walls.append(Wall(v_corners[i],v_corners[(i+1)%len(v_corners)])) + +dt = 0.01 # timestep for tubes accuracy +dt_traj = 0.1*dt # timestep for simulation +tdomain = Interval(0,10) # temporal limits [t_0,t_f] + +# Analytical true trajectory (not used for resolution) +x_truth = TrajectoryVector(tdomain, TFunction("( \ + 2*t+(1+cos(t*pi))/2 ; \ + 3*tanh(4*(1+cos((t+0.5)/2*pi))/2-2) ; \ + atan2( \ + -3*pi*(1-sqr(tanh(2*cos((2*t+1)*pi/4))))*sin((2*t+1)*pi/4), \ + 2-pi*sin(pi*t)/2) ; \ + sqrt( \ + sqr( -3*pi*(1-sqr(tanh(2*cos((2*t+1)*pi/4))))*sin((2*t+1)*pi/4) ) + \ + sqr( 2-pi*sin(pi*t)/2 ) \ + ))")) + +# Continuous measurements coming from the truth +measured_psi = x_truth[2].sample(dt_traj).make_continuous() +measured_psi += RandTrajectory(tdomain, dt_traj, Interval(-0.05,0.05)) # adding some noise +measured_speed = x_truth[3].sample(dt_traj) +measured_speed += RandTrajectory(tdomain, dt_traj, Interval(-0.1,0.1)) # adding some noise + +# =============== Defining domains for the variables =============== + +x = TubeVector(tdomain, dt, 4) # 4d tube for state vectors +v = TubeVector(tdomain, dt, 4) # 4d tube for derivatives of the states +u = TubeVector(tdomain, dt, 2) # 2d tube for inputs of the system + +x[2] = Tube(measured_psi, dt).inflate(0.05) # measured_psi/speed are continuous measurements.. +x[3] = Tube(measured_speed, dt).inflate(0.1) # ..of the headings and speeds + +# =============== Defining contractors to deal with equations =============== + +ctc_f = CtcFunction( # constraint v=f(x,u) <=> v-f(x,u)=0 + Function("v[4]", "x[4]", "u[2]", + "(v[0]-x[3]*cos(x[2]) ; v[1]-x[3]*sin(x[2]) ; v[2]-u[0] ; v[3]-u[1])")) + +ctc_plus = CtcFunction(Function("a", "b", "c", "a+b-c")) # constraint a+b-c=0 <=> a+c=c +ctc_minus = CtcFunction(Function("a", "b", "c", "a-b-c")) # constraint a-b-c=0 <=> a-b=c + +v_ctc_segm = [] +ctc_walls = CtcUnion(2) # contractor for the constraint \mathbf{q} \in \matbb{W} with \mathbb{W} the set of walls +for wi in v_walls: + v_ctc_segm.append(CtcSegment(wi.c1[0],wi.c1[1],wi.c2[0],wi.c2[1])) + ctc_walls |= v_ctc_segm[-1] + +fig = VIBesFigMap("Set inversion") +SIVIA([[-5,26],[-8.5,8.5]], ctc_walls, 0.5, fig_name="Set inversion") +fig.set_properties(50, 50, 900, 500) +fig.axis_limits(-5,26,-8.5,8.5) + +# We also use the predefined contractors ctc::polar, ctc::eval, ctc::dist, no need to build them + +# =============== Adding the contractors to a network =============== + +cn = ContractorNetwork() # creating a network +cn.add(ctc_f, [v, x, u]) # adding the f contractor, that is common over [t0,tf] + +fig = VIBesFigMap("Map") +fig.set_properties(50, 50, 900, 500) +fig.add_tube(x, "x", 0, 1) +fig.add_trajectory(x_truth, "x*", 0, 1, 2) +fig.smooth_tube_drawing(True) +fig.show(0.) +fig.axis_limits(-5,26,-8.5,8.5) + +for wi in v_walls: # drawing the map of walls + fig.draw_line([wi.c1[0],wi.c2[0]], [wi.c1[1],wi.c2[1]], "lightGray") + +# For each observation of the environment: +for t in np.arange(tdomain.lb(), tdomain.ub(), 0.8): + pi_truth = x_truth(t)[0:2] + ti = Interval(t) + pi = IntervalVector(4) + qi = IntervalVector(2) + cn.add(ctc.eval, [ti, pi, x, v]) # <=> x(ti)=pi + cn.add(ctc_walls, [qi]) # <=> \mathbf{q]_i \in \mathbb{W} + + di = IntervalVector(2) + yi = IntervalVector(2) + yi[0] = Interval(shorter_dist_to_walls(v_walls, pi_truth, x_truth(t)[2])) + yi[1] = Interval(x_truth(t)[2]) # bearing is zero, azimuth is the heading + + yi[0].inflate(0.02) + yi[1].inflate(0.05) + + cn.add(ctc_minus, [qi, cn.subvector(pi,0,1), di]) # <=> qi-pi[0,1]=di + cn.add(ctc.polar, [di, yi]) # di[0]=y1[0]*cos(yi[1]), di[1]=y1[0]*sin(yi[1]) + + fig.draw_pie(pi_truth[0], pi_truth[1], yi[0] | Interval(0), yi[1], "lightGray[#FFFFFF88]") + fig.draw_pie(pi_truth[0], pi_truth[1], yi[0], yi[1]) + + fig.draw_vehicle([x_truth(t)[0], x_truth(t)[1], x_truth(t)[2]], 0.7) + +cn.contract(True) + +fig.show(0.) +fig.axis_limits(-5,26,-8.5,8.5) +endDrawing() \ No newline at end of file diff --git a/doc/doc/tutorial-set4most/index.rst b/doc/doc/tutorial-set4most/index.rst new file mode 100644 index 000000000..9d96010ee --- /dev/null +++ b/doc/doc/tutorial-set4most/index.rst @@ -0,0 +1,10 @@ +.. _sec-tuto-set4most-main-page: + +################## +Tutorial: Set4MOST +################## + +* `Triskelion `_ +* `Range-only localization `_ +* `Walls localization `_ +* `SLAM `_ \ No newline at end of file From 7d54577b2b33506683bd08577e717a6c5a0b536b Mon Sep 17 00:00:00 2001 From: SimonRohou Date: Sat, 17 Feb 2024 13:34:06 +0100 Subject: [PATCH 176/256] [ex] robotics/12_wall: minor update --- examples/robotics/12_walls/build.sh | 11 +++++++++++ examples/robotics/12_walls/main.py | 16 +++++++++++----- 2 files changed, 22 insertions(+), 5 deletions(-) create mode 100755 examples/robotics/12_walls/build.sh diff --git a/examples/robotics/12_walls/build.sh b/examples/robotics/12_walls/build.sh new file mode 100755 index 000000000..3fa90fc6c --- /dev/null +++ b/examples/robotics/12_walls/build.sh @@ -0,0 +1,11 @@ +# ================================================================== +# Codac - build script +# ================================================================== + +#!/bin/bash + +mkdir build -p +cd build +cmake .. -DCMAKE_BUILD_TYPE=Debug +make +cd .. \ No newline at end of file diff --git a/examples/robotics/12_walls/main.py b/examples/robotics/12_walls/main.py index e515ed312..5689a7b2f 100644 --- a/examples/robotics/12_walls/main.py +++ b/examples/robotics/12_walls/main.py @@ -16,11 +16,13 @@ # =================== Parameters, truth and data =================== +beginDrawing() + v_corners = [ - [-4,6],[2.5,6],[2.5,-1],[4.5,-1],[4.5,4], - [11.5,6],[19,4],[19,-1],[25,-1],[25,-5], - [16.5,-5],[16.5,1],[14.5,1],[14.5,-5],[8.5,-5], - [8.5,1],[6.5,1],[6.5,-5],[3,-5],[-4,1] + [-4.5,6],[2.5,6.5],[2.8,-1.2],[4.5,-0.8],[4.8,4], + [11.5,6],[19,4],[19.5,-1],[25,-1.5],[24.5,-4.5], + [16.8,-5],[16.5,1.2],[14.7,1],[14.3,-5],[8.7,-4.5], + [8.5,1.2],[6.5,1],[6.3,-4.8],[3,-5],[-4,1] ] v_walls = [] # building walls from corners @@ -73,6 +75,11 @@ v_ctc_segm.append(CtcSegment(wi.c1[0],wi.c1[1],wi.c2[0],wi.c2[1])) ctc_walls |= v_ctc_segm[-1] +fig = VIBesFigMap("Set inversion") +SIVIA([[-5,26],[-8.5,8.5]], ctc_walls, 0.5, fig_name="Set inversion") +fig.set_properties(50, 50, 900, 500) +fig.axis_limits(-5,26,-8.5,8.5) + # We also use the predefined contractors ctc::polar, ctc::eval, ctc::dist, no need to build them # =============== Adding the contractors to a network =============== @@ -80,7 +87,6 @@ cn = ContractorNetwork() # creating a network cn.add(ctc_f, [v, x, u]) # adding the f contractor, that is common over [t0,tf] -beginDrawing() fig = VIBesFigMap("Map") fig.set_properties(50, 50, 900, 500) fig.add_tube(x, "x", 0, 1) From 8705a6e812e79d5f0bae1cfc43bf7e88af8ec4c3 Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Sat, 10 Feb 2024 23:34:43 +0100 Subject: [PATCH 177/256] Change unused /NoPath parameter in Chocolatey package --- packages/choco/codac/codac.nuspec | 4 ++-- .../codac/tools/chocolateybeforemodify.ps1 | 4 ++++ .../choco/codac/tools/chocolateyinstall.ps1 | 20 +++++++++---------- .../choco/codac/tools/chocolateyuninstall.ps1 | 4 ++++ 4 files changed, 20 insertions(+), 12 deletions(-) diff --git a/packages/choco/codac/codac.nuspec b/packages/choco/codac/codac.nuspec index 2f7ed607e..b04c96304 100644 --- a/packages/choco/codac/codac.nuspec +++ b/packages/choco/codac/codac.nuspec @@ -32,9 +32,9 @@ The following package parameters can be set: - `/urlX:URL` - Same as above, with X in [1,99], except this will not disable the installation of the MinGW libraries compatible with the corresponding MinGW Chocolatey package dependency. - `/checksumX:SHA256` - SHA256 checksum of the binary package specified by the `/urlX` parameter. If needed, use the standard parameter `choco install --ignore-checksums ...` for trusted sources. - `/InstallDir:INSTALLDIR` - Installation directory. -- `/NoPath` - Will not try to update Windows `PATH`. +- `/Path` - Will try to update Windows `PATH`. - `/NoRegistry` - Will not try to update Windows registry. -To pass package parameters, use `--params "''"` (e.g. `choco install codac --params "'/NoPath /NoRegistry'"`), and to install another binary package, try e.g. +To pass package parameters, use `--params "''"` (e.g. `choco install codac --params "'/Path /NoRegistry'"`), and to install another binary package, try e.g. ``` choco install -y --ignore-dependencies codac --params "'/url:https://github.com/codac-team/codac/releases/download/v1/codac_x64_vc17.zip'" ``` diff --git a/packages/choco/codac/tools/chocolateybeforemodify.ps1 b/packages/choco/codac/tools/chocolateybeforemodify.ps1 index c3cb63574..547954416 100644 --- a/packages/choco/codac/tools/chocolateybeforemodify.ps1 +++ b/packages/choco/codac/tools/chocolateybeforemodify.ps1 @@ -15,6 +15,10 @@ Write-Host "Codac is going to be uninstalled from '$installDir'" $root = Join-Path $installDir "codac" +$newpath = [environment]::GetEnvironmentVariable("Path","Machine") +$newpath = ($newpath.Split(';') | Where-Object { $_ -ne "$root\bin" }) -join ';' +[environment]::SetEnvironmentVariable("Path",$newpath,"Machine") + try { Get-ItemProperty -Path $CMakeSystemRepositoryPath\$CMakePackageName | Select-Object -ExpandProperty $CMakePackageName$CMakePackageVer -ErrorAction Stop | Out-Null Remove-ItemProperty -Path $CMakeSystemRepositoryPath\$CMakePackageName -Name $CMakePackageName$CMakePackageVer diff --git a/packages/choco/codac/tools/chocolateyinstall.ps1 b/packages/choco/codac/tools/chocolateyinstall.ps1 index 6f08af63e..4d49157c1 100644 --- a/packages/choco/codac/tools/chocolateyinstall.ps1 +++ b/packages/choco/codac/tools/chocolateyinstall.ps1 @@ -96,11 +96,11 @@ if (!$pp['NoRegistry']) { New-Item "$CMakeSystemRepositoryPath\$CMakePackageName" -ItemType directory -Force New-ItemProperty -Name "$CMakePackageName$CMakePackageVer" -PropertyType String -Value "$root\share\$CMakePackageName\cmake" -Path "$CMakeSystemRepositoryPath\$CMakePackageName" -Force } -#$pathtoadd = "$root\bin" -#if (!($pp['NoPath']) -and !([environment]::GetEnvironmentVariable("Path","Machine") -match [regex]::escape($pathtoadd))) { -# $newpath = [environment]::GetEnvironmentVariable("Path","Machine") + ";$pathtoadd" -# [environment]::SetEnvironmentVariable("Path",$newpath,"Machine") -#} +$pathtoadd = "$root\bin" +if (($pp['Path']) -and !([environment]::GetEnvironmentVariable("Path","Machine") -match [regex]::escape($pathtoadd))) { + $newpath = [environment]::GetEnvironmentVariable("Path","Machine") + ";$pathtoadd" + [environment]::SetEnvironmentVariable("Path",$newpath,"Machine") +} for ($i = 1; $i -le 99; $i++) { if ($pp['url'+$i]) { @@ -163,10 +163,10 @@ for ($i = 1; $i -le 99; $i++) { New-Item "$CMakeSystemRepositoryPath\$CMakePackageName" -ItemType directory -Force New-ItemProperty -Name "$CMakePackageName$CMakePackageVer" -PropertyType String -Value "$root\share\$CMakePackageName\cmake" -Path "$CMakeSystemRepositoryPath\$CMakePackageName" -Force } - #$pathtoadd = "$root\bin" - #if (!($pp['NoPath']) -and !([environment]::GetEnvironmentVariable("Path","Machine") -match [regex]::escape($pathtoadd))) { - # $newpath = [environment]::GetEnvironmentVariable("Path","Machine") + ";$pathtoadd" - # [environment]::SetEnvironmentVariable("Path",$newpath,"Machine") - #} + $pathtoadd = "$root\bin" + if (($pp['Path']) -and !([environment]::GetEnvironmentVariable("Path","Machine") -match [regex]::escape($pathtoadd))) { + $newpath = [environment]::GetEnvironmentVariable("Path","Machine") + ";$pathtoadd" + [environment]::SetEnvironmentVariable("Path",$newpath,"Machine") + } } } diff --git a/packages/choco/codac/tools/chocolateyuninstall.ps1 b/packages/choco/codac/tools/chocolateyuninstall.ps1 index c3cb63574..547954416 100644 --- a/packages/choco/codac/tools/chocolateyuninstall.ps1 +++ b/packages/choco/codac/tools/chocolateyuninstall.ps1 @@ -15,6 +15,10 @@ Write-Host "Codac is going to be uninstalled from '$installDir'" $root = Join-Path $installDir "codac" +$newpath = [environment]::GetEnvironmentVariable("Path","Machine") +$newpath = ($newpath.Split(';') | Where-Object { $_ -ne "$root\bin" }) -join ';' +[environment]::SetEnvironmentVariable("Path",$newpath,"Machine") + try { Get-ItemProperty -Path $CMakeSystemRepositoryPath\$CMakePackageName | Select-Object -ExpandProperty $CMakePackageName$CMakePackageVer -ErrorAction Stop | Out-Null Remove-ItemProperty -Path $CMakeSystemRepositoryPath\$CMakePackageName -Name $CMakePackageName$CMakePackageVer From 0715086bfa47a34cb94a3859963be5e02fff7245 Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Thu, 8 Feb 2024 23:03:32 +0100 Subject: [PATCH 178/256] Update workflows --- .github/workflows/unixmatrix.yml | 109 +++++++++++++++++++++---------- 1 file changed, 73 insertions(+), 36 deletions(-) diff --git a/.github/workflows/unixmatrix.yml b/.github/workflows/unixmatrix.yml index 212d89277..06c2835ae 100644 --- a/.github/workflows/unixmatrix.yml +++ b/.github/workflows/unixmatrix.yml @@ -28,24 +28,28 @@ jobs: fail-fast: false matrix: cfg: - - { os: windows-2022, shell: cmd, arch: x64, runtime: vc17, cmake_params: '-G "Visual Studio 17" -T v143 -A x64', cmake_flags: ' /wd4267 /wd4244 /wd4305 /wd4996', test_config: 'Release/', desc: 'Windows Visual Studio 2022 x64' } - - { os: windows-2022, shell: cmd, arch: x86, runtime: vc17, cmake_params: '-G "Visual Studio 17" -T v143 -A Win32', cmake_flags: ' /wd4267 /wd4244 /wd4305 /wd4996', test_config: 'Release/', choco_flags: '--x86', desc: 'Windows Visual Studio 2022 x86' } - - { os: windows-2022, shell: cmd, arch: x64, runtime: vc16, cmake_params: '-G "Visual Studio 17" -T v142 -A x64', cmake_flags: ' /wd4267 /wd4244 /wd4305 /wd4996', test_config: 'Release/', desc: 'Windows Visual Studio 2019 x64' } - - { os: windows-2022, shell: cmd, arch: x86, runtime: vc16, cmake_params: '-G "Visual Studio 17" -T v142 -A Win32', cmake_flags: ' /wd4267 /wd4244 /wd4305 /wd4996', test_config: 'Release/', choco_flags: '--x86', desc: 'Windows Visual Studio 2019 x86' } - - { os: windows-2022, shell: cmd, arch: x64, runtime: vc15, cmake_params: '-G "Visual Studio 17" -T v141 -A x64', cmake_flags: ' /wd4267 /wd4244 /wd4305 /wd4996', test_config: 'Release/', desc: 'Windows Visual Studio 2017 x64' } - - { os: windows-2022, shell: cmd, arch: x86, runtime: vc15, cmake_params: '-G "Visual Studio 17" -T v141 -A Win32', cmake_flags: ' /wd4267 /wd4244 /wd4305 /wd4996', test_config: 'Release/', choco_flags: '--x86', desc: 'Windows Visual Studio 2017 x86' } - - { os: windows-2022, shell: cmd, arch: x64, runtime: mingw11, cmake_params: '-G "MinGW Makefiles"', cmake_flags: '-fPIC', desc: 'Windows MinGW 11.2.0 x64' } - - { os: windows-2022, shell: cmd, arch: x86, runtime: mingw11, cmake_params: '-G "MinGW Makefiles"', cmake_flags: '-fPIC', choco_flags: '--x86', desc: 'Windows MinGW 11.2.0 x86' } - - { os: windows-2019, shell: cmd, arch: x64, runtime: mingw8, cmake_params: '-G "MinGW Makefiles"', cmake_flags: '-fPIC', desc: 'Windows MinGW 8.1.0 x64' } - - { os: windows-2019, shell: cmd, arch: x86, runtime: mingw8, cmake_params: '-G "MinGW Makefiles"', cmake_flags: '-fPIC', choco_flags: '--x86', desc: 'Windows MinGW 8.1.0 x86' } - - { os: windows-2019, shell: cmd, arch: x64, runtime: mingw7, cmake_params: '-G "MinGW Makefiles"', cmake_flags: '-fPIC', desc: 'Windows MinGW 7.3.0 x64' } - - { os: windows-2019, shell: cmd, arch: x86, runtime: mingw7, cmake_params: '-G "MinGW Makefiles"', cmake_flags: '-fPIC', choco_flags: '--x86', desc: 'Windows MinGW 7.3.0 x86' } - - { os: ubuntu-22.04, shell: bash, arch: amd64, runtime: jammy, cmake_flags: '-fPIC', desc: 'Ubuntu 22.04' } - - { os: ubuntu-20.04, shell: bash, arch: amd64, runtime: focal, cmake_flags: '-fPIC', desc: 'Ubuntu 20.04' } - - { os: macos-13, shell: bash, arch: arm64, runtime: ventura, cmake_params: '-D CMAKE_SYSTEM_NAME=Darwin -D CMAKE_OSX_ARCHITECTURES=arm64', cmake_flags: '-fPIC', desc: 'macOS Ventura arm64' } - - { os: macos-13, shell: bash, arch: x86_64, runtime: ventura, cmake_params: '-D CMAKE_SYSTEM_NAME=Darwin -D CMAKE_OSX_ARCHITECTURES=x86_64', cmake_flags: '-fPIC', desc: 'macOS Ventura x86_64' } - - { os: macos-12, shell: bash, arch: arm64, runtime: monterey, cmake_params: '-D CMAKE_SYSTEM_NAME=Darwin -D CMAKE_OSX_ARCHITECTURES=arm64', cmake_flags: '-fPIC', desc: 'macOS Monterey arm64' } - - { os: macos-12, shell: bash, arch: x86_64, runtime: monterey, cmake_params: '-D CMAKE_SYSTEM_NAME=Darwin -D CMAKE_OSX_ARCHITECTURES=x86_64', cmake_flags: '-fPIC', desc: 'macOS Monterey x86_64' } + - { os: windows-2022, shell: cmd, arch: x64, bitness: 64, runtime: vc17, cmake_params: '-G "Visual Studio 17" -T v143 -A x64', cmake_flags: ' /wd4267 /wd4244 /wd4305 /wd4996', test_config: 'Release/', desc: 'Windows Visual Studio 2022 x64' } + - { os: windows-2022, shell: cmd, arch: x86, bitness: 32, runtime: vc17, cmake_params: '-G "Visual Studio 17" -T v143 -A Win32', cmake_flags: ' /wd4267 /wd4244 /wd4305 /wd4996', test_config: 'Release/', choco_flags: '--x86', desc: 'Windows Visual Studio 2022 x86' } + - { os: windows-2022, shell: cmd, arch: x64, bitness: 64, runtime: vc16, cmake_params: '-G "Visual Studio 17" -T v142 -A x64', cmake_flags: ' /wd4267 /wd4244 /wd4305 /wd4996', test_config: 'Release/', desc: 'Windows Visual Studio 2019 x64' } + - { os: windows-2022, shell: cmd, arch: x86, bitness: 32, runtime: vc16, cmake_params: '-G "Visual Studio 17" -T v142 -A Win32', cmake_flags: ' /wd4267 /wd4244 /wd4305 /wd4996', test_config: 'Release/', choco_flags: '--x86', desc: 'Windows Visual Studio 2019 x86' } + - { os: windows-2022, shell: cmd, arch: x64, bitness: 64, runtime: vc15, cmake_params: '-G "Visual Studio 17" -T v141 -A x64', cmake_flags: ' /wd4267 /wd4244 /wd4305 /wd4996', test_config: 'Release/', desc: 'Windows Visual Studio 2017 x64' } + - { os: windows-2022, shell: cmd, arch: x86, bitness: 32, runtime: vc15, cmake_params: '-G "Visual Studio 17" -T v141 -A Win32', cmake_flags: ' /wd4267 /wd4244 /wd4305 /wd4996', test_config: 'Release/', choco_flags: '--x86', desc: 'Windows Visual Studio 2017 x86' } + - { os: windows-2022, shell: cmd, arch: x64, bitness: 64, runtime: mingw13, cmake_params: '-G "MinGW Makefiles"', cmake_flags: '-fPIC', desc: 'Windows MinGW 13.2.0 x64' } + - { os: windows-2022, shell: cmd, arch: x86, bitness: 32, runtime: mingw13, cmake_params: '-G "MinGW Makefiles"', cmake_flags: '-fPIC', choco_flags: '--x86', desc: 'Windows MinGW 13.2.0 x86' } + - { os: windows-2022, shell: cmd, arch: x64, bitness: 64, runtime: mingw12, cmake_params: '-G "MinGW Makefiles"', cmake_flags: '-fPIC', desc: 'Windows MinGW 12.2.0 x64' } + - { os: windows-2022, shell: cmd, arch: x86, bitness: 32, runtime: mingw12, cmake_params: '-G "MinGW Makefiles"', cmake_flags: '-fPIC', choco_flags: '--x86', desc: 'Windows MinGW 12.2.0 x86' } + - { os: windows-2022, shell: cmd, arch: x64, bitness: 64, runtime: mingw11, cmake_params: '-G "MinGW Makefiles"', cmake_flags: '-fPIC', desc: 'Windows MinGW 11.2.0 x64' } + - { os: windows-2022, shell: cmd, arch: x86, bitness: 32, runtime: mingw11, cmake_params: '-G "MinGW Makefiles"', cmake_flags: '-fPIC', choco_flags: '--x86', desc: 'Windows MinGW 11.2.0 x86' } + - { os: windows-2019, shell: cmd, arch: x64, bitness: 64, runtime: mingw8, cmake_params: '-G "MinGW Makefiles"', cmake_flags: '-fPIC', desc: 'Windows MinGW 8.1.0 x64' } + - { os: windows-2019, shell: cmd, arch: x86, bitness: 32, runtime: mingw8, cmake_params: '-G "MinGW Makefiles"', cmake_flags: '-fPIC', choco_flags: '--x86', desc: 'Windows MinGW 8.1.0 x86' } + - { os: windows-2019, shell: cmd, arch: x64, bitness: 64, runtime: mingw7, cmake_params: '-G "MinGW Makefiles"', cmake_flags: '-fPIC', desc: 'Windows MinGW 7.3.0 x64' } + - { os: windows-2019, shell: cmd, arch: x86, bitness: 32, runtime: mingw7, cmake_params: '-G "MinGW Makefiles"', cmake_flags: '-fPIC', choco_flags: '--x86', desc: 'Windows MinGW 7.3.0 x86' } + - { os: ubuntu-22.04, shell: bash, arch: amd64, bitness: 64, runtime: jammy, cmake_flags: '-fPIC', desc: 'Ubuntu 22.04' } + - { os: ubuntu-20.04, shell: bash, arch: amd64, bitness: 64, runtime: focal, cmake_flags: '-fPIC', desc: 'Ubuntu 20.04' } + - { os: macos-13, shell: bash, arch: arm64, bitness: 64, runtime: ventura, cmake_params: '-D CMAKE_SYSTEM_NAME=Darwin -D CMAKE_OSX_ARCHITECTURES=arm64', cmake_flags: '-fPIC', desc: 'macOS Ventura arm64' } + - { os: macos-13, shell: bash, arch: x86_64, bitness: 64, runtime: ventura, cmake_params: '-D CMAKE_SYSTEM_NAME=Darwin -D CMAKE_OSX_ARCHITECTURES=x86_64', cmake_flags: '-fPIC', desc: 'macOS Ventura x86_64' } + - { os: macos-12, shell: bash, arch: arm64, bitness: 64, runtime: monterey, cmake_params: '-D CMAKE_SYSTEM_NAME=Darwin -D CMAKE_OSX_ARCHITECTURES=arm64', cmake_flags: '-fPIC', desc: 'macOS Monterey arm64' } + - { os: macos-12, shell: bash, arch: x86_64, bitness: 64, runtime: monterey, cmake_params: '-D CMAKE_SYSTEM_NAME=Darwin -D CMAKE_OSX_ARCHITECTURES=x86_64', cmake_flags: '-fPIC', desc: 'macOS Monterey x86_64' } name: ${{ matrix.cfg.desc }} steps: - uses: actions/checkout@v4 @@ -70,18 +74,46 @@ jobs: - run: echo "PACKAGE_VERSION=$SOFTWARE_VERSION-${DEBIAN_PACKAGE_REV}${{ matrix.cfg.runtime }}$PACKAGE_REV" >> $GITHUB_ENV shell: bash if: runner.os=='Linux' - - run: choco install -y -r --no-progress mingw --version=7.3.0 --force ${{ matrix.cfg.choco_flags }} - if: (runner.os=='Windows')&&(matrix.cfg.runtime=='mingw7') - - run: choco install -y -r --no-progress mingw --version=8.1.0 --force ${{ matrix.cfg.choco_flags }} - if: (runner.os=='Windows')&&(matrix.cfg.runtime=='mingw8')&&(matrix.cfg.arch=='x86') - - run: choco install -y -r --no-progress mingw --version=11.2.0.07112021 --force ${{ matrix.cfg.choco_flags }} - if: (runner.os=='Windows')&&(matrix.cfg.runtime=='mingw11') - - run: echo C:\ProgramData\chocolatey\lib\mingw\tools\install\mingw32\bin>>%GITHUB_PATH% - if: startsWith(matrix.cfg.runtime, 'mingw')&&(matrix.cfg.arch=='x86') - - run: echo C:\ProgramData\chocolatey\lib\mingw\tools\install\mingw64\bin>>%GITHUB_PATH% - if: startsWith(matrix.cfg.runtime, 'mingw')&&(matrix.cfg.arch=='x64') - run: | choco install -y -r --no-progress checksum wget zip + rem Workaround to try solve some random package download failures... + wget https://www.ensta-bretagne.fr/packages/choco/winflexbison.2.4.9.20170215.nupkg --no-check-certificate -nv + wget https://www.ensta-bretagne.fr/packages/choco/patch.2.5.9.nupkg --no-check-certificate -nv + choco install -y -r --no-progress -s . winflexbison patch ${{ matrix.cfg.choco_flags }} + del /f /q winflexbison.2.4.9.20170215.nupkg patch.2.5.9.nupkg + 7z x windows_server_core_prereq.zip -o"%SystemRoot%" -y & cd. & rem ksuser.dll already on windows-2016...? + wget http://www.ensta-bretagne.fr/lebars/Share/cmake_extra_tools.zip --no-check-certificate -nv + 7z x cmake_extra_tools.zip -o"%SystemDrive%" -y + del /f /q cmake_extra_tools.zip + wget https://gist.github.com/lebarsfa/237841f9e5dad55ef192713b3b1b2f16/raw/04d77ced3457346c55f183ca12a10dbcb850e6d5/refreshenv.bashrc --no-check-certificate -nv + move /y refreshenv.bashrc %USERPROFILE% + if: runner.os=='Windows' + - run: | + choco install -y -r --no-progress mingw --version=7.3.0 --force ${{ matrix.cfg.choco_flags }} + %SystemDrive%\cmake_extra_tools\pathman /as C:\ProgramData\chocolatey\lib\mingw\tools\install\mingw${{ matrix.cfg.bitness }}\bin & cd. & rem Non-zero exit code...? + echo export BASHMINGWPATH=/c/ProgramData/chocolatey/lib/mingw/tools/install/mingw${{ matrix.cfg.bitness }}/bin>>%USERPROFILE%\.bashrc + if: (matrix.cfg.runtime=='mingw7') + - run: | + choco install -y -r --no-progress mingw --version=8.1.0 --force ${{ matrix.cfg.choco_flags }} + %SystemDrive%\cmake_extra_tools\pathman /as C:\ProgramData\chocolatey\lib\mingw\tools\install\mingw${{ matrix.cfg.bitness }}\bin & cd. & rem Non-zero exit code...? + echo export BASHMINGWPATH=/c/ProgramData/chocolatey/lib/mingw/tools/install/mingw${{ matrix.cfg.bitness }}/bin>>%USERPROFILE%\.bashrc + if: (matrix.cfg.runtime=='mingw8') + - run: | + choco install -y -r --no-progress mingw --version=11.2.0.07112021 --force ${{ matrix.cfg.choco_flags }} + %SystemDrive%\cmake_extra_tools\pathman /as C:\ProgramData\chocolatey\lib\mingw\tools\install\mingw${{ matrix.cfg.bitness }}\bin & cd. & rem Non-zero exit code...? + echo export BASHMINGWPATH=/c/ProgramData/chocolatey/lib/mingw/tools/install/mingw${{ matrix.cfg.bitness }}/bin>>%USERPROFILE%\.bashrc + if: (matrix.cfg.runtime=='mingw11') + - run: | + choco install -y -r --no-progress mingw --version=12.2.0.03042023 --force ${{ matrix.cfg.choco_flags }} + %SystemDrive%\cmake_extra_tools\pathman /as C:\ProgramData\chocolatey\lib\mingw\tools\install\mingw${{ matrix.cfg.bitness }}\bin & cd. & rem Non-zero exit code...? + echo export BASHMINGWPATH=/c/ProgramData/chocolatey/lib/mingw/tools/install/mingw${{ matrix.cfg.bitness }}/bin>>%USERPROFILE%\.bashrc + if: (matrix.cfg.runtime=='mingw12') + - run: | + choco install -y -r --no-progress mingw --version=13.2.0 --force ${{ matrix.cfg.choco_flags }} + %SystemDrive%\cmake_extra_tools\pathman /as C:\ProgramData\mingw64\mingw${{ matrix.cfg.bitness }}\bin & cd. & rem Non-zero exit code...? + echo export BASHMINGWPATH=/c/ProgramData/mingw64/mingw${{ matrix.cfg.bitness }}/bin>>%USERPROFILE%\.bashrc + if: (matrix.cfg.runtime=='mingw13') + - run: | choco install -y -r --no-progress eigen --version=3.4.0 ${{ matrix.cfg.choco_flags }} wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20231007/ibex.2.8.9.20231007.nupkg --no-check-certificate -nv choco install -y -r --no-progress --ignore-dependencies -s . ibex --version=2.8.9.20231007 ${{ matrix.cfg.choco_flags }} --params "'/url:https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20231007/ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip'" @@ -107,6 +139,7 @@ jobs: # - run: git clone --depth 1 -b master https://github.com/lebarsfa/ibex-lib.git ; cd ibex-lib ; mkdir build ; cd build ; cmake -E env CXXFLAGS="${{ matrix.cfg.cmake_flags }}" CFLAGS="${{ matrix.cfg.cmake_flags }}" cmake ${{ matrix.cfg.cmake_params }} -D CMAKE_INSTALL_PREFIX="../../ibex" .. ; cmake --build . --config Release --target install ; cd ../.. # shell: bash - run: | + if [ ${{ runner.os }} = Windows ]; then source ~/refreshenv.bashrc ; refreshenv ; export PATH=$BASHMINGWPATH:$PATH ; fi mkdir build ; cd build cmake -E env CXXFLAGS="${{ matrix.cfg.cmake_flags }}" CFLAGS="${{ matrix.cfg.cmake_flags }}" cmake ${{ matrix.cfg.cmake_params }} -D CMAKE_INSTALL_PREFIX="../codac" .. cmake --build . --config Debug --target install @@ -116,6 +149,7 @@ jobs: zip -q -r codac_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip codac shell: bash - run: | + if [ ${{ runner.os }} = Windows ]; then source ~/refreshenv.bashrc ; refreshenv ; export PATH=$BASHMINGWPATH:$PATH ; fi mkdir -p codac_standalone/example ; cd codac_standalone wget https://community.chocolatey.org/api/v2/package/eigen/3.4.0 --no-check-certificate -nv ; unzip -q 3.4.0 -d eigen ; rm -Rf 3.4.0 eigen/*.xml eigen/*.nuspec eigen/_* eigen/package eigen/tools ; cd eigen/share/cmake ; if [ ${{ runner.os }} = macOS ]; then sed -i "" "s/\\/..\\/../\\/..\\/..\\/../" *.cmake ; else sed -i "s/\\/..\\/../\\/..\\/..\\/../" *.cmake ; fi ; cd .. ; mkdir eigen3 ; mv cmake eigen3/ ; cd ../.. if [ ${{ runner.os }} = Windows ]; then cp -Rf /C/ProgramData/chocolatey/lib/ibex . ; rm -Rf ibex/tools ibex/ibex.* @@ -125,6 +159,7 @@ jobs: cp -Rf ../codac . ; cp -Rf ../tests/test_codac/* ./example/ ; cd .. ; zip -q -r codac_standalone_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip codac_standalone shell: bash - run: | + if [ ${{ runner.os }} = Windows ]; then source ~/refreshenv.bashrc ; refreshenv ; export PATH=$BASHMINGWPATH:$PATH ; fi cd codac_standalone/example cmake ${{ matrix.cfg.cmake_params }} . cmake --build . --config Release @@ -134,6 +169,7 @@ jobs: shell: bash if: (runner.os!='macOS')||(matrix.cfg.arch=='x86_64') - run: | + if [ ${{ runner.os }} = Windows ]; then source ~/refreshenv.bashrc ; refreshenv ; export PATH=$BASHMINGWPATH:$PATH ; fi cd packages/choco sed_param=s/\1\<\\/version\>/\${PACKAGE_VERSION}\<\\/version\>/ sed -i "$sed_param" codac/codac.nuspec @@ -165,7 +201,16 @@ jobs: cp -Rf codac/* /usr/local/ shell: bash if: (runner.os=='macOS')&&(matrix.cfg.arch=='x86_64') + - uses: xresloader/upload-to-github-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + file: "*.zip;*.nupkg;*.deb" + overwrite: true + tag_name: autotagname-${{ github.sha }} + if: (github.event_name != 'pull_request')&&(github.ref_name == 'master') - run: | + if [ ${{ runner.os }} = Windows ]; then source ~/refreshenv.bashrc ; refreshenv ; export PATH=$BASHMINGWPATH:$PATH ; fi rm -Rf codac cd tests/test_codac cmake ${{ matrix.cfg.cmake_params }} . @@ -175,11 +220,3 @@ jobs: cd ../.. shell: bash if: (runner.os!='macOS')||(matrix.cfg.arch=='x86_64') - - uses: xresloader/upload-to-github-release@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - file: "*.zip;*.nupkg;*.deb" - overwrite: true - tag_name: autotagname-${{ github.sha }} - if: (github.event_name != 'pull_request')&&(github.ref_name == 'master') From 12e6f9e64338d3b250185b6e3ea355c56cb7aabd Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Sat, 17 Feb 2024 18:38:20 +0100 Subject: [PATCH 179/256] Change registry key name --- packages/choco/codac/tools/chocolateybeforemodify.ps1 | 4 ++-- packages/choco/codac/tools/chocolateyinstall.ps1 | 4 ++-- packages/choco/codac/tools/chocolateyuninstall.ps1 | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/choco/codac/tools/chocolateybeforemodify.ps1 b/packages/choco/codac/tools/chocolateybeforemodify.ps1 index 547954416..b8b121868 100644 --- a/packages/choco/codac/tools/chocolateybeforemodify.ps1 +++ b/packages/choco/codac/tools/chocolateybeforemodify.ps1 @@ -20,8 +20,8 @@ $newpath = ($newpath.Split(';') | Where-Object { $_ -ne "$root\bin" }) -join ';' [environment]::SetEnvironmentVariable("Path",$newpath,"Machine") try { - Get-ItemProperty -Path $CMakeSystemRepositoryPath\$CMakePackageName | Select-Object -ExpandProperty $CMakePackageName$CMakePackageVer -ErrorAction Stop | Out-Null - Remove-ItemProperty -Path $CMakeSystemRepositoryPath\$CMakePackageName -Name $CMakePackageName$CMakePackageVer + Get-ItemProperty -Path $CMakeSystemRepositoryPath\$CMakePackageName | Select-Object -ExpandProperty "$CMakePackageName$CMakePackageVer`_$arch" -ErrorAction Stop | Out-Null + Remove-ItemProperty -Path $CMakeSystemRepositoryPath\$CMakePackageName -Name "$CMakePackageName$CMakePackageVer`_$arch" } catch { diff --git a/packages/choco/codac/tools/chocolateyinstall.ps1 b/packages/choco/codac/tools/chocolateyinstall.ps1 index 4d49157c1..3c59891f2 100644 --- a/packages/choco/codac/tools/chocolateyinstall.ps1 +++ b/packages/choco/codac/tools/chocolateyinstall.ps1 @@ -94,7 +94,7 @@ else { if (!$pp['NoRegistry']) { New-Item "$CMakeSystemRepositoryPath\$CMakePackageName" -ItemType directory -Force - New-ItemProperty -Name "$CMakePackageName$CMakePackageVer" -PropertyType String -Value "$root\share\$CMakePackageName\cmake" -Path "$CMakeSystemRepositoryPath\$CMakePackageName" -Force + New-ItemProperty -Name "$CMakePackageName$CMakePackageVer`_$arch" -PropertyType String -Value "$root\share\$CMakePackageName\cmake" -Path "$CMakeSystemRepositoryPath\$CMakePackageName" -Force } $pathtoadd = "$root\bin" if (($pp['Path']) -and !([environment]::GetEnvironmentVariable("Path","Machine") -match [regex]::escape($pathtoadd))) { @@ -161,7 +161,7 @@ for ($i = 1; $i -le 99; $i++) { if (!$pp['NoRegistry']) { New-Item "$CMakeSystemRepositoryPath\$CMakePackageName" -ItemType directory -Force - New-ItemProperty -Name "$CMakePackageName$CMakePackageVer" -PropertyType String -Value "$root\share\$CMakePackageName\cmake" -Path "$CMakeSystemRepositoryPath\$CMakePackageName" -Force + New-ItemProperty -Name "$CMakePackageName$CMakePackageVer`_$arch" -PropertyType String -Value "$root\share\$CMakePackageName\cmake" -Path "$CMakeSystemRepositoryPath\$CMakePackageName" -Force } $pathtoadd = "$root\bin" if (($pp['Path']) -and !([environment]::GetEnvironmentVariable("Path","Machine") -match [regex]::escape($pathtoadd))) { diff --git a/packages/choco/codac/tools/chocolateyuninstall.ps1 b/packages/choco/codac/tools/chocolateyuninstall.ps1 index 547954416..b8b121868 100644 --- a/packages/choco/codac/tools/chocolateyuninstall.ps1 +++ b/packages/choco/codac/tools/chocolateyuninstall.ps1 @@ -20,8 +20,8 @@ $newpath = ($newpath.Split(';') | Where-Object { $_ -ne "$root\bin" }) -join ';' [environment]::SetEnvironmentVariable("Path",$newpath,"Machine") try { - Get-ItemProperty -Path $CMakeSystemRepositoryPath\$CMakePackageName | Select-Object -ExpandProperty $CMakePackageName$CMakePackageVer -ErrorAction Stop | Out-Null - Remove-ItemProperty -Path $CMakeSystemRepositoryPath\$CMakePackageName -Name $CMakePackageName$CMakePackageVer + Get-ItemProperty -Path $CMakeSystemRepositoryPath\$CMakePackageName | Select-Object -ExpandProperty "$CMakePackageName$CMakePackageVer`_$arch" -ErrorAction Stop | Out-Null + Remove-ItemProperty -Path $CMakeSystemRepositoryPath\$CMakePackageName -Name "$CMakePackageName$CMakePackageVer`_$arch" } catch { From 5b971694c32077dd5c332cdec236a6f50c29ee2f Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Sat, 24 Feb 2024 22:38:45 +0100 Subject: [PATCH 180/256] Update Eigen package used in workflows --- .github/workflows/dockerpi.yml | 2 +- .github/workflows/unixmatrix.yml | 4 ++-- .github/workflows/vcmatrix.yml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/dockerpi.yml b/.github/workflows/dockerpi.yml index 5f74ccacf..3e86f6577 100644 --- a/.github/workflows/dockerpi.yml +++ b/.github/workflows/dockerpi.yml @@ -16,7 +16,7 @@ jobs: fetch-depth: 0 clean: false - run: docker run --rm --privileged multiarch/qemu-user-static:register --reset - - run: docker run -i -v "${PWD}/..:${PWD}/.." lebarsfa/pi:buster-for-codac /bin/bash -c "uname -a && cat /etc/os-release && cd ${PWD} && pwd && lsb_release -a && sudo apt-get -q update --allow-releaseinfo-change ; sudo apt-get -y install libeigen3-dev ; wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20231007/ibex_armv6hf_buster.zip --no-check-certificate -nv ; unzip -q ibex_armv6hf_buster.zip && rm -Rf ibex_armv6hf_buster.zip && sudo cp -Rf ibex/* /usr/local/ && mkdir build ; cd build && cmake -E env CXXFLAGS="-fPIC" CFLAGS="-fPIC" cmake -D CMAKE_INSTALL_PREFIX=../codac .. && cmake --build . --target install && cd .. && mkdir -p codac_standalone/example ; cd codac_standalone ; wget https://community.chocolatey.org/api/v2/package/eigen/3.4.0 --no-check-certificate -nv ; unzip -q 3.4.0 -d eigen ; rm -Rf 3.4.0 eigen/*.xml eigen/*.nuspec eigen/_* eigen/package eigen/tools ; mkdir -p ibex/include ; mkdir -p ibex/lib ; mkdir -p ibex/share ; mkdir -p ibex/bin ; cp -Rf /usr/local/include/ibex* ibex/include/ ; cp -Rf /usr/local/lib/*ibex* ibex/lib/ ; cp -Rf /usr/local/share/*ibex* ibex/share/ ; cp -Rf /usr/local/share/pkgconfig ibex/share/ ; cp -Rf /usr/local/bin/ibex* ibex/bin/ ; cp -Rf ../codac . ; cp -Rf ../tests/test_codac/* ./example/ ; cd .. ; zip -q -r codac_standalone_armv6hf_buster.zip codac_standalone ; cd codac_standalone/example && cmake . && cmake --build . && ./my_project" + - run: docker run -i -v "${PWD}/..:${PWD}/.." lebarsfa/pi:buster-for-codac /bin/bash -c "uname -a && cat /etc/os-release && cd ${PWD} && pwd && lsb_release -a && sudo apt-get -q update --allow-releaseinfo-change ; sudo apt-get -y install libeigen3-dev ; wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20231007/ibex_armv6hf_buster.zip --no-check-certificate -nv ; unzip -q ibex_armv6hf_buster.zip && rm -Rf ibex_armv6hf_buster.zip && sudo cp -Rf ibex/* /usr/local/ && mkdir build ; cd build && cmake -E env CXXFLAGS="-fPIC" CFLAGS="-fPIC" cmake -D CMAKE_INSTALL_PREFIX=../codac .. && cmake --build . --target install && cd .. && mkdir -p codac_standalone/example ; cd codac_standalone ; wget https://community.chocolatey.org/api/v2/package/eigen/3.4.0.20240224 --no-check-certificate -nv ; unzip -q 3.4.0.20240224 -d eigen ; rm -Rf 3.4.0.20240224 eigen/*.xml eigen/*.nuspec eigen/_* eigen/package eigen/tools ; mkdir -p ibex/include ; mkdir -p ibex/lib ; mkdir -p ibex/share ; mkdir -p ibex/bin ; cp -Rf /usr/local/include/ibex* ibex/include/ ; cp -Rf /usr/local/lib/*ibex* ibex/lib/ ; cp -Rf /usr/local/share/*ibex* ibex/share/ ; cp -Rf /usr/local/share/pkgconfig ibex/share/ ; cp -Rf /usr/local/bin/ibex* ibex/bin/ ; cp -Rf ../codac . ; cp -Rf ../tests/test_codac/* ./example/ ; cd .. ; zip -q -r codac_standalone_armv6hf_buster.zip codac_standalone ; cd codac_standalone/example && cmake . && cmake --build . && ./my_project" - uses: xresloader/upload-to-github-release@v1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/unixmatrix.yml b/.github/workflows/unixmatrix.yml index 06c2835ae..30ff9e1fc 100644 --- a/.github/workflows/unixmatrix.yml +++ b/.github/workflows/unixmatrix.yml @@ -114,7 +114,7 @@ jobs: echo export BASHMINGWPATH=/c/ProgramData/mingw64/mingw${{ matrix.cfg.bitness }}/bin>>%USERPROFILE%\.bashrc if: (matrix.cfg.runtime=='mingw13') - run: | - choco install -y -r --no-progress eigen --version=3.4.0 ${{ matrix.cfg.choco_flags }} + choco install -y -r --no-progress eigen --version=3.4.0.20240224 ${{ matrix.cfg.choco_flags }} wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20231007/ibex.2.8.9.20231007.nupkg --no-check-certificate -nv choco install -y -r --no-progress --ignore-dependencies -s . ibex --version=2.8.9.20231007 ${{ matrix.cfg.choco_flags }} --params "'/url:https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20231007/ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip'" del /f /q ibex.2.8.9.20231007.nupkg @@ -151,7 +151,7 @@ jobs: - run: | if [ ${{ runner.os }} = Windows ]; then source ~/refreshenv.bashrc ; refreshenv ; export PATH=$BASHMINGWPATH:$PATH ; fi mkdir -p codac_standalone/example ; cd codac_standalone - wget https://community.chocolatey.org/api/v2/package/eigen/3.4.0 --no-check-certificate -nv ; unzip -q 3.4.0 -d eigen ; rm -Rf 3.4.0 eigen/*.xml eigen/*.nuspec eigen/_* eigen/package eigen/tools ; cd eigen/share/cmake ; if [ ${{ runner.os }} = macOS ]; then sed -i "" "s/\\/..\\/../\\/..\\/..\\/../" *.cmake ; else sed -i "s/\\/..\\/../\\/..\\/..\\/../" *.cmake ; fi ; cd .. ; mkdir eigen3 ; mv cmake eigen3/ ; cd ../.. + wget https://community.chocolatey.org/api/v2/package/eigen/3.4.0.20240224 --no-check-certificate -nv ; unzip -q 3.4.0.20240224 -d eigen ; rm -Rf 3.4.0.20240224 eigen/*.xml eigen/*.nuspec eigen/_* eigen/package eigen/tools if [ ${{ runner.os }} = Windows ]; then cp -Rf /C/ProgramData/chocolatey/lib/ibex . ; rm -Rf ibex/tools ibex/ibex.* elif [ ${{ runner.os }} = Linux ]; then mkdir -p ibex/include ; mkdir -p ibex/lib ; mkdir -p ibex/share ; mkdir -p ibex/bin ; cp -Rf /usr/include/ibex* ibex/include/ ; cp -Rf /usr/lib/*ibex* ibex/lib/ ; cp -Rf /usr/share/*ibex* ibex/share/ ; cp -Rf /usr/share/pkgconfig ibex/share/ ; cp -Rf /usr/bin/ibex* ibex/bin/ elif [ ${{ runner.os }} = macOS ]; then cp -Rf ../ibex . diff --git a/.github/workflows/vcmatrix.yml b/.github/workflows/vcmatrix.yml index 4fbf9bcfc..6191e34b5 100644 --- a/.github/workflows/vcmatrix.yml +++ b/.github/workflows/vcmatrix.yml @@ -54,7 +54,7 @@ jobs: 7z x C:\Windows\Temp\windows_extra_tools.zip -o"C:\Windows" -y shell: pwsh if: runner.os=='Windows' - - run: choco install -y -r --no-progress eigen --version=3.4.0 ${{ matrix.cfg.choco_flags }} + - run: choco install -y -r --no-progress eigen --version=3.4.0.20240224 ${{ matrix.cfg.choco_flags }} if: runner.os=='Windows' - run: wget https://packages.ensta-bretagne.fr/choco/doxygen.install.1.9.6.nupkg --no-check-certificate -nv & choco upgrade -y -r --no-progress -s . doxygen.install --version=1.9.6 & del /f /q doxygen.install.1.9.6.nupkg & choco install -y -r --no-progress graphviz & pip install --upgrade wheel setuptools sphinx breathe sphinx-issues sphinx-tabs sphinx_rtd_theme sphinx-reredirects if: runner.os=='Windows' From 27d673f1ae4ad94b3a641b1ba5c2365112efc8ad Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Sun, 25 Feb 2024 21:56:47 +0100 Subject: [PATCH 181/256] Test specific IBEX version --- .github/workflows/dockerpi.yml | 2 +- .github/workflows/macosmatrix.yml | 2 +- .github/workflows/unixmatrix.yml | 14 +++++++------- .github/workflows/vcmatrix.yml | 6 +++--- scripts/docker/build_pybinding.sh | 2 +- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/.github/workflows/dockerpi.yml b/.github/workflows/dockerpi.yml index 3e86f6577..b209c9f09 100644 --- a/.github/workflows/dockerpi.yml +++ b/.github/workflows/dockerpi.yml @@ -16,7 +16,7 @@ jobs: fetch-depth: 0 clean: false - run: docker run --rm --privileged multiarch/qemu-user-static:register --reset - - run: docker run -i -v "${PWD}/..:${PWD}/.." lebarsfa/pi:buster-for-codac /bin/bash -c "uname -a && cat /etc/os-release && cd ${PWD} && pwd && lsb_release -a && sudo apt-get -q update --allow-releaseinfo-change ; sudo apt-get -y install libeigen3-dev ; wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20231007/ibex_armv6hf_buster.zip --no-check-certificate -nv ; unzip -q ibex_armv6hf_buster.zip && rm -Rf ibex_armv6hf_buster.zip && sudo cp -Rf ibex/* /usr/local/ && mkdir build ; cd build && cmake -E env CXXFLAGS="-fPIC" CFLAGS="-fPIC" cmake -D CMAKE_INSTALL_PREFIX=../codac .. && cmake --build . --target install && cd .. && mkdir -p codac_standalone/example ; cd codac_standalone ; wget https://community.chocolatey.org/api/v2/package/eigen/3.4.0.20240224 --no-check-certificate -nv ; unzip -q 3.4.0.20240224 -d eigen ; rm -Rf 3.4.0.20240224 eigen/*.xml eigen/*.nuspec eigen/_* eigen/package eigen/tools ; mkdir -p ibex/include ; mkdir -p ibex/lib ; mkdir -p ibex/share ; mkdir -p ibex/bin ; cp -Rf /usr/local/include/ibex* ibex/include/ ; cp -Rf /usr/local/lib/*ibex* ibex/lib/ ; cp -Rf /usr/local/share/*ibex* ibex/share/ ; cp -Rf /usr/local/share/pkgconfig ibex/share/ ; cp -Rf /usr/local/bin/ibex* ibex/bin/ ; cp -Rf ../codac . ; cp -Rf ../tests/test_codac/* ./example/ ; cd .. ; zip -q -r codac_standalone_armv6hf_buster.zip codac_standalone ; cd codac_standalone/example && cmake . && cmake --build . && ./my_project" + - run: docker run -i -v "${PWD}/..:${PWD}/.." lebarsfa/pi:buster-for-codac /bin/bash -c "uname -a && cat /etc/os-release && cd ${PWD} && pwd && lsb_release -a && sudo apt-get -q update --allow-releaseinfo-change ; sudo apt-get -y install libeigen3-dev ; wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20240224/ibex_armv6hf_buster.zip --no-check-certificate -nv ; unzip -q ibex_armv6hf_buster.zip && rm -Rf ibex_armv6hf_buster.zip && sudo cp -Rf ibex/* /usr/local/ && mkdir build ; cd build && cmake -E env CXXFLAGS="-fPIC" CFLAGS="-fPIC" cmake -D CMAKE_INSTALL_PREFIX=../codac .. && cmake --build . --target install && cd .. && mkdir -p codac_standalone/example ; cd codac_standalone ; wget https://community.chocolatey.org/api/v2/package/eigen/3.4.0.20240224 --no-check-certificate -nv ; unzip -q 3.4.0.20240224 -d eigen ; rm -Rf 3.4.0.20240224 eigen/*.xml eigen/*.nuspec eigen/_* eigen/package eigen/tools ; mkdir -p ibex/include ; mkdir -p ibex/lib ; mkdir -p ibex/share ; mkdir -p ibex/bin ; cp -Rf /usr/local/include/ibex* ibex/include/ ; cp -Rf /usr/local/lib/*ibex* ibex/lib/ ; cp -Rf /usr/local/share/*ibex* ibex/share/ ; cp -Rf /usr/local/share/pkgconfig ibex/share/ ; cp -Rf /usr/local/bin/ibex* ibex/bin/ ; cp -Rf ../codac . ; cp -Rf ../tests/test_codac/* ./example/ ; cd .. ; zip -q -r codac_standalone_armv6hf_buster.zip codac_standalone ; cd codac_standalone/example && cmake . && cmake --build . && ./my_project" - uses: xresloader/upload-to-github-release@v1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/macosmatrix.yml b/.github/workflows/macosmatrix.yml index f97273c39..fec9aca99 100644 --- a/.github/workflows/macosmatrix.yml +++ b/.github/workflows/macosmatrix.yml @@ -57,7 +57,7 @@ jobs: - run: brew install graphviz ; wget https://github.com/Homebrew/homebrew-core/raw/d2267b9f2ad247bc9c8273eb755b39566a474a70/Formula/doxygen.rb ; brew reinstall ./doxygen.rb ; brew pin doxygen ; python -m pip install --upgrade pip ; pip install --upgrade wheel setuptools sphinx breathe sphinx_rtd_theme sphinx-tabs sphinx-issues sphinx-reredirects if: runner.os=='macOS' - run: | - wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20231007/ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip --no-check-certificate -nv + wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20240224/ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip --no-check-certificate -nv unzip -q ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip rm -Rf ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip cp -Rf ibex/* /usr/local/ diff --git a/.github/workflows/unixmatrix.yml b/.github/workflows/unixmatrix.yml index 30ff9e1fc..92654c55b 100644 --- a/.github/workflows/unixmatrix.yml +++ b/.github/workflows/unixmatrix.yml @@ -115,23 +115,23 @@ jobs: if: (matrix.cfg.runtime=='mingw13') - run: | choco install -y -r --no-progress eigen --version=3.4.0.20240224 ${{ matrix.cfg.choco_flags }} - wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20231007/ibex.2.8.9.20231007.nupkg --no-check-certificate -nv - choco install -y -r --no-progress --ignore-dependencies -s . ibex --version=2.8.9.20231007 ${{ matrix.cfg.choco_flags }} --params "'/url:https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20231007/ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip'" - del /f /q ibex.2.8.9.20231007.nupkg + wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20240224/ibex.2.8.9.20240224.nupkg --no-check-certificate -nv + choco install -y -r --no-progress --ignore-dependencies -s . ibex --version=2.8.9.20240224 ${{ matrix.cfg.choco_flags }} --params "'/url:https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20240224/ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip'" + del /f /q ibex.2.8.9.20240224.nupkg if: runner.os=='Windows' - run: | # Replace these 2 lines by the next ones to test a specific binary package of IBEX. #sudo sh -c 'echo "deb [trusted=yes] https://packages.ensta-bretagne.fr/`lsb_release --id -s | tr [:upper:] [:lower:]`/`lsb_release -cs` ./" > /etc/apt/sources.list.d/ensta-bretagne.list' #sudo apt-get -q update ; sudo apt-get -y install libibex-dev libeigen3-dev dpkg-dev || true sudo apt-get -q update ; sudo apt-get -y install libeigen3-dev dpkg-dev || true - wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20231007/libibex-dev-2.8.9.20231007-0${{ matrix.cfg.runtime }}0_${{ matrix.cfg.arch }}.deb --no-check-certificate -nv - sudo dpkg -i libibex-dev-2.8.9.20231007-0${{ matrix.cfg.runtime }}0_${{ matrix.cfg.arch }}.deb - rm -Rf libibex-dev-2.8.9.20231007-0${{ matrix.cfg.runtime }}0_${{ matrix.cfg.arch }}.deb + wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20240224/libibex-dev-2.8.9.20240224-0${{ matrix.cfg.runtime }}0_${{ matrix.cfg.arch }}.deb --no-check-certificate -nv + sudo dpkg -i libibex-dev-2.8.9.20240224-0${{ matrix.cfg.runtime }}0_${{ matrix.cfg.arch }}.deb + rm -Rf libibex-dev-2.8.9.20240224-0${{ matrix.cfg.runtime }}0_${{ matrix.cfg.arch }}.deb shell: bash if: runner.os=='Linux' - run: | brew install eigen - wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20231007/ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip --no-check-certificate -nv + wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20240224/ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip --no-check-certificate -nv unzip -q ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip rm -Rf ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip sudo cp -Rf ibex/* /usr/local/ diff --git a/.github/workflows/vcmatrix.yml b/.github/workflows/vcmatrix.yml index 6191e34b5..343ea72ed 100644 --- a/.github/workflows/vcmatrix.yml +++ b/.github/workflows/vcmatrix.yml @@ -59,9 +59,9 @@ jobs: - run: wget https://packages.ensta-bretagne.fr/choco/doxygen.install.1.9.6.nupkg --no-check-certificate -nv & choco upgrade -y -r --no-progress -s . doxygen.install --version=1.9.6 & del /f /q doxygen.install.1.9.6.nupkg & choco install -y -r --no-progress graphviz & pip install --upgrade wheel setuptools sphinx breathe sphinx-issues sphinx-tabs sphinx_rtd_theme sphinx-reredirects if: runner.os=='Windows' - run: | - wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20231007/ibex.2.8.9.20231007.nupkg --no-check-certificate -nv - choco install -y -r --no-progress --ignore-dependencies -s . ibex --version=2.8.9.20231007 ${{ matrix.cfg.choco_flags }} --params "'/url:https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20231007/ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip'" - del /f /q ibex.2.8.9.20231007.nupkg + wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20240224/ibex.2.8.9.20240224.nupkg --no-check-certificate -nv + choco install -y -r --no-progress --ignore-dependencies -s . ibex --version=2.8.9.20240224 ${{ matrix.cfg.choco_flags }} --params "'/url:https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20240224/ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip'" + del /f /q ibex.2.8.9.20240224.nupkg - run: | mkdir build ; cd build cmake -E env CXXFLAGS=" /wd4267 /wd4244 /wd4305 /wd4996" CFLAGS=" /wd4267 /wd4244 /wd4305 /wd4996" cmake ${{ matrix.cfg.cmake_params }} -D CMAKE_INSTALL_PREFIX="../codac" -D WITH_PYTHON=ON .. diff --git a/scripts/docker/build_pybinding.sh b/scripts/docker/build_pybinding.sh index 292d9e0b3..46ef0236a 100755 --- a/scripts/docker/build_pybinding.sh +++ b/scripts/docker/build_pybinding.sh @@ -2,7 +2,7 @@ set -e -x -wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20231007/ibex_x86_64_manylinux2010.zip --no-check-certificate -nv +wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20240224/ibex_x86_64_manylinux2010.zip --no-check-certificate -nv unzip -q ibex_x86_64_manylinux2010.zip rm -Rf ibex_x86_64_manylinux2010.zip sudo cp -Rf ibex/* /usr/local/ From 50b138694a2f89bca1b7d6dbff190ac364082e68 Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Mon, 26 Feb 2024 16:18:15 +0100 Subject: [PATCH 182/256] Workarounds of unsupported operators or special functions for MATLAB compatibility, see https://fr.mathworks.com/help/matlab/matlab_external/differences-between-matlab-python.html --- .../core/contractors/static/codac_py_Ctc.cpp | 25 +++++++++++++++++++ python/src/robotics/codac_py_Wall.cpp | 4 +++ 2 files changed, 29 insertions(+) diff --git a/python/src/core/contractors/static/codac_py_Ctc.cpp b/python/src/core/contractors/static/codac_py_Ctc.cpp index 8d2c1ec39..894816d8b 100644 --- a/python/src/core/contractors/static/codac_py_Ctc.cpp +++ b/python/src/core/contractors/static/codac_py_Ctc.cpp @@ -59,6 +59,19 @@ py::class_ export_Ctc(py::module& m) py::return_value_policy::take_ownership, py::keep_alive<0,1>(), py::keep_alive<0,2>()) + // For MATLAB compatibility. + .def("union", [](Ctc& c1, Ctc& c2) + { + CtcUnion *cu = new CtcUnion(2); + cu->add_raw_ptr(&c1); + cu->add_raw_ptr(&c2); + // todo: manage delete + return cu; + }, + DOC_CTC_OR, + py::return_value_policy::take_ownership, + py::keep_alive<0,1>(), py::keep_alive<0,2>()) + .def("__and__", [](Ctc& c1, Ctc& c2) { return new CtcCompo(c1, c2); @@ -67,6 +80,16 @@ py::class_ export_Ctc(py::module& m) DOC_CTC_AND, py::return_value_policy::take_ownership, py::keep_alive<0,1>(), py::keep_alive<0,2>()) + + // For MATLAB compatibility. + .def("inter", [](Ctc& c1, Ctc& c2) + { + return new CtcCompo(c1, c2); + // todo: manage delete + }, + DOC_CTC_AND, + py::return_value_policy::take_ownership, + py::keep_alive<0,1>(), py::keep_alive<0,2>()) ; // Export comparaison constant @@ -108,6 +131,8 @@ py::class_ export_Ctc(py::module& m) py::keep_alive<0,1>(), py::keep_alive<0,2>(), py::keep_alive<0,3>(), "c1"_a, "c2"_a, "c3"_a) .def("contract", (void (Ctc::*)(IntervalVector&)) &CtcUnion::contract) .def("__ior__", [](CtcUnion& cu, Ctc& c) { return cu.add_raw_ptr(&c); }, py::keep_alive<1,2>(), py::return_value_policy::take_ownership) + // For MATLAB compatibility. + .def("union_self", [](CtcUnion& cu, Ctc& c) { return cu.add_raw_ptr(&c); }, py::keep_alive<1,2>(), py::return_value_policy::take_ownership) ; // Export CtcCompo diff --git a/python/src/robotics/codac_py_Wall.cpp b/python/src/robotics/codac_py_Wall.cpp index 56b304b86..cce059b71 100644 --- a/python/src/robotics/codac_py_Wall.cpp +++ b/python/src/robotics/codac_py_Wall.cpp @@ -33,6 +33,10 @@ void export_Wall(py::module& m) .def(py::init(), "todo") .def("contains", &Wall::contains, "todo", "p"_a) .def(py::self & py::self) + // For MATLAB compatibility. + //.def_static("inter", [](const Wall& x, const Wall& y) { return x&y; }) + // For MATLAB compatibility. + .def("inter", [](const Wall& s, const Wall& y) { return s&y; }) .def_readwrite("c1", &Wall::c1) .def_readwrite("c2", &Wall::c2) ; From e2236f1540b54c25c11330d416fa86d3dd707b72 Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Mon, 26 Feb 2024 15:21:18 +0100 Subject: [PATCH 183/256] Update MATLAB documentation --- doc/doc/dev/info_dev.rst | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/doc/doc/dev/info_dev.rst b/doc/doc/dev/info_dev.rst index 0de057c8d..1101add57 100644 --- a/doc/doc/dev/info_dev.rst +++ b/doc/doc/dev/info_dev.rst @@ -230,4 +230,12 @@ In the :file:`codac` directory, test the Ubuntu configuration locally using Dock .. code-block:: bash chmod a+x scripts/docker/build_pybinding.sh - docker run --rm -v `pwd`:/io lebarsfa/manylinux2010_x86_64-for-codac /io/scripts/docker/build_pybinding.sh \ No newline at end of file + docker run --rm -v `pwd`:/io lebarsfa/manylinux2010_x86_64-for-codac /io/scripts/docker/build_pybinding.sh + +.. rubric:: MATLAB compatibility + +Some operators or special functions need special attention: + +* Square bracket operator, see e.g. ``getitem`` (``__setitem__``) and ``setitem`` (``__getitem__``) in :commit:`65784a201dda9841194fb581e689247adab07ef7`. +* ``inter`` (``py::self & py::self``, ``__and__``, ``__rand__``), ``union`` (``py::self | py::self``, ``__or__``, ``__ror__``), ``inter_self`` (``py::self &= py::self``, ``__iand__``), ``union_self`` (``py::self |= py::self``, ``__ior__``), ``abs`` (``__abs__``), ``pow`` (``__pow__``), ``get_item`` (``__get_item__``), ``hash`` (``__hash__``) in :commit:`54966a6be0ddd6fafe64bd90fe6401822ba49587` and :commit:`a4500627cafd21a45b1d15bcb37352746482d33a` (similarly: :commit:`50b138694a2f89bca1b7d6dbff190ac364082e68`). +* See also the bottom of https://fr.mathworks.com/help/matlab/matlab_external/differences-between-matlab-python.html and `Start a MATLAB project <../install/04-start-matlab-project.html>`_. From 9501d3cf9a9de9817b0ebe5b990cbed4d9de0fab Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Mon, 26 Feb 2024 18:45:42 +0100 Subject: [PATCH 184/256] manylinux2010 replaced by manylinux2014 --- .github/workflows/dockercentos.yml | 2 +- doc/doc/dev/info_dev.rst | 2 +- scripts/docker/build_pybinding.sh | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/dockercentos.yml b/.github/workflows/dockercentos.yml index ff756faad..7b4d5ee08 100644 --- a/.github/workflows/dockercentos.yml +++ b/.github/workflows/dockercentos.yml @@ -17,7 +17,7 @@ jobs: clean: false - run: | chmod a+x scripts/docker/build_pybinding.sh - docker run --rm -v `pwd`:/io lebarsfa/manylinux2010_x86_64-for-codac /io/scripts/docker/build_pybinding.sh + docker run --rm -v `pwd`:/io lebarsfa/manylinux2014_x86_64-for-codac /io/scripts/docker/build_pybinding.sh ls wheelhouse - uses: xresloader/upload-to-github-release@v1 env: diff --git a/doc/doc/dev/info_dev.rst b/doc/doc/dev/info_dev.rst index 0de057c8d..e3b5bc338 100644 --- a/doc/doc/dev/info_dev.rst +++ b/doc/doc/dev/info_dev.rst @@ -230,4 +230,4 @@ In the :file:`codac` directory, test the Ubuntu configuration locally using Dock .. code-block:: bash chmod a+x scripts/docker/build_pybinding.sh - docker run --rm -v `pwd`:/io lebarsfa/manylinux2010_x86_64-for-codac /io/scripts/docker/build_pybinding.sh \ No newline at end of file + docker run --rm -v `pwd`:/io lebarsfa/manylinux2014_x86_64-for-codac /io/scripts/docker/build_pybinding.sh \ No newline at end of file diff --git a/scripts/docker/build_pybinding.sh b/scripts/docker/build_pybinding.sh index 46ef0236a..6928666de 100755 --- a/scripts/docker/build_pybinding.sh +++ b/scripts/docker/build_pybinding.sh @@ -2,9 +2,9 @@ set -e -x -wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20240224/ibex_x86_64_manylinux2010.zip --no-check-certificate -nv -unzip -q ibex_x86_64_manylinux2010.zip -rm -Rf ibex_x86_64_manylinux2010.zip +wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20240224/ibex_x86_64_manylinux2014.zip --no-check-certificate -nv +unzip -q ibex_x86_64_manylinux2014.zip +rm -Rf ibex_x86_64_manylinux2014.zip sudo cp -Rf ibex/* /usr/local/ git config --global --add safe.directory /io From caf1b909538920e255b5520695228c00f2e8c6c6 Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Mon, 26 Feb 2024 18:46:26 +0100 Subject: [PATCH 185/256] Revert "Workaround for error "NumPy requires GCC >= 8.4" in CentOS docker" This reverts commit ee082308ac4cda3e5e7b69aab65136cbe15bf21c. --- scripts/docker/build_pybinding.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/docker/build_pybinding.sh b/scripts/docker/build_pybinding.sh index 6928666de..a66733d34 100755 --- a/scripts/docker/build_pybinding.sh +++ b/scripts/docker/build_pybinding.sh @@ -30,7 +30,7 @@ for PYBIN in /opt/python/cp3*/bin; do auditwheel repair "$whl" -w /io/wheelhouse/ done - "${PYBIN}/python" -m pip install "numpy<1.23" # Recent versions of numpy require GCC >= 8.4... + "${PYBIN}/python" -m pip install numpy "${PYBIN}/python" -m pip install codac --no-deps --no-index -f /io/wheelhouse (cd "$HOME"; "${PYBIN}/python" -m unittest discover codac.tests) cd /io From 194204b430bc078f94b3ad4ba7a12f8f2af0a4ef Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Tue, 27 Feb 2024 20:19:03 +0100 Subject: [PATCH 186/256] Update workflows --- .github/workflows/unixmatrix.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/unixmatrix.yml b/.github/workflows/unixmatrix.yml index 92654c55b..9cddaed69 100644 --- a/.github/workflows/unixmatrix.yml +++ b/.github/workflows/unixmatrix.yml @@ -81,7 +81,6 @@ jobs: wget https://www.ensta-bretagne.fr/packages/choco/patch.2.5.9.nupkg --no-check-certificate -nv choco install -y -r --no-progress -s . winflexbison patch ${{ matrix.cfg.choco_flags }} del /f /q winflexbison.2.4.9.20170215.nupkg patch.2.5.9.nupkg - 7z x windows_server_core_prereq.zip -o"%SystemRoot%" -y & cd. & rem ksuser.dll already on windows-2016...? wget http://www.ensta-bretagne.fr/lebars/Share/cmake_extra_tools.zip --no-check-certificate -nv 7z x cmake_extra_tools.zip -o"%SystemDrive%" -y del /f /q cmake_extra_tools.zip From 3c80b2911486776d4c2751fc6fdebf04dba57de4 Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Mon, 26 Feb 2024 23:13:03 +0100 Subject: [PATCH 187/256] Update macOS workflows --- .github/workflows/macosmatrix.yml | 18 +++++++++--------- .github/workflows/unixmatrix.yml | 14 ++++++++------ 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/.github/workflows/macosmatrix.yml b/.github/workflows/macosmatrix.yml index fec9aca99..cb8600ff6 100644 --- a/.github/workflows/macosmatrix.yml +++ b/.github/workflows/macosmatrix.yml @@ -19,13 +19,13 @@ jobs: fail-fast: false matrix: cfg: - - { os: macos-12, shell: bash, arch: arm64, runtime: monterey, cmake_flags: '-fPIC', trgt: '11.0', cpcfg: '-macosx_11_0_arm64', py_v_maj: 3, py_v_min: 12, desc: 'macOS Monterey Python 3.12 arm64' } - - { os: macos-12, shell: bash, arch: arm64, runtime: monterey, cmake_flags: '-fPIC', trgt: '11.0', cpcfg: '-macosx_11_0_arm64', py_v_maj: 3, py_v_min: 11, desc: 'macOS Monterey Python 3.11 arm64' } - - { os: macos-12, shell: bash, arch: arm64, runtime: monterey, cmake_flags: '-fPIC', trgt: '11.0', cpcfg: '-macosx_11_0_arm64', py_v_maj: 3, py_v_min: 10, desc: 'macOS Monterey Python 3.10 arm64' } - - { os: macos-12, shell: bash, arch: arm64, runtime: monterey, cmake_flags: '-fPIC', trgt: '11.0', cpcfg: '-macosx_11_0_arm64', py_v_maj: 3, py_v_min: 9, desc: 'macOS Monterey Python 3.9 arm64' } - - { os: macos-12, shell: bash, arch: arm64, runtime: monterey, cmake_flags: '-fPIC', trgt: '11.0', cpcfg: '-macosx_11_0_arm64', py_v_maj: 3, py_v_min: 8, desc: 'macOS Monterey Python 3.8 arm64' } - - { os: macos-12, shell: bash, arch: arm64, runtime: monterey, cmake_flags: '-fPIC', trgt: '11.0', cpcfg: '-macosx_11_0_arm64', py_v_maj: 3, py_v_min: 7, desc: 'macOS Monterey Python 3.7 arm64' } - - { os: macos-12, shell: bash, arch: arm64, runtime: monterey, cmake_flags: '-fPIC', trgt: '11.0', cpcfg: '-macosx_11_0_arm64', py_v_maj: 3, py_v_min: 6, desc: 'macOS Monterey Python 3.6 arm64' } + - { os: macos-14, shell: bash, arch: arm64, runtime: sonoma, cmake_flags: '-fPIC', trgt: '11.0', cpcfg: '-macosx_11_0_arm64', py_v_maj: 3, py_v_min: 12, desc: 'macOS Sonoma Python 3.12 arm64' } + - { os: macos-14, shell: bash, arch: arm64, runtime: sonoma, cmake_flags: '-fPIC', trgt: '11.0', cpcfg: '-macosx_11_0_arm64', py_v_maj: 3, py_v_min: 11, desc: 'macOS Sonoma Python 3.11 arm64' } + - { os: macos-12, shell: bash, arch: arm64, runtime: monterey, cmake_flags: '-fPIC', trgt: '11.0', cpcfg: '-macosx_11_0_arm64', py_v_maj: 3, py_v_min: 10, cross: true, desc: 'macOS Monterey Python 3.10 arm64 (cross)' } + - { os: macos-12, shell: bash, arch: arm64, runtime: monterey, cmake_flags: '-fPIC', trgt: '11.0', cpcfg: '-macosx_11_0_arm64', py_v_maj: 3, py_v_min: 9, cross: true, desc: 'macOS Monterey Python 3.9 arm64 (cross)' } + - { os: macos-12, shell: bash, arch: arm64, runtime: monterey, cmake_flags: '-fPIC', trgt: '11.0', cpcfg: '-macosx_11_0_arm64', py_v_maj: 3, py_v_min: 8, cross: true, desc: 'macOS Monterey Python 3.8 arm64 (cross)' } + - { os: macos-12, shell: bash, arch: arm64, runtime: monterey, cmake_flags: '-fPIC', trgt: '11.0', cpcfg: '-macosx_11_0_arm64', py_v_maj: 3, py_v_min: 7, cross: true, desc: 'macOS Monterey Python 3.7 arm64 (cross)' } + - { os: macos-12, shell: bash, arch: arm64, runtime: monterey, cmake_flags: '-fPIC', trgt: '11.0', cpcfg: '-macosx_11_0_arm64', py_v_maj: 3, py_v_min: 6, cross: true, desc: 'macOS Monterey Python 3.6 arm64 (cross)' } - { os: macos-12, shell: bash, arch: x86_64, runtime: monterey, cmake_flags: '-fPIC', trgt: '10.9', cpcfg: '-macosx_10_9_x86_64', py_v_maj: 3, py_v_min: 12, desc: 'macOS Monterey Python 3.12 x86_64' } - { os: macos-12, shell: bash, arch: x86_64, runtime: monterey, cmake_flags: '-fPIC', trgt: '10.9', cpcfg: '-macosx_10_9_x86_64', py_v_maj: 3, py_v_min: 11, desc: 'macOS Monterey Python 3.11 x86_64' } - { os: macos-12, shell: bash, arch: x86_64, runtime: monterey, cmake_flags: '-fPIC', trgt: '10.9', cpcfg: '-macosx_10_9_x86_64', py_v_maj: 3, py_v_min: 10, desc: 'macOS Monterey Python 3.10 x86_64' } @@ -60,7 +60,7 @@ jobs: wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20240224/ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip --no-check-certificate -nv unzip -q ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip rm -Rf ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip - cp -Rf ibex/* /usr/local/ + sudo cp -Rf ibex/* /usr/local/ shell: bash - run: | mkdir build ; cd build @@ -71,7 +71,7 @@ jobs: shell: bash - run: pip install *.whl ; python -c "import sys; print(sys.version)" ; python examples/tuto/01_getting_started/01_getting_started.py ; pip install numpy ; python -m unittest discover codac.tests shell: bash - if: (runner.os=='macOS')&&(matrix.cfg.arch=='x86_64') + if: matrix.cfg.cross!=true - uses: xresloader/upload-to-github-release@v1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/unixmatrix.yml b/.github/workflows/unixmatrix.yml index 9cddaed69..1f8ad0246 100644 --- a/.github/workflows/unixmatrix.yml +++ b/.github/workflows/unixmatrix.yml @@ -46,9 +46,11 @@ jobs: - { os: windows-2019, shell: cmd, arch: x86, bitness: 32, runtime: mingw7, cmake_params: '-G "MinGW Makefiles"', cmake_flags: '-fPIC', choco_flags: '--x86', desc: 'Windows MinGW 7.3.0 x86' } - { os: ubuntu-22.04, shell: bash, arch: amd64, bitness: 64, runtime: jammy, cmake_flags: '-fPIC', desc: 'Ubuntu 22.04' } - { os: ubuntu-20.04, shell: bash, arch: amd64, bitness: 64, runtime: focal, cmake_flags: '-fPIC', desc: 'Ubuntu 20.04' } - - { os: macos-13, shell: bash, arch: arm64, bitness: 64, runtime: ventura, cmake_params: '-D CMAKE_SYSTEM_NAME=Darwin -D CMAKE_OSX_ARCHITECTURES=arm64', cmake_flags: '-fPIC', desc: 'macOS Ventura arm64' } + - { os: macos-14, shell: bash, arch: arm64, bitness: 64, runtime: sonoma, cmake_params: '-D CMAKE_SYSTEM_NAME=Darwin -D CMAKE_OSX_ARCHITECTURES=arm64', cmake_flags: '-fPIC', desc: 'macOS Sonoma arm64' } + - { os: macos-14, shell: bash, arch: x86_64, bitness: 64, runtime: sonoma, cmake_params: '-D CMAKE_SYSTEM_NAME=Darwin -D CMAKE_OSX_ARCHITECTURES=x86_64', cmake_flags: '-fPIC', cross: true, desc: 'macOS Sonoma x86_64 (cross)' } + - { os: macos-13, shell: bash, arch: arm64, bitness: 64, runtime: ventura, cmake_params: '-D CMAKE_SYSTEM_NAME=Darwin -D CMAKE_OSX_ARCHITECTURES=arm64', cmake_flags: '-fPIC', cross: true, desc: 'macOS Ventura arm64 (cross)' } - { os: macos-13, shell: bash, arch: x86_64, bitness: 64, runtime: ventura, cmake_params: '-D CMAKE_SYSTEM_NAME=Darwin -D CMAKE_OSX_ARCHITECTURES=x86_64', cmake_flags: '-fPIC', desc: 'macOS Ventura x86_64' } - - { os: macos-12, shell: bash, arch: arm64, bitness: 64, runtime: monterey, cmake_params: '-D CMAKE_SYSTEM_NAME=Darwin -D CMAKE_OSX_ARCHITECTURES=arm64', cmake_flags: '-fPIC', desc: 'macOS Monterey arm64' } + - { os: macos-12, shell: bash, arch: arm64, bitness: 64, runtime: monterey, cmake_params: '-D CMAKE_SYSTEM_NAME=Darwin -D CMAKE_OSX_ARCHITECTURES=arm64', cmake_flags: '-fPIC', cross: true, desc: 'macOS Monterey arm64 (cross)' } - { os: macos-12, shell: bash, arch: x86_64, bitness: 64, runtime: monterey, cmake_params: '-D CMAKE_SYSTEM_NAME=Darwin -D CMAKE_OSX_ARCHITECTURES=x86_64', cmake_flags: '-fPIC', desc: 'macOS Monterey x86_64' } name: ${{ matrix.cfg.desc }} steps: @@ -166,7 +168,7 @@ jobs: ./${{ matrix.cfg.test_config }}my_project cd ../.. shell: bash - if: (runner.os!='macOS')||(matrix.cfg.arch=='x86_64') + if: matrix.cfg.cross!=true - run: | if [ ${{ runner.os }} = Windows ]; then source ~/refreshenv.bashrc ; refreshenv ; export PATH=$BASHMINGWPATH:$PATH ; fi cd packages/choco @@ -197,9 +199,9 @@ jobs: shell: bash if: runner.os=='Linux' - run: | - cp -Rf codac/* /usr/local/ + sudo cp -Rf codac/* /usr/local/ shell: bash - if: (runner.os=='macOS')&&(matrix.cfg.arch=='x86_64') + if: runner.os=='macOS' - uses: xresloader/upload-to-github-release@v1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -218,4 +220,4 @@ jobs: ./${{ matrix.cfg.test_config }}my_project cd ../.. shell: bash - if: (runner.os!='macOS')||(matrix.cfg.arch=='x86_64') + if: matrix.cfg.cross!=true From 2e2dda361d4567dd4bbe443cae2559a7b7e0ba7d Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Thu, 29 Feb 2024 11:17:06 +0100 Subject: [PATCH 188/256] Minor correction in macOS wheels workflows --- .github/workflows/macosmatrix.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/macosmatrix.yml b/.github/workflows/macosmatrix.yml index cb8600ff6..5729a46c6 100644 --- a/.github/workflows/macosmatrix.yml +++ b/.github/workflows/macosmatrix.yml @@ -24,15 +24,15 @@ jobs: - { os: macos-12, shell: bash, arch: arm64, runtime: monterey, cmake_flags: '-fPIC', trgt: '11.0', cpcfg: '-macosx_11_0_arm64', py_v_maj: 3, py_v_min: 10, cross: true, desc: 'macOS Monterey Python 3.10 arm64 (cross)' } - { os: macos-12, shell: bash, arch: arm64, runtime: monterey, cmake_flags: '-fPIC', trgt: '11.0', cpcfg: '-macosx_11_0_arm64', py_v_maj: 3, py_v_min: 9, cross: true, desc: 'macOS Monterey Python 3.9 arm64 (cross)' } - { os: macos-12, shell: bash, arch: arm64, runtime: monterey, cmake_flags: '-fPIC', trgt: '11.0', cpcfg: '-macosx_11_0_arm64', py_v_maj: 3, py_v_min: 8, cross: true, desc: 'macOS Monterey Python 3.8 arm64 (cross)' } - - { os: macos-12, shell: bash, arch: arm64, runtime: monterey, cmake_flags: '-fPIC', trgt: '11.0', cpcfg: '-macosx_11_0_arm64', py_v_maj: 3, py_v_min: 7, cross: true, desc: 'macOS Monterey Python 3.7 arm64 (cross)' } - - { os: macos-12, shell: bash, arch: arm64, runtime: monterey, cmake_flags: '-fPIC', trgt: '11.0', cpcfg: '-macosx_11_0_arm64', py_v_maj: 3, py_v_min: 6, cross: true, desc: 'macOS Monterey Python 3.6 arm64 (cross)' } + - { os: macos-12, shell: bash, arch: arm64, runtime: monterey, cmake_flags: '-fPIC', trgt: '11.0', cpcfg: 'm-macosx_11_0_arm64', py_v_maj: 3, py_v_min: 7, cross: true, desc: 'macOS Monterey Python 3.7 arm64 (cross)' } + - { os: macos-12, shell: bash, arch: arm64, runtime: monterey, cmake_flags: '-fPIC', trgt: '11.0', cpcfg: 'm-macosx_11_0_arm64', py_v_maj: 3, py_v_min: 6, cross: true, desc: 'macOS Monterey Python 3.6 arm64 (cross)' } - { os: macos-12, shell: bash, arch: x86_64, runtime: monterey, cmake_flags: '-fPIC', trgt: '10.9', cpcfg: '-macosx_10_9_x86_64', py_v_maj: 3, py_v_min: 12, desc: 'macOS Monterey Python 3.12 x86_64' } - { os: macos-12, shell: bash, arch: x86_64, runtime: monterey, cmake_flags: '-fPIC', trgt: '10.9', cpcfg: '-macosx_10_9_x86_64', py_v_maj: 3, py_v_min: 11, desc: 'macOS Monterey Python 3.11 x86_64' } - { os: macos-12, shell: bash, arch: x86_64, runtime: monterey, cmake_flags: '-fPIC', trgt: '10.9', cpcfg: '-macosx_10_9_x86_64', py_v_maj: 3, py_v_min: 10, desc: 'macOS Monterey Python 3.10 x86_64' } - { os: macos-12, shell: bash, arch: x86_64, runtime: monterey, cmake_flags: '-fPIC', trgt: '10.9', cpcfg: '-macosx_10_9_x86_64', py_v_maj: 3, py_v_min: 9, desc: 'macOS Monterey Python 3.9 x86_64' } - { os: macos-12, shell: bash, arch: x86_64, runtime: monterey, cmake_flags: '-fPIC', trgt: '10.9', cpcfg: '-macosx_10_9_x86_64', py_v_maj: 3, py_v_min: 8, desc: 'macOS Monterey Python 3.8 x86_64' } - - { os: macos-12, shell: bash, arch: x86_64, runtime: monterey, cmake_flags: '-fPIC', trgt: '10.9', cpcfg: '-macosx_10_9_x86_64', py_v_maj: 3, py_v_min: 7, desc: 'macOS Monterey Python 3.7 x86_64' } - - { os: macos-12, shell: bash, arch: x86_64, runtime: monterey, cmake_flags: '-fPIC', trgt: '10.14', cpcfg: '-macosx_10_14_x86_64', py_v_maj: 3, py_v_min: 6, desc: 'macOS Monterey Python 3.6 x86_64' } # 10.14 because of error $MACOSX_DEPLOYMENT_TARGET mismatch: now "10.9" but "10.14" during configure. + - { os: macos-12, shell: bash, arch: x86_64, runtime: monterey, cmake_flags: '-fPIC', trgt: '10.9', cpcfg: 'm-macosx_10_9_x86_64', py_v_maj: 3, py_v_min: 7, desc: 'macOS Monterey Python 3.7 x86_64' } + - { os: macos-12, shell: bash, arch: x86_64, runtime: monterey, cmake_flags: '-fPIC', trgt: '10.14', cpcfg: 'm-macosx_10_14_x86_64', py_v_maj: 3, py_v_min: 6, desc: 'macOS Monterey Python 3.6 x86_64' } # 10.14 because of error $MACOSX_DEPLOYMENT_TARGET mismatch: now "10.9" but "10.14" during configure. name: ${{ matrix.cfg.desc }} steps: - uses: actions/checkout@v4 From 1388b4dc592bd201a667c54db75217eb00bba430 Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Thu, 29 Feb 2024 11:29:22 +0100 Subject: [PATCH 189/256] Add scripts to help downloading all Python wheels from the latest release --- scripts/wheels/gencodacwhl.sh | 45 +++++++++++++++++++++++++++++ scripts/wheels/getlatestcodacwhl.sh | 14 +++++++++ 2 files changed, 59 insertions(+) create mode 100644 scripts/wheels/gencodacwhl.sh create mode 100644 scripts/wheels/getlatestcodacwhl.sh diff --git a/scripts/wheels/gencodacwhl.sh b/scripts/wheels/gencodacwhl.sh new file mode 100644 index 000000000..f6384f93e --- /dev/null +++ b/scripts/wheels/gencodacwhl.sh @@ -0,0 +1,45 @@ +#!/bin/bash + +read -p "Please enter the codac package version (X.Y.Z[.D]) : " PACKAGE_VERSION + +CPCFG=m-macosx_10_9_x86_64 PY_V_MAJ=3 PY_V_MIN=6 ; rm -Rf codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl ; wget https://github.com/codac-team/codac/releases/download/v$PACKAGE_VERSION/codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl --no-check-certificate -nv +CPCFG=m-macosx_11_0_arm64 PY_V_MAJ=3 PY_V_MIN=6 ; rm -Rf codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl ; wget https://github.com/codac-team/codac/releases/download/v$PACKAGE_VERSION/codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl --no-check-certificate -nv +CPCFG=m-manylinux_2_17_x86_64.manylinux2014_x86_64 PY_V_MAJ=3 PY_V_MIN=6 ; rm -Rf codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl ; wget https://github.com/codac-team/codac/releases/download/v$PACKAGE_VERSION/codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl --no-check-certificate -nv +CPCFG=m-win32 PY_V_MAJ=3 PY_V_MIN=6 ; rm -Rf codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl ; wget https://github.com/codac-team/codac/releases/download/v$PACKAGE_VERSION/codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl --no-check-certificate -nv +CPCFG=m-win_amd64 PY_V_MAJ=3 PY_V_MIN=6 ; rm -Rf codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl ; wget https://github.com/codac-team/codac/releases/download/v$PACKAGE_VERSION/codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl --no-check-certificate -nv + +CPCFG=m-macosx_10_9_x86_64 PY_V_MAJ=3 PY_V_MIN=7 ; rm -Rf codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl ; wget https://github.com/codac-team/codac/releases/download/v$PACKAGE_VERSION/codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl --no-check-certificate -nv +CPCFG=m-macosx_11_0_arm64 PY_V_MAJ=3 PY_V_MIN=7 ; rm -Rf codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl ; wget https://github.com/codac-team/codac/releases/download/v$PACKAGE_VERSION/codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl --no-check-certificate -nv +CPCFG=m-manylinux_2_17_x86_64.manylinux2014_x86_64 PY_V_MAJ=3 PY_V_MIN=7 ; rm -Rf codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl ; wget https://github.com/codac-team/codac/releases/download/v$PACKAGE_VERSION/codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl --no-check-certificate -nv +CPCFG=m-win32 PY_V_MAJ=3 PY_V_MIN=7 ; rm -Rf codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl ; wget https://github.com/codac-team/codac/releases/download/v$PACKAGE_VERSION/codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl --no-check-certificate -nv +CPCFG=m-win_amd64 PY_V_MAJ=3 PY_V_MIN=7 ; rm -Rf codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl ; wget https://github.com/codac-team/codac/releases/download/v$PACKAGE_VERSION/codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl --no-check-certificate -nv + +CPCFG=-macosx_10_9_x86_64 PY_V_MAJ=3 PY_V_MIN=8 ; rm -Rf codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl ; wget https://github.com/codac-team/codac/releases/download/v$PACKAGE_VERSION/codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl --no-check-certificate -nv +CPCFG=-macosx_11_0_arm64 PY_V_MAJ=3 PY_V_MIN=8 ; rm -Rf codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl ; wget https://github.com/codac-team/codac/releases/download/v$PACKAGE_VERSION/codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl --no-check-certificate -nv +CPCFG=-manylinux_2_17_x86_64.manylinux2014_x86_64 PY_V_MAJ=3 PY_V_MIN=8 ; rm -Rf codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl ; wget https://github.com/codac-team/codac/releases/download/v$PACKAGE_VERSION/codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl --no-check-certificate -nv +CPCFG=-win32 PY_V_MAJ=3 PY_V_MIN=8 ; rm -Rf codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl ; wget https://github.com/codac-team/codac/releases/download/v$PACKAGE_VERSION/codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl --no-check-certificate -nv +CPCFG=-win_amd64 PY_V_MAJ=3 PY_V_MIN=8 ; rm -Rf codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl ; wget https://github.com/codac-team/codac/releases/download/v$PACKAGE_VERSION/codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl --no-check-certificate -nv + +CPCFG=-macosx_10_9_x86_64 PY_V_MAJ=3 PY_V_MIN=9 ; rm -Rf codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl ; wget https://github.com/codac-team/codac/releases/download/v$PACKAGE_VERSION/codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl --no-check-certificate -nv +CPCFG=-macosx_11_0_arm64 PY_V_MAJ=3 PY_V_MIN=9 ; rm -Rf codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl ; wget https://github.com/codac-team/codac/releases/download/v$PACKAGE_VERSION/codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl --no-check-certificate -nv +CPCFG=-manylinux_2_17_x86_64.manylinux2014_x86_64 PY_V_MAJ=3 PY_V_MIN=9 ; rm -Rf codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl ; wget https://github.com/codac-team/codac/releases/download/v$PACKAGE_VERSION/codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl --no-check-certificate -nv +CPCFG=-win32 PY_V_MAJ=3 PY_V_MIN=9 ; rm -Rf codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl ; wget https://github.com/codac-team/codac/releases/download/v$PACKAGE_VERSION/codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl --no-check-certificate -nv +CPCFG=-win_amd64 PY_V_MAJ=3 PY_V_MIN=9 ; rm -Rf codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl ; wget https://github.com/codac-team/codac/releases/download/v$PACKAGE_VERSION/codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl --no-check-certificate -nv + +CPCFG=-macosx_10_9_x86_64 PY_V_MAJ=3 PY_V_MIN=10 ; rm -Rf codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl ; wget https://github.com/codac-team/codac/releases/download/v$PACKAGE_VERSION/codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl --no-check-certificate -nv +CPCFG=-macosx_11_0_arm64 PY_V_MAJ=3 PY_V_MIN=10 ; rm -Rf codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl ; wget https://github.com/codac-team/codac/releases/download/v$PACKAGE_VERSION/codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl --no-check-certificate -nv +CPCFG=-manylinux_2_17_x86_64.manylinux2014_x86_64 PY_V_MAJ=3 PY_V_MIN=10 ; rm -Rf codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl ; wget https://github.com/codac-team/codac/releases/download/v$PACKAGE_VERSION/codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl --no-check-certificate -nv +CPCFG=-win32 PY_V_MAJ=3 PY_V_MIN=10 ; rm -Rf codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl ; wget https://github.com/codac-team/codac/releases/download/v$PACKAGE_VERSION/codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl --no-check-certificate -nv +CPCFG=-win_amd64 PY_V_MAJ=3 PY_V_MIN=10 ; rm -Rf codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl ; wget https://github.com/codac-team/codac/releases/download/v$PACKAGE_VERSION/codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl --no-check-certificate -nv + +CPCFG=-macosx_10_9_x86_64 PY_V_MAJ=3 PY_V_MIN=12 ; rm -Rf codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl ; wget https://github.com/codac-team/codac/releases/download/v$PACKAGE_VERSION/codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl --no-check-certificate -nv +CPCFG=-macosx_11_0_arm64 PY_V_MAJ=3 PY_V_MIN=12 ; rm -Rf codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl ; wget https://github.com/codac-team/codac/releases/download/v$PACKAGE_VERSION/codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl --no-check-certificate -nv +CPCFG=-manylinux_2_17_x86_64.manylinux2014_x86_64 PY_V_MAJ=3 PY_V_MIN=12 ; rm -Rf codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl ; wget https://github.com/codac-team/codac/releases/download/v$PACKAGE_VERSION/codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl --no-check-certificate -nv +CPCFG=-win32 PY_V_MAJ=3 PY_V_MIN=12 ; rm -Rf codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl ; wget https://github.com/codac-team/codac/releases/download/v$PACKAGE_VERSION/codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl --no-check-certificate -nv +CPCFG=-win_amd64 PY_V_MAJ=3 PY_V_MIN=12 ; rm -Rf codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl ; wget https://github.com/codac-team/codac/releases/download/v$PACKAGE_VERSION/codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl --no-check-certificate -nv + +CPCFG=-macosx_10_9_x86_64 PY_V_MAJ=3 PY_V_MIN=11 ; rm -Rf codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl ; wget https://github.com/codac-team/codac/releases/download/v$PACKAGE_VERSION/codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl --no-check-certificate -nv +CPCFG=-macosx_11_0_arm64 PY_V_MAJ=3 PY_V_MIN=11 ; rm -Rf codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl ; wget https://github.com/codac-team/codac/releases/download/v$PACKAGE_VERSION/codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl --no-check-certificate -nv +CPCFG=-manylinux_2_17_x86_64.manylinux2014_x86_64 PY_V_MAJ=3 PY_V_MIN=11 ; rm -Rf codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl ; wget https://github.com/codac-team/codac/releases/download/v$PACKAGE_VERSION/codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl --no-check-certificate -nv +CPCFG=-win32 PY_V_MAJ=3 PY_V_MIN=11 ; rm -Rf codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl ; wget https://github.com/codac-team/codac/releases/download/v$PACKAGE_VERSION/codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl --no-check-certificate -nv +CPCFG=-win_amd64 PY_V_MAJ=3 PY_V_MIN=11 ; rm -Rf codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl ; wget https://github.com/codac-team/codac/releases/download/v$PACKAGE_VERSION/codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl --no-check-certificate -nv diff --git a/scripts/wheels/getlatestcodacwhl.sh b/scripts/wheels/getlatestcodacwhl.sh new file mode 100644 index 000000000..e4538944e --- /dev/null +++ b/scripts/wheels/getlatestcodacwhl.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +export LATEST_TAG=`curl -IkLs -o /dev/null -w %{url_effective} "https://github.com/codac-team/codac/releases/latest" | sed 's#.*tag/\(.*\).*#\1#'` +export LATEST_VER="${LATEST_TAG:1}" + +export LK_VER=`cat lk_codac_whl.ver` + +echo Latest: $LATEST_VER +echo Last known: $LK_VER + +if [[ -z "$LK_VER" || $(printf "%s\n%s" "$LATEST_VER" "$LK_VER" | sort -V | head -n 1) != "$LATEST_VER" && "$LATEST_VER" != "$LK_VER" ]] then + echo $LATEST_VER is newer than $LK_VER + echo $LATEST_VER>lk_codac_whl.ver && cat lk_codac_whl.ver | ./gencodacwhl.sh +fi From 2acc5f5fd89417284ceea7efe0d23f34487959e8 Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Thu, 29 Feb 2024 11:51:29 +0100 Subject: [PATCH 190/256] Update Python wheels upload documentation --- doc/doc/dev/info_dev.rst | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/doc/doc/dev/info_dev.rst b/doc/doc/dev/info_dev.rst index 06c272ce4..eecd2eb68 100644 --- a/doc/doc/dev/info_dev.rst +++ b/doc/doc/dev/info_dev.rst @@ -210,12 +210,12 @@ Tag the current version: git tag -a v3.0.0-beta1 git push origin v3.0.0-beta1 -Create the *wheels* with a Docker: +Get all the *wheels* generated by GitHub Actions (the generated release needs to be explicitely set as latest): .. code-block:: bash - docker pull benensta/pyibex-docker - docker run --rm -v `pwd`:/io benensta/pyibex-docker /io/scripts/docker/build_pybinding.sh + cd scripts/wheels + ./getlatestcodacwhl.sh Upload the *wheels* on PyPi: @@ -223,13 +223,14 @@ Upload the *wheels* on PyPi: python3 -m twine upload --repository pypi * -.. rubric:: Testing with Docker +.. rubric:: Testing the Linux *wheels* with Docker -In the :file:`codac` directory, test the Ubuntu configuration locally using Docker: +In the :file:`codac` directory, test the Linux configuration locally using Docker: .. code-block:: bash chmod a+x scripts/docker/build_pybinding.sh + docker pull lebarsfa/manylinux2014_x86_64-for-codac docker run --rm -v `pwd`:/io lebarsfa/manylinux2014_x86_64-for-codac /io/scripts/docker/build_pybinding.sh .. rubric:: MATLAB compatibility From c42ff66c6fa49b4053d8f1d761e227cdc0988825 Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Wed, 6 Mar 2024 23:38:11 +0100 Subject: [PATCH 191/256] Documentation updates --- doc/doc/install/01-installation-full-linux.rst | 2 +- doc/doc/install/01-installation-full-macos.rst | 17 +++++++++++++---- .../install/01-installation-full-windows.rst | 17 +++++++---------- doc/doc/install/02-start-py-project.rst | 3 +-- doc/doc/install/03-start-cpp-project.rst | 3 +-- doc/doc/install/04-start-matlab-project.rst | 7 +++++++ 6 files changed, 30 insertions(+), 19 deletions(-) diff --git a/doc/doc/install/01-installation-full-linux.rst b/doc/doc/install/01-installation-full-linux.rst index 88375c110..d69113f5a 100644 --- a/doc/doc/install/01-installation-full-linux.rst +++ b/doc/doc/install/01-installation-full-linux.rst @@ -27,7 +27,7 @@ Then, check your installation `with the instructions of this page <03-start-cpp- .. note:: - For a Raspberry Pi running Raspbian Buster, download and extract ``codac_standalone_armv6hf_buster.zip`` from ``_, then in the ``example`` folder run: + For a Raspberry Pi running Raspbian Buster 32 bit, download and extract ``codac_standalone_armv6hf_buster.zip`` from ``_, then in the ``example`` folder run: .. code-block:: bash diff --git a/doc/doc/install/01-installation-full-macos.rst b/doc/doc/install/01-installation-full-macos.rst index eb479e55a..c0ca9f64c 100644 --- a/doc/doc/install/01-installation-full-macos.rst +++ b/doc/doc/install/01-installation-full-macos.rst @@ -10,7 +10,7 @@ Installing Codac on macOS for C++ use Quick start ----------- -Install Homebrew package manager and build tools: +Install `Homebrew package manager `_ and build tools: .. code-block:: bash @@ -18,7 +18,7 @@ Install Homebrew package manager and build tools: brew install wget autoconf automake libtool brew install --cask cmake -Download and extract *e.g.* ``codac_standalone_x86_64_bigsur.zip`` (for macOS 11 Big Sur (x86_64)) from ``_, then in ``example`` folder run: +Download and extract *e.g.* ``codac_standalone_arm64_monterey.zip`` (for macOS 12 Monterey on a Mac with Apple silicon (arm64 processor), use ``codac_standalone_x86_64_monterey.zip`` for a Mac with an Intel processor (x86_64 processor), see https://support.apple.com/en-us/116943) from ``_, then in ``example`` folder run: .. code-block:: bash @@ -26,16 +26,25 @@ Download and extract *e.g.* ``codac_standalone_x86_64_bigsur.zip`` (for macOS 11 and check that "My first tube:Tube [0, 10]" appears. -Optionally, download and run ``_ (see also https://support.apple.com/HT211861) before running the project, and check that a tube appears in VIBes window. +Optionally, download and run ``_ (see also https://support.apple.com/HT211861) before running the project, and check that a tube appears in :ref:`VIBes viewer `. Building from sources --------------------- -You will probably need to install those prerequisites (assuming you installed `Homebrew package manager `_): +You will probably need to install those prerequisites: .. code-block:: bash brew install eigen +Optionally, for Python and documentation: + +.. code-block:: bash + + wget https://github.com/Homebrew/homebrew-core/raw/d2267b9f2ad247bc9c8273eb755b39566a474a70/Formula/doxygen.rb ; brew reinstall ./doxygen.rb ; brew pin doxygen + brew install graphviz + python -m pip install --upgrade pip + pip install --upgrade wheel setuptools sphinx breathe sphinx-issues sphinx-tabs sphinx_rtd_theme sphinx-reredirects + The logic to follow will then be similar to `Linux <01-installation-full-linux.html>`_. See also `Information for developers `_. diff --git a/doc/doc/install/01-installation-full-windows.rst b/doc/doc/install/01-installation-full-windows.rst index 2771e8610..d52a27757 100644 --- a/doc/doc/install/01-installation-full-windows.rst +++ b/doc/doc/install/01-installation-full-windows.rst @@ -17,9 +17,9 @@ Check https://community.chocolatey.org/packages/codac. .. rubric:: Using Visual Studio -Download and extract *e.g.* ``codac_standalone_x64_vc16.zip`` (for Visual Studio 2019) from ``_, open ``example\CMakelists.txt``, choose ``x64-Release`` configuration in Visual Studio (instead of ``x64-Debug``), double-click on ``main.cpp`` in the Solution Explorer and then click on the green Start button, finally check that "My first tube:Tube [0, 10]" appears. +Download and extract *e.g.* ``codac_standalone_x64_vc17.zip`` (for Visual Studio 2022) from ``_, open ``example\CMakelists.txt``, choose ``x64-Release`` configuration in Visual Studio (instead of ``x64-Debug``), double-click on ``main.cpp`` in the Solution Explorer and then click on the green Start button, finally check that "My first tube:Tube [0, 10]" appears. -Optionally, download and run ``_ before running the project, and check that a tube appears in VIBes window. +Optionally, download and run ``_ before running the project, and check that a tube appears in :ref:`VIBes viewer `. Building from sources @@ -32,19 +32,16 @@ You will probably need to install these prerequisites (assuming you installed `C choco install cmake git make patch winflexbison choco install eigen -Then, install the desired compiler (*e.g.* ``choco install mingw --version=8.1.0``). +Then, install the desired compiler (*e.g.* ``choco install mingw --version=11.2.0.07112021``). -Optionally, for Python (*e.g.* ``choco install python --version=3.8.2``) and documentation: +Optionally, for Python (*e.g.* ``choco install python --version=3.10.4``) and documentation: .. code-block:: bat - choco install doxygen.install graphviz + choco install doxygen.install --version=1.9.6 + choco install graphviz python -m pip install --upgrade pip - pip install --upgrade wheel setuptools - git clone --depth 1 -b v3.1.1 https://github.com/sphinx-doc/sphinx - cd sphinx - pip install . - pip install --upgrade breathe sphinx-issues sphinx-tabs sphinx_rtd_theme sphinx-reredirects + pip install --upgrade wheel setuptools sphinx breathe sphinx-issues sphinx-tabs sphinx_rtd_theme sphinx-reredirects The logic to follow will then be similar to `Linux <01-installation-full-linux.html>`_ (note that for Visual Studio, commands such as ``make install`` need to be replaced with something similar to: diff --git a/doc/doc/install/02-start-py-project.rst b/doc/doc/install/02-start-py-project.rst index 5a40f330b..7e9ee6c15 100644 --- a/doc/doc/install/02-start-py-project.rst +++ b/doc/doc/install/02-start-py-project.rst @@ -8,8 +8,7 @@ Start a Python project | You are using C++? | :ref:`sec-start-cpp-project` -| Codac is ready to be used on your computer. -| You can now import the ``codac`` package and start using it: +| Assuming :ref:`Codac Python package is installed `, you can import the ``codac`` package and start using it: .. code-block:: py diff --git a/doc/doc/install/03-start-cpp-project.rst b/doc/doc/install/03-start-cpp-project.rst index 49f1b5335..335b9c3da 100644 --- a/doc/doc/install/03-start-cpp-project.rst +++ b/doc/doc/install/03-start-cpp-project.rst @@ -8,8 +8,7 @@ Start a C++ project | You are using Python? | :ref:`sec-start-py-project` -| Codac is ready to be used on your computer. -| You can now copy-paste the following example code in a file named :file:`main.cpp`: +| Assuming :ref:`Codac library is installed `, you can copy-paste the following example code in a file named :file:`main.cpp`: .. code-block:: c++ diff --git a/doc/doc/install/04-start-matlab-project.rst b/doc/doc/install/04-start-matlab-project.rst index b69699ea5..18dfc823c 100644 --- a/doc/doc/install/04-start-matlab-project.rst +++ b/doc/doc/install/04-start-matlab-project.rst @@ -47,3 +47,10 @@ In order to visualize the tube, you need to launch before the :ref:`VIBes viewer If everything is well installed on your computer, you should see the following window appear: .. Figure:: img/helloworld.png + +.. warning:: + + Octave support is more limited than MATLAB at the moment (and only briefly tested on Ubuntu 22.04 for now). `Pythonic `_ needs to be installed to be able to call Python code from Octave. It has currently some limitations (see https://wiki.octave.org/Pythonic) such as: + + * Missing `import` command. https://gist.github.com/lebarsfa/ebc19b143df77753842f920b4b680b84#file-import-m can be loaded in Octave as a workaround. + * Operators such as `+`, `-`, `*`, `/`, `==`, `!=`, etc. do not seem overloaded (see https://gitlab.com/gnu-octave/octave-pythonic/-/issues/129, note that these operators are supported by MATLAB, but some others might not be supported by MATLAB either, see the bottom of https://fr.mathworks.com/help/matlab/matlab_external/differences-between-matlab-python.html). The internal methods `__add__()`, `__sub__()`, `__neg__()`, `__mul__()`, `__truediv__()`, `__eq__()`, `__ne__()`, etc. can be used instead. From d4f62e004c33dcab47530e8c0d5627ef3c32c276 Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Thu, 7 Mar 2024 22:10:52 +0100 Subject: [PATCH 192/256] Update gencodacwhl.sh --- scripts/wheels/gencodacwhl.sh | 50 +++++++++-------------------------- 1 file changed, 13 insertions(+), 37 deletions(-) diff --git a/scripts/wheels/gencodacwhl.sh b/scripts/wheels/gencodacwhl.sh index f6384f93e..14ab8af57 100644 --- a/scripts/wheels/gencodacwhl.sh +++ b/scripts/wheels/gencodacwhl.sh @@ -2,44 +2,20 @@ read -p "Please enter the codac package version (X.Y.Z[.D]) : " PACKAGE_VERSION -CPCFG=m-macosx_10_9_x86_64 PY_V_MAJ=3 PY_V_MIN=6 ; rm -Rf codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl ; wget https://github.com/codac-team/codac/releases/download/v$PACKAGE_VERSION/codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl --no-check-certificate -nv -CPCFG=m-macosx_11_0_arm64 PY_V_MAJ=3 PY_V_MIN=6 ; rm -Rf codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl ; wget https://github.com/codac-team/codac/releases/download/v$PACKAGE_VERSION/codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl --no-check-certificate -nv -CPCFG=m-manylinux_2_17_x86_64.manylinux2014_x86_64 PY_V_MAJ=3 PY_V_MIN=6 ; rm -Rf codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl ; wget https://github.com/codac-team/codac/releases/download/v$PACKAGE_VERSION/codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl --no-check-certificate -nv -CPCFG=m-win32 PY_V_MAJ=3 PY_V_MIN=6 ; rm -Rf codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl ; wget https://github.com/codac-team/codac/releases/download/v$PACKAGE_VERSION/codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl --no-check-certificate -nv -CPCFG=m-win_amd64 PY_V_MAJ=3 PY_V_MIN=6 ; rm -Rf codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl ; wget https://github.com/codac-team/codac/releases/download/v$PACKAGE_VERSION/codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl --no-check-certificate -nv +PY_V_MAJ=3 -CPCFG=m-macosx_10_9_x86_64 PY_V_MAJ=3 PY_V_MIN=7 ; rm -Rf codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl ; wget https://github.com/codac-team/codac/releases/download/v$PACKAGE_VERSION/codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl --no-check-certificate -nv -CPCFG=m-macosx_11_0_arm64 PY_V_MAJ=3 PY_V_MIN=7 ; rm -Rf codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl ; wget https://github.com/codac-team/codac/releases/download/v$PACKAGE_VERSION/codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl --no-check-certificate -nv -CPCFG=m-manylinux_2_17_x86_64.manylinux2014_x86_64 PY_V_MAJ=3 PY_V_MIN=7 ; rm -Rf codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl ; wget https://github.com/codac-team/codac/releases/download/v$PACKAGE_VERSION/codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl --no-check-certificate -nv -CPCFG=m-win32 PY_V_MAJ=3 PY_V_MIN=7 ; rm -Rf codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl ; wget https://github.com/codac-team/codac/releases/download/v$PACKAGE_VERSION/codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl --no-check-certificate -nv -CPCFG=m-win_amd64 PY_V_MAJ=3 PY_V_MIN=7 ; rm -Rf codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl ; wget https://github.com/codac-team/codac/releases/download/v$PACKAGE_VERSION/codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl --no-check-certificate -nv +CPCFG=m-macosx_10_14_x86_64 PY_V_MIN=6 ; rm -Rf codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl ; wget https://github.com/codac-team/codac/releases/download/v$PACKAGE_VERSION/codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl --no-check-certificate -nv -CPCFG=-macosx_10_9_x86_64 PY_V_MAJ=3 PY_V_MIN=8 ; rm -Rf codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl ; wget https://github.com/codac-team/codac/releases/download/v$PACKAGE_VERSION/codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl --no-check-certificate -nv -CPCFG=-macosx_11_0_arm64 PY_V_MAJ=3 PY_V_MIN=8 ; rm -Rf codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl ; wget https://github.com/codac-team/codac/releases/download/v$PACKAGE_VERSION/codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl --no-check-certificate -nv -CPCFG=-manylinux_2_17_x86_64.manylinux2014_x86_64 PY_V_MAJ=3 PY_V_MIN=8 ; rm -Rf codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl ; wget https://github.com/codac-team/codac/releases/download/v$PACKAGE_VERSION/codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl --no-check-certificate -nv -CPCFG=-win32 PY_V_MAJ=3 PY_V_MIN=8 ; rm -Rf codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl ; wget https://github.com/codac-team/codac/releases/download/v$PACKAGE_VERSION/codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl --no-check-certificate -nv -CPCFG=-win_amd64 PY_V_MAJ=3 PY_V_MIN=8 ; rm -Rf codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl ; wget https://github.com/codac-team/codac/releases/download/v$PACKAGE_VERSION/codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl --no-check-certificate -nv +CPCFG=m-macosx_10_9_x86_64 PY_V_MIN=7 ; rm -Rf codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl ; wget https://github.com/codac-team/codac/releases/download/v$PACKAGE_VERSION/codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl --no-check-certificate -nv -CPCFG=-macosx_10_9_x86_64 PY_V_MAJ=3 PY_V_MIN=9 ; rm -Rf codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl ; wget https://github.com/codac-team/codac/releases/download/v$PACKAGE_VERSION/codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl --no-check-certificate -nv -CPCFG=-macosx_11_0_arm64 PY_V_MAJ=3 PY_V_MIN=9 ; rm -Rf codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl ; wget https://github.com/codac-team/codac/releases/download/v$PACKAGE_VERSION/codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl --no-check-certificate -nv -CPCFG=-manylinux_2_17_x86_64.manylinux2014_x86_64 PY_V_MAJ=3 PY_V_MIN=9 ; rm -Rf codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl ; wget https://github.com/codac-team/codac/releases/download/v$PACKAGE_VERSION/codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl --no-check-certificate -nv -CPCFG=-win32 PY_V_MAJ=3 PY_V_MIN=9 ; rm -Rf codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl ; wget https://github.com/codac-team/codac/releases/download/v$PACKAGE_VERSION/codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl --no-check-certificate -nv -CPCFG=-win_amd64 PY_V_MAJ=3 PY_V_MIN=9 ; rm -Rf codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl ; wget https://github.com/codac-team/codac/releases/download/v$PACKAGE_VERSION/codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl --no-check-certificate -nv +for PY_V_MIN in 6 7; do + for CPCFG in "m-macosx_11_0_arm64" "m-manylinux_2_17_x86_64.manylinux2014_x86_64" "m-win32" "m-win_amd64"; do + rm -Rf codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl ; wget https://github.com/codac-team/codac/releases/download/v$PACKAGE_VERSION/codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl --no-check-certificate -nv + done +done -CPCFG=-macosx_10_9_x86_64 PY_V_MAJ=3 PY_V_MIN=10 ; rm -Rf codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl ; wget https://github.com/codac-team/codac/releases/download/v$PACKAGE_VERSION/codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl --no-check-certificate -nv -CPCFG=-macosx_11_0_arm64 PY_V_MAJ=3 PY_V_MIN=10 ; rm -Rf codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl ; wget https://github.com/codac-team/codac/releases/download/v$PACKAGE_VERSION/codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl --no-check-certificate -nv -CPCFG=-manylinux_2_17_x86_64.manylinux2014_x86_64 PY_V_MAJ=3 PY_V_MIN=10 ; rm -Rf codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl ; wget https://github.com/codac-team/codac/releases/download/v$PACKAGE_VERSION/codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl --no-check-certificate -nv -CPCFG=-win32 PY_V_MAJ=3 PY_V_MIN=10 ; rm -Rf codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl ; wget https://github.com/codac-team/codac/releases/download/v$PACKAGE_VERSION/codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl --no-check-certificate -nv -CPCFG=-win_amd64 PY_V_MAJ=3 PY_V_MIN=10 ; rm -Rf codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl ; wget https://github.com/codac-team/codac/releases/download/v$PACKAGE_VERSION/codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl --no-check-certificate -nv - -CPCFG=-macosx_10_9_x86_64 PY_V_MAJ=3 PY_V_MIN=12 ; rm -Rf codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl ; wget https://github.com/codac-team/codac/releases/download/v$PACKAGE_VERSION/codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl --no-check-certificate -nv -CPCFG=-macosx_11_0_arm64 PY_V_MAJ=3 PY_V_MIN=12 ; rm -Rf codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl ; wget https://github.com/codac-team/codac/releases/download/v$PACKAGE_VERSION/codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl --no-check-certificate -nv -CPCFG=-manylinux_2_17_x86_64.manylinux2014_x86_64 PY_V_MAJ=3 PY_V_MIN=12 ; rm -Rf codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl ; wget https://github.com/codac-team/codac/releases/download/v$PACKAGE_VERSION/codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl --no-check-certificate -nv -CPCFG=-win32 PY_V_MAJ=3 PY_V_MIN=12 ; rm -Rf codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl ; wget https://github.com/codac-team/codac/releases/download/v$PACKAGE_VERSION/codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl --no-check-certificate -nv -CPCFG=-win_amd64 PY_V_MAJ=3 PY_V_MIN=12 ; rm -Rf codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl ; wget https://github.com/codac-team/codac/releases/download/v$PACKAGE_VERSION/codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl --no-check-certificate -nv - -CPCFG=-macosx_10_9_x86_64 PY_V_MAJ=3 PY_V_MIN=11 ; rm -Rf codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl ; wget https://github.com/codac-team/codac/releases/download/v$PACKAGE_VERSION/codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl --no-check-certificate -nv -CPCFG=-macosx_11_0_arm64 PY_V_MAJ=3 PY_V_MIN=11 ; rm -Rf codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl ; wget https://github.com/codac-team/codac/releases/download/v$PACKAGE_VERSION/codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl --no-check-certificate -nv -CPCFG=-manylinux_2_17_x86_64.manylinux2014_x86_64 PY_V_MAJ=3 PY_V_MIN=11 ; rm -Rf codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl ; wget https://github.com/codac-team/codac/releases/download/v$PACKAGE_VERSION/codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl --no-check-certificate -nv -CPCFG=-win32 PY_V_MAJ=3 PY_V_MIN=11 ; rm -Rf codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl ; wget https://github.com/codac-team/codac/releases/download/v$PACKAGE_VERSION/codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl --no-check-certificate -nv -CPCFG=-win_amd64 PY_V_MAJ=3 PY_V_MIN=11 ; rm -Rf codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl ; wget https://github.com/codac-team/codac/releases/download/v$PACKAGE_VERSION/codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl --no-check-certificate -nv +for PY_V_MIN in 8 9 10 11 12; do + for CPCFG in "-macosx_10_9_x86_64" "-macosx_11_0_arm64" "-manylinux_2_17_x86_64.manylinux2014_x86_64" "-win32" "-win_amd64"; do + rm -Rf codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl ; wget https://github.com/codac-team/codac/releases/download/v$PACKAGE_VERSION/codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl --no-check-certificate -nv + done +done From dcdee5888e02d32e93105832421dee09e3e2ea33 Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Sun, 10 Mar 2024 16:13:21 +0100 Subject: [PATCH 193/256] Minor correction in Chocolatey package --- packages/choco/codac/codac.nuspec | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/choco/codac/codac.nuspec b/packages/choco/codac/codac.nuspec index b04c96304..650a4e12f 100644 --- a/packages/choco/codac/codac.nuspec +++ b/packages/choco/codac/codac.nuspec @@ -36,11 +36,13 @@ The following package parameters can be set: - `/NoRegistry` - Will not try to update Windows registry. To pass package parameters, use `--params "''"` (e.g. `choco install codac --params "'/Path /NoRegistry'"`), and to install another binary package, try e.g. ``` +choco install -y chocolatey-core.extension choco install -y --ignore-dependencies codac --params "'/url:https://github.com/codac-team/codac/releases/download/v1/codac_x64_vc17.zip'" ``` https://github.com/codac-team/codac/releases + From 69cc2ee4569f0cb785e0945b486125ed83ef10f1 Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Sun, 17 Mar 2024 17:25:49 +0100 Subject: [PATCH 194/256] Update MATLAB/Octave documentation --- doc/doc/install/04-start-matlab-project.rst | 27 ++++++++++++++++++--- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/doc/doc/install/04-start-matlab-project.rst b/doc/doc/install/04-start-matlab-project.rst index 18dfc823c..dc853e1f9 100644 --- a/doc/doc/install/04-start-matlab-project.rst +++ b/doc/doc/install/04-start-matlab-project.rst @@ -10,7 +10,7 @@ Start a MATLAB project .. warning:: - | MATLAB support is preliminary, limited tests have been made for now. See `this example `_. + | MATLAB/Octave support is preliminary, limited tests have been made for now. See `this example `_. | Codac is ready to be used if you have at least MATLAB R2019b, `a supported version of Python `_ and :ref:`installed Codac Python package `. @@ -50,7 +50,26 @@ If everything is well installed on your computer, you should see the following w .. warning:: - Octave support is more limited than MATLAB at the moment (and only briefly tested on Ubuntu 22.04 for now). `Pythonic `_ needs to be installed to be able to call Python code from Octave. It has currently some limitations (see https://wiki.octave.org/Pythonic) such as: + Octave support has been only briefly tested on Ubuntu 22.04 for now. `Pythonic `_ can be used to be able to call Python code from Octave. Some known necessary adaptations (see also https://wiki.octave.org/Pythonic) are: - * Missing `import` command. https://gist.github.com/lebarsfa/ebc19b143df77753842f920b4b680b84#file-import-m can be loaded in Octave as a workaround. - * Operators such as `+`, `-`, `*`, `/`, `==`, `!=`, etc. do not seem overloaded (see https://gitlab.com/gnu-octave/octave-pythonic/-/issues/129, note that these operators are supported by MATLAB, but some others might not be supported by MATLAB either, see the bottom of https://fr.mathworks.com/help/matlab/matlab_external/differences-between-matlab-python.html). The internal methods `__add__()`, `__sub__()`, `__neg__()`, `__mul__()`, `__truediv__()`, `__eq__()`, `__ne__()`, etc. can be used instead. + * Missing `import` command. https://gist.github.com/lebarsfa/ebc19b143df77753842f920b4b680b84#file-import-m can be loaded in Octave as a workaround. To always load it, try to add its content to `~/.octaverc` (e.g. with `cat import.m>>~/.octaverc`). + * In at least Octave v6.4.0 and below, operators such as `+`, `-`, `*`, `/`, `==`, `!=`, etc. are not overloaded (note that these operators are supported by MATLAB, but some others might not be supported by MATLAB either, see the bottom of https://fr.mathworks.com/help/matlab/matlab_external/differences-between-matlab-python.html). The internal methods `__add__()`, `__sub__()`, `__neg__()`, `__mul__()`, `__truediv__()`, `__eq__()`, `__ne__()`, etc. can be used instead. A modified version of Pythonic is currently being developed to support these operators (see https://gitlab.com/gnu-octave/octave-pythonic/-/issues/129) and can be installed as follows: + + .. code-block:: bash + + sudo apt install octave liboctave-dev libpython3-dev build-essential make git + git clone https://gitlab.com/gnu-octave/octave-pythonic.git + cd octave-pythonic + git fetch https://gitlab.com/Vipul-Cariappa/octave-pythonic.git operators + git checkout -b octave-pythonic-operators FETCH_HEAD + make + make check + octave --path $PWD/inst --path $PWD/src + + Then in Octave: + + .. code-block:: octave + + pkg install . + + Then, Codac should be ready to be used in Octave as in MATLAB. From 0df460b3ade994ff9cabf9269c72242027898fb3 Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Thu, 29 Feb 2024 12:18:39 +0100 Subject: [PATCH 195/256] Minor update --- scripts/docker/build_pybinding.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/docker/build_pybinding.sh b/scripts/docker/build_pybinding.sh index a66733d34..66078d0f9 100755 --- a/scripts/docker/build_pybinding.sh +++ b/scripts/docker/build_pybinding.sh @@ -2,9 +2,9 @@ set -e -x -wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20240224/ibex_x86_64_manylinux2014.zip --no-check-certificate -nv -unzip -q ibex_x86_64_manylinux2014.zip -rm -Rf ibex_x86_64_manylinux2014.zip +wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20240224/ibex_$(uname -m)_manylinux2014.zip --no-check-certificate -nv +unzip -q ibex_$(uname -m)_manylinux2014.zip +rm -Rf ibex_$(uname -m)_manylinux2014.zip sudo cp -Rf ibex/* /usr/local/ git config --global --add safe.directory /io From 5d7ac471e866b8aaf4ede9f3acb4da6fc944e7c6 Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Fri, 22 Mar 2024 23:35:32 +0100 Subject: [PATCH 196/256] Update workflows --- .github/workflows/unixmatrix.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/unixmatrix.yml b/.github/workflows/unixmatrix.yml index 1f8ad0246..fba7781c8 100644 --- a/.github/workflows/unixmatrix.yml +++ b/.github/workflows/unixmatrix.yml @@ -10,7 +10,7 @@ jobs: deploy: runs-on: ubuntu-latest steps: - - uses: softprops/action-gh-release@v1 + - uses: softprops/action-gh-release@v2 id: create_release env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 63ef4c7661069eac20c69ce2332232dd473c9d19 Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Fri, 22 Mar 2024 23:36:56 +0100 Subject: [PATCH 197/256] Update workflows --- .github/workflows/unixmatrix.yml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/unixmatrix.yml b/.github/workflows/unixmatrix.yml index fba7781c8..c9e36aaef 100644 --- a/.github/workflows/unixmatrix.yml +++ b/.github/workflows/unixmatrix.yml @@ -44,8 +44,8 @@ jobs: - { os: windows-2019, shell: cmd, arch: x86, bitness: 32, runtime: mingw8, cmake_params: '-G "MinGW Makefiles"', cmake_flags: '-fPIC', choco_flags: '--x86', desc: 'Windows MinGW 8.1.0 x86' } - { os: windows-2019, shell: cmd, arch: x64, bitness: 64, runtime: mingw7, cmake_params: '-G "MinGW Makefiles"', cmake_flags: '-fPIC', desc: 'Windows MinGW 7.3.0 x64' } - { os: windows-2019, shell: cmd, arch: x86, bitness: 32, runtime: mingw7, cmake_params: '-G "MinGW Makefiles"', cmake_flags: '-fPIC', choco_flags: '--x86', desc: 'Windows MinGW 7.3.0 x86' } - - { os: ubuntu-22.04, shell: bash, arch: amd64, bitness: 64, runtime: jammy, cmake_flags: '-fPIC', desc: 'Ubuntu 22.04' } - - { os: ubuntu-20.04, shell: bash, arch: amd64, bitness: 64, runtime: focal, cmake_flags: '-fPIC', desc: 'Ubuntu 20.04' } + - { os: ubuntu-22.04, shell: bash, arch: amd64, bitness: 64, runtime: jammy, cmake_flags: '-fPIC', deb: true, desc: 'Ubuntu 22.04 amd64' } + - { os: ubuntu-20.04, shell: bash, arch: amd64, bitness: 64, runtime: focal, cmake_flags: '-fPIC', deb: true, desc: 'Ubuntu 20.04 amd64' } - { os: macos-14, shell: bash, arch: arm64, bitness: 64, runtime: sonoma, cmake_params: '-D CMAKE_SYSTEM_NAME=Darwin -D CMAKE_OSX_ARCHITECTURES=arm64', cmake_flags: '-fPIC', desc: 'macOS Sonoma arm64' } - { os: macos-14, shell: bash, arch: x86_64, bitness: 64, runtime: sonoma, cmake_params: '-D CMAKE_SYSTEM_NAME=Darwin -D CMAKE_OSX_ARCHITECTURES=x86_64', cmake_flags: '-fPIC', cross: true, desc: 'macOS Sonoma x86_64 (cross)' } - { os: macos-13, shell: bash, arch: arm64, bitness: 64, runtime: ventura, cmake_params: '-D CMAKE_SYSTEM_NAME=Darwin -D CMAKE_OSX_ARCHITECTURES=arm64', cmake_flags: '-fPIC', cross: true, desc: 'macOS Ventura arm64 (cross)' } @@ -75,7 +75,7 @@ jobs: if: runner.os=='Windows' - run: echo "PACKAGE_VERSION=$SOFTWARE_VERSION-${DEBIAN_PACKAGE_REV}${{ matrix.cfg.runtime }}$PACKAGE_REV" >> $GITHUB_ENV shell: bash - if: runner.os=='Linux' + if: matrix.cfg.deb==true - run: | choco install -y -r --no-progress checksum wget zip rem Workaround to try solve some random package download failures... @@ -129,7 +129,7 @@ jobs: sudo dpkg -i libibex-dev-2.8.9.20240224-0${{ matrix.cfg.runtime }}0_${{ matrix.cfg.arch }}.deb rm -Rf libibex-dev-2.8.9.20240224-0${{ matrix.cfg.runtime }}0_${{ matrix.cfg.arch }}.deb shell: bash - if: runner.os=='Linux' + if: matrix.cfg.deb==true - run: | brew install eigen wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20240224/ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip --no-check-certificate -nv @@ -154,8 +154,8 @@ jobs: mkdir -p codac_standalone/example ; cd codac_standalone wget https://community.chocolatey.org/api/v2/package/eigen/3.4.0.20240224 --no-check-certificate -nv ; unzip -q 3.4.0.20240224 -d eigen ; rm -Rf 3.4.0.20240224 eigen/*.xml eigen/*.nuspec eigen/_* eigen/package eigen/tools if [ ${{ runner.os }} = Windows ]; then cp -Rf /C/ProgramData/chocolatey/lib/ibex . ; rm -Rf ibex/tools ibex/ibex.* - elif [ ${{ runner.os }} = Linux ]; then mkdir -p ibex/include ; mkdir -p ibex/lib ; mkdir -p ibex/share ; mkdir -p ibex/bin ; cp -Rf /usr/include/ibex* ibex/include/ ; cp -Rf /usr/lib/*ibex* ibex/lib/ ; cp -Rf /usr/share/*ibex* ibex/share/ ; cp -Rf /usr/share/pkgconfig ibex/share/ ; cp -Rf /usr/bin/ibex* ibex/bin/ - elif [ ${{ runner.os }} = macOS ]; then cp -Rf ../ibex . + elif [ ${{ matrix.cfg.deb }} = true ]; then mkdir -p ibex/include ; mkdir -p ibex/lib ; mkdir -p ibex/share ; mkdir -p ibex/bin ; cp -Rf /usr/include/ibex* ibex/include/ ; cp -Rf /usr/lib/*ibex* ibex/lib/ ; cp -Rf /usr/share/*ibex* ibex/share/ ; cp -Rf /usr/share/pkgconfig ibex/share/ ; cp -Rf /usr/bin/ibex* ibex/bin/ + else cp -Rf ../ibex . fi cp -Rf ../codac . ; cp -Rf ../tests/test_codac/* ./example/ ; cd .. ; zip -q -r codac_standalone_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip codac_standalone shell: bash @@ -170,7 +170,7 @@ jobs: shell: bash if: matrix.cfg.cross!=true - run: | - if [ ${{ runner.os }} = Windows ]; then source ~/refreshenv.bashrc ; refreshenv ; export PATH=$BASHMINGWPATH:$PATH ; fi + source ~/refreshenv.bashrc ; refreshenv ; export PATH=$BASHMINGWPATH:$PATH cd packages/choco sed_param=s/\1\<\\/version\>/\${PACKAGE_VERSION}\<\\/version\>/ sed -i "$sed_param" codac/codac.nuspec @@ -193,11 +193,11 @@ jobs: - run: | cd packages chmod +x ./genlibcodac-dev.sh - ./genlibcodac-dev.sh ubuntu ${{ matrix.cfg.runtime }} ${{ matrix.cfg.arch }} $SOFTWARE_VERSION $DEBIAN_PACKAGE_REV $PACKAGE_REV + ./genlibcodac-dev.sh $(lsb_release -is | tr [:upper:] [:lower:]) ${{ matrix.cfg.runtime }} $(dpkg --print-architecture) $SOFTWARE_VERSION $DEBIAN_PACKAGE_REV $PACKAGE_REV cd .. - sudo dpkg -i libcodac-dev-$PACKAGE_VERSION\_${{ matrix.cfg.arch }}.deb + sudo dpkg -i libcodac-dev-$PACKAGE_VERSION\_$(dpkg --print-architecture).deb shell: bash - if: runner.os=='Linux' + if: matrix.cfg.deb==true - run: | sudo cp -Rf codac/* /usr/local/ shell: bash From 352f7bb8e5ef82e6168a5fefad472715d50904f7 Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Sun, 24 Mar 2024 19:44:45 +0100 Subject: [PATCH 198/256] Regroup docker workflows --- .github/workflows/dockermatrix.yml | 103 +++++++++++++++++++++++++++++ .github/workflows/dockerpi.yml | 27 -------- 2 files changed, 103 insertions(+), 27 deletions(-) create mode 100644 .github/workflows/dockermatrix.yml delete mode 100644 .github/workflows/dockerpi.yml diff --git a/.github/workflows/dockermatrix.yml b/.github/workflows/dockermatrix.yml new file mode 100644 index 000000000..8869a87eb --- /dev/null +++ b/.github/workflows/dockermatrix.yml @@ -0,0 +1,103 @@ +# This file checks that the lib runs on ARM +on: + push: + branches: 'master' + tags: '' # Restrict to blank tags + pull_request: + +jobs: + dockermatrix: + runs-on: ubuntu-latest + defaults: + run: + shell: ${{ matrix.cfg.shell }} + strategy: + fail-fast: false + matrix: + cfg: + - { img: 'lebarsfa/manylinux2014_x86_64-for-codac', shell: bash, arch: x86_64, bitness: 64, runtime: manylinux2014, cmake_flags: '-fPIC', desc: 'CentOS manylinux2014 x86_64' } + - { img: 'lebarsfa/manylinux2014_aarch64-for-codac', shell: bash, arch: aarch64, bitness: 64, runtime: manylinux2014, cmake_flags: '-fPIC', docker_flags: '--platform linux/arm64', desc: 'CentOS manylinux2014 aarch64' } + - { img: 'lebarsfa/pi-64:jammy-for-codac', shell: bash, arch: arm64, bitness: 64, runtime: jammy, cmake_flags: '-fPIC', deb: true, desc: 'Ubuntu 22.04 arm64' } + - { img: 'lebarsfa/pi-64:bookworm-for-codac', shell: bash, arch: arm64, bitness: 64, runtime: bookworm, cmake_flags: '-fPIC', deb: true, desc: 'Debian Bookworm arm64' } + - { img: 'lebarsfa/pi:bookworm-for-codac', shell: bash, arch: armhf, bitness: 32, runtime: bookworm, cmake_flags: '-fPIC', deb: true, desc: 'Raspbian Bookworm armv6hf' } + - { img: 'lebarsfa/pi:buster-for-codac', shell: bash, arch: armhf, bitness: 32, runtime: buster, cmake_flags: '-fPIC', deb: true, desc: 'Raspbian Buster armv6hf' } + name: ${{ matrix.cfg.desc }} + steps: + - uses: actions/checkout@v4 + with: + submodules: true + fetch-depth: 0 + clean: false + # From https://github.com/Munkei/VersionFromGit.cmake/blob/master/VersionFromGit.cmake + - run: echo "git_tag=`git describe --tags --abbrev=0`" >> $GITHUB_ENV + shell: bash + # See https://askubuntu.com/questions/620533/what-is-the-meaning-of-the-xubuntuy-string-in-ubuntu-package-names + - run: | + echo "SOFTWARE_VERSION=${git_tag:1}" >> $GITHUB_ENV + echo "DEBIAN_PACKAGE_REV=0" >> $GITHUB_ENV + echo "PACKAGE_REV=0" >> $GITHUB_ENV + echo "CHOCO_PACKAGE_REV=" >> $GITHUB_ENV + echo "VERBOSE=1" >> $GITHUB_ENV + shell: bash + - run: echo "PACKAGE_VERSION=$SOFTWARE_VERSION-${DEBIAN_PACKAGE_REV}${{ matrix.cfg.runtime }}$PACKAGE_REV" >> $GITHUB_ENV + shell: bash + if: matrix.cfg.deb==true + - run: | + sudo apt-get -y install qemu binfmt-support qemu-user-static || true + #docker run --rm --privileged multiarch/qemu-user-static:register --reset + docker run --rm --privileged multiarch/qemu-user-static --reset -p yes + if: (matrix.cfg.arch!='x86_64')&&(matrix.cfg.arch!='i386') + - run: | + docker run ${{ matrix.cfg.docker_flags }} -i -v "${PWD}/..:${PWD}/.." ${{ matrix.cfg.img }} /bin/bash -c "uname -a ; cat /etc/os-release ; cd ${PWD} && pwd && lsb_release -a ; \ + if [ ${{ matrix.cfg.deb }} = true ]; then \ + sudo apt-get -q update --allow-releaseinfo-change ; sudo apt-get -y install libeigen3-dev dpkg-dev || true && \ + wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20240224/libibex-dev-2.8.9.20240224-0${{ matrix.cfg.runtime }}0_${{ matrix.cfg.arch }}.deb --no-check-certificate -nv && \ + sudo dpkg -i libibex-dev-2.8.9.20240224-0${{ matrix.cfg.runtime }}0_${{ matrix.cfg.arch }}.deb && \ + rm -Rf libibex-dev-2.8.9.20240224-0${{ matrix.cfg.runtime }}0_${{ matrix.cfg.arch }}.deb ; \ + else \ + wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20240224/ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip --no-check-certificate -nv && \ + unzip -q ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip && \ + rm -Rf ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip && \ + sudo cp -Rf ibex/* /usr/ ; \ + fi && \ + mkdir build ; cd build && \ + cmake -E env CXXFLAGS="${{ matrix.cfg.cmake_flags }}" CFLAGS="${{ matrix.cfg.cmake_flags }}" cmake ${{ matrix.cfg.cmake_params }} -D CMAKE_INSTALL_PREFIX="../codac" .. && \ + cmake --build . --config Debug --target install && \ + cd .. && \ + zip -q -r codac_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip codac && \ + mkdir -p codac_standalone/example ; cd codac_standalone && \ + wget https://community.chocolatey.org/api/v2/package/eigen/3.4.0.20240224 --no-check-certificate -nv ; unzip -q 3.4.0.20240224 -d eigen ; rm -Rf 3.4.0.20240224 eigen/*.xml eigen/*.nuspec eigen/_* eigen/package eigen/tools && \ + if [ ${{ matrix.cfg.deb }} = true ]; then mkdir -p ibex/include ; mkdir -p ibex/lib ; mkdir -p ibex/share ; mkdir -p ibex/bin ; cp -Rf /usr/include/ibex* ibex/include/ ; cp -Rf /usr/lib/*ibex* ibex/lib/ ; cp -Rf /usr/share/*ibex* ibex/share/ ; cp -Rf /usr/share/pkgconfig ibex/share/ ; cp -Rf /usr/bin/ibex* ibex/bin/ ; \ + else cp -Rf ../ibex . ; \ + fi && \ + cp -Rf ../codac . ; cp -Rf ../tests/test_codac/* ./example/ ; cd .. ; zip -q -r codac_standalone_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip codac_standalone && \ + cd codac_standalone/example && \ + cmake ${{ matrix.cfg.cmake_params }} . && \ + cmake --build . --config Release && \ + file ./${{ matrix.cfg.test_config }}my_project && \ + ./${{ matrix.cfg.test_config }}my_project && \ + cd ../.. && \ + if [ ${{ matrix.cfg.deb }} = true ]; then \ + cd packages && \ + chmod +x ./genlibcodac-dev.sh && \ + ./genlibcodac-dev.sh \$(lsb_release -is | tr [:upper:] [:lower:]) ${{ matrix.cfg.runtime }} \$(dpkg --print-architecture) $SOFTWARE_VERSION $DEBIAN_PACKAGE_REV $PACKAGE_REV && \ + cd .. && \ + sudo dpkg -i libcodac-dev-$PACKAGE_VERSION\_\$(dpkg --print-architecture).deb ; \ + else \ + sudo cp -Rf codac/* /usr/local/ ; \ + fi && \ + rm -Rf codac && \ + cd tests/test_codac && \ + cmake ${{ matrix.cfg.cmake_params }} . && \ + cmake --build . --config Release && \ + file ./${{ matrix.cfg.test_config }}my_project && \ + ./${{ matrix.cfg.test_config }}my_project && \ + cd ../.." + - uses: xresloader/upload-to-github-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + file: "*.zip;*.deb" + overwrite: true + tag_name: autotagname-${{ github.sha }} + if: (github.event_name != 'pull_request') diff --git a/.github/workflows/dockerpi.yml b/.github/workflows/dockerpi.yml deleted file mode 100644 index b209c9f09..000000000 --- a/.github/workflows/dockerpi.yml +++ /dev/null @@ -1,27 +0,0 @@ -# This file checks that the lib runs on ARM -on: - push: - branches: 'master' - tags: '' # Restrict to blank tags - pull_request: - -jobs: - dockerpi: - name: Raspbian Buster pi Docker - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - submodules: true - fetch-depth: 0 - clean: false - - run: docker run --rm --privileged multiarch/qemu-user-static:register --reset - - run: docker run -i -v "${PWD}/..:${PWD}/.." lebarsfa/pi:buster-for-codac /bin/bash -c "uname -a && cat /etc/os-release && cd ${PWD} && pwd && lsb_release -a && sudo apt-get -q update --allow-releaseinfo-change ; sudo apt-get -y install libeigen3-dev ; wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20240224/ibex_armv6hf_buster.zip --no-check-certificate -nv ; unzip -q ibex_armv6hf_buster.zip && rm -Rf ibex_armv6hf_buster.zip && sudo cp -Rf ibex/* /usr/local/ && mkdir build ; cd build && cmake -E env CXXFLAGS="-fPIC" CFLAGS="-fPIC" cmake -D CMAKE_INSTALL_PREFIX=../codac .. && cmake --build . --target install && cd .. && mkdir -p codac_standalone/example ; cd codac_standalone ; wget https://community.chocolatey.org/api/v2/package/eigen/3.4.0.20240224 --no-check-certificate -nv ; unzip -q 3.4.0.20240224 -d eigen ; rm -Rf 3.4.0.20240224 eigen/*.xml eigen/*.nuspec eigen/_* eigen/package eigen/tools ; mkdir -p ibex/include ; mkdir -p ibex/lib ; mkdir -p ibex/share ; mkdir -p ibex/bin ; cp -Rf /usr/local/include/ibex* ibex/include/ ; cp -Rf /usr/local/lib/*ibex* ibex/lib/ ; cp -Rf /usr/local/share/*ibex* ibex/share/ ; cp -Rf /usr/local/share/pkgconfig ibex/share/ ; cp -Rf /usr/local/bin/ibex* ibex/bin/ ; cp -Rf ../codac . ; cp -Rf ../tests/test_codac/* ./example/ ; cd .. ; zip -q -r codac_standalone_armv6hf_buster.zip codac_standalone ; cd codac_standalone/example && cmake . && cmake --build . && ./my_project" - - uses: xresloader/upload-to-github-release@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - file: "*.zip" - overwrite: true - tag_name: autotagname-${{ github.sha }} - if: (github.event_name != 'pull_request')&&(github.ref_name == 'master') From 12cdd80784099e3b1495d856b23449871cbe2b7c Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Sun, 25 Feb 2024 15:50:23 +0100 Subject: [PATCH 199/256] Update .deb configurations --- packages/bionic/armhf/libcodac-dev/DEBIAN/control | 9 --------- .../{bionic/amd64/libcodac-dev/DEBIAN => deb}/control | 0 packages/focal/amd64/libcodac-dev/DEBIAN/control | 9 --------- packages/focal/armhf/libcodac-dev/DEBIAN/control | 9 --------- packages/genlibcodac-dev.sh | 3 +++ packages/jammy/amd64/libcodac-dev/DEBIAN/control | 9 --------- packages/jammy/armhf/libcodac-dev/DEBIAN/control | 9 --------- packages/xenial/amd64/libcodac-dev/DEBIAN/control | 9 --------- packages/xenial/armhf/libcodac-dev/DEBIAN/control | 9 --------- 9 files changed, 3 insertions(+), 63 deletions(-) delete mode 100644 packages/bionic/armhf/libcodac-dev/DEBIAN/control rename packages/{bionic/amd64/libcodac-dev/DEBIAN => deb}/control (100%) delete mode 100644 packages/focal/amd64/libcodac-dev/DEBIAN/control delete mode 100644 packages/focal/armhf/libcodac-dev/DEBIAN/control delete mode 100644 packages/jammy/amd64/libcodac-dev/DEBIAN/control delete mode 100644 packages/jammy/armhf/libcodac-dev/DEBIAN/control delete mode 100644 packages/xenial/amd64/libcodac-dev/DEBIAN/control delete mode 100644 packages/xenial/armhf/libcodac-dev/DEBIAN/control diff --git a/packages/bionic/armhf/libcodac-dev/DEBIAN/control b/packages/bionic/armhf/libcodac-dev/DEBIAN/control deleted file mode 100644 index fc1156647..000000000 --- a/packages/bionic/armhf/libcodac-dev/DEBIAN/control +++ /dev/null @@ -1,9 +0,0 @@ -Package: libcodac-dev -Version: 1 -Architecture: armhf -Depends: libeigen3-dev, libibex-dev -Section: math -Priority: optional -Description: Codac is a library providing tools for constraint programming over reals, trajectories and sets -Maintainer: codac.io -Homepage: codac.io diff --git a/packages/bionic/amd64/libcodac-dev/DEBIAN/control b/packages/deb/control similarity index 100% rename from packages/bionic/amd64/libcodac-dev/DEBIAN/control rename to packages/deb/control diff --git a/packages/focal/amd64/libcodac-dev/DEBIAN/control b/packages/focal/amd64/libcodac-dev/DEBIAN/control deleted file mode 100644 index 1a587f0ce..000000000 --- a/packages/focal/amd64/libcodac-dev/DEBIAN/control +++ /dev/null @@ -1,9 +0,0 @@ -Package: libcodac-dev -Version: 1 -Architecture: amd64 -Depends: libeigen3-dev, libibex-dev -Section: math -Priority: optional -Description: Codac is a library providing tools for constraint programming over reals, trajectories and sets -Maintainer: codac.io -Homepage: codac.io diff --git a/packages/focal/armhf/libcodac-dev/DEBIAN/control b/packages/focal/armhf/libcodac-dev/DEBIAN/control deleted file mode 100644 index fc1156647..000000000 --- a/packages/focal/armhf/libcodac-dev/DEBIAN/control +++ /dev/null @@ -1,9 +0,0 @@ -Package: libcodac-dev -Version: 1 -Architecture: armhf -Depends: libeigen3-dev, libibex-dev -Section: math -Priority: optional -Description: Codac is a library providing tools for constraint programming over reals, trajectories and sets -Maintainer: codac.io -Homepage: codac.io diff --git a/packages/genlibcodac-dev.sh b/packages/genlibcodac-dev.sh index 4442d6400..c49fa6bd9 100644 --- a/packages/genlibcodac-dev.sh +++ b/packages/genlibcodac-dev.sh @@ -12,9 +12,12 @@ REV=$6 #sudo apt-get -y install dpkg-dev +mkdir -p $DIST/$ARCH cd $DIST/$ARCH mkdir -p libcodac-dev/usr cp -Rf ../../../codac/* libcodac-dev/usr/ +mkdir -p libcodac-dev/DEBIAN +cp -Rf ../../deb/control libcodac-dev/DEBIAN/control sed_param=s/Version:\ .*/Version:\ ${VER}/ sed -i "$sed_param" libcodac-dev/DEBIAN/control sed_param=s/Architecture:\ .*/Architecture:\ ${ARCH}/ diff --git a/packages/jammy/amd64/libcodac-dev/DEBIAN/control b/packages/jammy/amd64/libcodac-dev/DEBIAN/control deleted file mode 100644 index 1a587f0ce..000000000 --- a/packages/jammy/amd64/libcodac-dev/DEBIAN/control +++ /dev/null @@ -1,9 +0,0 @@ -Package: libcodac-dev -Version: 1 -Architecture: amd64 -Depends: libeigen3-dev, libibex-dev -Section: math -Priority: optional -Description: Codac is a library providing tools for constraint programming over reals, trajectories and sets -Maintainer: codac.io -Homepage: codac.io diff --git a/packages/jammy/armhf/libcodac-dev/DEBIAN/control b/packages/jammy/armhf/libcodac-dev/DEBIAN/control deleted file mode 100644 index fc1156647..000000000 --- a/packages/jammy/armhf/libcodac-dev/DEBIAN/control +++ /dev/null @@ -1,9 +0,0 @@ -Package: libcodac-dev -Version: 1 -Architecture: armhf -Depends: libeigen3-dev, libibex-dev -Section: math -Priority: optional -Description: Codac is a library providing tools for constraint programming over reals, trajectories and sets -Maintainer: codac.io -Homepage: codac.io diff --git a/packages/xenial/amd64/libcodac-dev/DEBIAN/control b/packages/xenial/amd64/libcodac-dev/DEBIAN/control deleted file mode 100644 index 1a587f0ce..000000000 --- a/packages/xenial/amd64/libcodac-dev/DEBIAN/control +++ /dev/null @@ -1,9 +0,0 @@ -Package: libcodac-dev -Version: 1 -Architecture: amd64 -Depends: libeigen3-dev, libibex-dev -Section: math -Priority: optional -Description: Codac is a library providing tools for constraint programming over reals, trajectories and sets -Maintainer: codac.io -Homepage: codac.io diff --git a/packages/xenial/armhf/libcodac-dev/DEBIAN/control b/packages/xenial/armhf/libcodac-dev/DEBIAN/control deleted file mode 100644 index fc1156647..000000000 --- a/packages/xenial/armhf/libcodac-dev/DEBIAN/control +++ /dev/null @@ -1,9 +0,0 @@ -Package: libcodac-dev -Version: 1 -Architecture: armhf -Depends: libeigen3-dev, libibex-dev -Section: math -Priority: optional -Description: Codac is a library providing tools for constraint programming over reals, trajectories and sets -Maintainer: codac.io -Homepage: codac.io From 9d502a25dd5203f9b2344fdfc9fff45541aaa811 Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Sun, 24 Mar 2024 22:29:57 +0100 Subject: [PATCH 200/256] Update workflows --- .github/workflows/dockermatrix.yml | 2 +- .github/workflows/macosmatrix.yml | 2 +- .github/workflows/unixmatrix.yml | 2 +- .github/workflows/vcmatrix.yml | 2 +- scripts/docker/build_pybinding.sh | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/dockermatrix.yml b/.github/workflows/dockermatrix.yml index 8869a87eb..9a16e78f2 100644 --- a/.github/workflows/dockermatrix.yml +++ b/.github/workflows/dockermatrix.yml @@ -62,7 +62,7 @@ jobs: fi && \ mkdir build ; cd build && \ cmake -E env CXXFLAGS="${{ matrix.cfg.cmake_flags }}" CFLAGS="${{ matrix.cfg.cmake_flags }}" cmake ${{ matrix.cfg.cmake_params }} -D CMAKE_INSTALL_PREFIX="../codac" .. && \ - cmake --build . --config Debug --target install && \ + cmake --build . -j 4 --config Debug --target install && \ cd .. && \ zip -q -r codac_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip codac && \ mkdir -p codac_standalone/example ; cd codac_standalone && \ diff --git a/.github/workflows/macosmatrix.yml b/.github/workflows/macosmatrix.yml index 5729a46c6..804506aa9 100644 --- a/.github/workflows/macosmatrix.yml +++ b/.github/workflows/macosmatrix.yml @@ -65,7 +65,7 @@ jobs: - run: | mkdir build ; cd build cmake -E env CXXFLAGS="${{ matrix.cfg.cmake_flags }}" CFLAGS="${{ matrix.cfg.cmake_flags }}" cmake ${{ matrix.cfg.cmake_params }} -D CMAKE_SYSTEM_NAME=Darwin -D CMAKE_OSX_ARCHITECTURES=${{ matrix.cfg.arch }} -D CMAKE_INSTALL_PREFIX="../codac" -D WITH_PYTHON=ON .. - cmake --build . --config Release --target install + cmake --build . -j 4 --config Release --target install cmake --build . --config Release --target pip_package ; cp `ls *.whl` ../`ls *.whl | sed "s/py3-none-any/cp${{ matrix.cfg.py_v_maj }}${{ matrix.cfg.py_v_min }}-cp${{ matrix.cfg.py_v_maj }}${{ matrix.cfg.py_v_min }}${{ matrix.cfg.cpcfg }}/"` cd .. shell: bash diff --git a/.github/workflows/unixmatrix.yml b/.github/workflows/unixmatrix.yml index c9e36aaef..adc52837a 100644 --- a/.github/workflows/unixmatrix.yml +++ b/.github/workflows/unixmatrix.yml @@ -143,7 +143,7 @@ jobs: if [ ${{ runner.os }} = Windows ]; then source ~/refreshenv.bashrc ; refreshenv ; export PATH=$BASHMINGWPATH:$PATH ; fi mkdir build ; cd build cmake -E env CXXFLAGS="${{ matrix.cfg.cmake_flags }}" CFLAGS="${{ matrix.cfg.cmake_flags }}" cmake ${{ matrix.cfg.cmake_params }} -D CMAKE_INSTALL_PREFIX="../codac" .. - cmake --build . --config Debug --target install + cmake --build . -j 4 --config Debug --target install cd .. sed_param=s/PATH_SUFFIXES\ /PATHS\ \$\{CMAKE_CURRENT_LIST_FILE\}\\/..\\/..\\/..\\/..\\/\ PATH_SUFFIXES\ / if [ ${{ runner.os }} = Windows ]; then sed -i "$sed_param" codac/share/codac/cmake/*.cmake ; fi diff --git a/.github/workflows/vcmatrix.yml b/.github/workflows/vcmatrix.yml index 343ea72ed..300f985f4 100644 --- a/.github/workflows/vcmatrix.yml +++ b/.github/workflows/vcmatrix.yml @@ -65,7 +65,7 @@ jobs: - run: | mkdir build ; cd build cmake -E env CXXFLAGS=" /wd4267 /wd4244 /wd4305 /wd4996" CFLAGS=" /wd4267 /wd4244 /wd4305 /wd4996" cmake ${{ matrix.cfg.cmake_params }} -D CMAKE_INSTALL_PREFIX="../codac" -D WITH_PYTHON=ON .. - cmake --build . --config Release --target install + cmake --build . -j 4 --config Release --target install cmake --build . --config Release --target pip_package ; cp `ls *.whl` ../`ls *.whl | sed "s/py3-none-any/cp${{ matrix.cfg.py_v_maj }}${{ matrix.cfg.py_v_min }}-cp${{ matrix.cfg.py_v_maj }}${{ matrix.cfg.py_v_min }}${{ matrix.cfg.cpcfg }}/"` cd .. shell: bash diff --git a/scripts/docker/build_pybinding.sh b/scripts/docker/build_pybinding.sh index 66078d0f9..46eb240d3 100755 --- a/scripts/docker/build_pybinding.sh +++ b/scripts/docker/build_pybinding.sh @@ -18,7 +18,7 @@ for PYBIN in /opt/python/cp3*/bin; do "${PYBIN}/python" -m pip install --upgrade pip mkdir -p build_dir && cd build_dir cmake -E env CXXFLAGS="-fPIC" CFLAGS="-fPIC" cmake -DPYTHON_EXECUTABLE=${PYBIN}/python -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTS=ON -DWITH_TUBE_TREE=OFF -DWITH_CAPD=OFF -DWITH_PYTHON=ON .. - make -j2 + make -j4 make test ARGS="-V --output-on-failure" echo "start of Testing/Temporary/LastTest.log" From 163c9ef29af2fb2249049144e400a38543d3e2e1 Mon Sep 17 00:00:00 2001 From: Simon Rohou Date: Thu, 11 Apr 2024 10:49:31 +0200 Subject: [PATCH 201/256] [py] added draw_polygon et Polygon bindings --- .../src/core/geometry/codac_py_geometry.cpp | 35 +++++++++++++++++++ .../src/core/graphics/codac_py_VIBesFig.cpp | 4 +++ 2 files changed, 39 insertions(+) diff --git a/python/src/core/geometry/codac_py_geometry.cpp b/python/src/core/geometry/codac_py_geometry.cpp index 2d2ed71f1..742d9b814 100644 --- a/python/src/core/geometry/codac_py_geometry.cpp +++ b/python/src/core/geometry/codac_py_geometry.cpp @@ -17,6 +17,7 @@ #include #include "../contractors/static/codac_py_Ctc.h" #include "../separators/codac_py_Sep.h" +#include "codac_Polygon.h" // Generated file from Doxygen XML (doxygen2docstring.py): #include "codac_py_CtcSegment_docs.h" @@ -28,6 +29,34 @@ namespace py = pybind11; using namespace ibex; using namespace codac; +Polygon* create_polygon_from_pylist(const vector& lst) +{ + vector v_pts; + + if(lst.size() < 1) + throw invalid_argument("size of the input list is 0"); + + //double (*tmp)[2] = new double[lst.size()][2]; + for(size_t i = 0; i < lst.size(); i++) + { + if(lst[i].size() != 2) + { + //delete[] tmp; + throw invalid_argument("sub list must contain only two elements"); + } + + //tmp[i][0] = lst[i][0].cast(); + //tmp[i][1] = lst[i][1].cast(); + v_pts.push_back({ lst[i][0].cast(), lst[i][1].cast() }); + } + + //IntervalVector *instance = new IntervalVector(lst.size(), tmp); + //delete[] tmp; + //return instance; + return new Polygon(v_pts); + // todo: manage delete +} + SepPolygon* SepPolygonFromList(std::vector< std::array >& lst){ size_t n = lst.size(); std::vector ax(n), ay(n),bx(n),by(n); @@ -66,4 +95,10 @@ void export_geometry(py::module& m, py::class_& ctc, py::class_(m, "Polygon") + .def(py::init(&create_polygon_from_pylist), "list"_a) + .def(py::init>()) + ; + } diff --git a/python/src/core/graphics/codac_py_VIBesFig.cpp b/python/src/core/graphics/codac_py_VIBesFig.cpp index eb895a904..4ff9ebe4a 100644 --- a/python/src/core/graphics/codac_py_VIBesFig.cpp +++ b/python/src/core/graphics/codac_py_VIBesFig.cpp @@ -96,5 +96,9 @@ void export_VIBesFig(py::module& m) .def("draw_line", (void (VIBesFig::*)(const std::vector&,const std::vector&,const string&,const vibes::Params &))&VIBesFig::draw_line, "todo", "v_x"_a, "v_y"_a, "color"_a="", "params"_a=vibes::Params()) + + .def("draw_polygon", (void (VIBesFig::*)(const Polygon&,const string&,const vibes::Params &))&VIBesFig::draw_polygon, + VIBESFIG_VOID_DRAW_POLYGON_POLYGON_STRING_VIBESPARAMS, + "p"_a, "color"_a="", "params"_a=vibes::Params()) ; } \ No newline at end of file From add377a9f5a763d05cfe5a139573e63d48749b33 Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Sun, 7 Apr 2024 17:37:34 +0200 Subject: [PATCH 202/256] Update documentation --- doc/doc/install/01-installation.rst | 5 ----- 1 file changed, 5 deletions(-) diff --git a/doc/doc/install/01-installation.rst b/doc/doc/install/01-installation.rst index ad8893491..0fb0dae8d 100644 --- a/doc/doc/install/01-installation.rst +++ b/doc/doc/install/01-installation.rst @@ -74,11 +74,6 @@ Some methods have been implemented in Codac in order to extend VIBes' features t You can `download the VIBes viewer directly from the official page `_ (click on the *Last Release* link). -.. warning:: - - | **macOS (arm64):** - | `Rosetta `_ might be necessary to run `VIBes `_. - .. admonition:: (optional) Get the very last version of VIBes from the sources You can also install the last version from the sources available on `the GitHub development repository `_. From 720e990d6335987a7981df6d324a5f13e2918e65 Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Thu, 11 Apr 2024 19:57:30 +0200 Subject: [PATCH 203/256] Update getlatestcodacwhl.sh --- scripts/wheels/getlatestcodacwhl.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/wheels/getlatestcodacwhl.sh b/scripts/wheels/getlatestcodacwhl.sh index e4538944e..57d87717a 100644 --- a/scripts/wheels/getlatestcodacwhl.sh +++ b/scripts/wheels/getlatestcodacwhl.sh @@ -8,7 +8,7 @@ export LK_VER=`cat lk_codac_whl.ver` echo Latest: $LATEST_VER echo Last known: $LK_VER -if [[ -z "$LK_VER" || $(printf "%s\n%s" "$LATEST_VER" "$LK_VER" | sort -V | head -n 1) != "$LATEST_VER" && "$LATEST_VER" != "$LK_VER" ]] then +if [[ -z "$LK_VER" || $(printf "%s\n%s" "$LATEST_VER" "$LK_VER" | sort -V | head -n 1) != "$LATEST_VER" && "$LATEST_VER" != "$LK_VER" ]]; then echo $LATEST_VER is newer than $LK_VER echo $LATEST_VER>lk_codac_whl.ver && cat lk_codac_whl.ver | ./gencodacwhl.sh fi From 930f3ced5d5df8a0ab3dcdd910272776838e5ecd Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Thu, 11 Apr 2024 19:57:34 +0200 Subject: [PATCH 204/256] Update Python wheels upload documentation --- doc/doc/dev/info_dev.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/doc/dev/info_dev.rst b/doc/doc/dev/info_dev.rst index eecd2eb68..966363e31 100644 --- a/doc/doc/dev/info_dev.rst +++ b/doc/doc/dev/info_dev.rst @@ -214,8 +214,11 @@ Get all the *wheels* generated by GitHub Actions (the generated release needs to .. code-block:: bash + sudo apt install curl wget sed cd scripts/wheels + chmod a+x *.sh ./getlatestcodacwhl.sh + rm lk_codac_whl.ver # Optional, contain the version number that was last downloaded. Upload the *wheels* on PyPi: From 2e858cbcc127518e40bf4abf7fdc345e9d45b01a Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Sun, 7 Apr 2024 17:37:34 +0200 Subject: [PATCH 205/256] Update documentation --- doc/doc/install/01-installation.rst | 5 ----- 1 file changed, 5 deletions(-) diff --git a/doc/doc/install/01-installation.rst b/doc/doc/install/01-installation.rst index ad8893491..0fb0dae8d 100644 --- a/doc/doc/install/01-installation.rst +++ b/doc/doc/install/01-installation.rst @@ -74,11 +74,6 @@ Some methods have been implemented in Codac in order to extend VIBes' features t You can `download the VIBes viewer directly from the official page `_ (click on the *Last Release* link). -.. warning:: - - | **macOS (arm64):** - | `Rosetta `_ might be necessary to run `VIBes `_. - .. admonition:: (optional) Get the very last version of VIBes from the sources You can also install the last version from the sources available on `the GitHub development repository `_. From 76c4d9221d72ec5b3b7cabaa9ea97f15b3c203ba Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Thu, 11 Apr 2024 19:57:30 +0200 Subject: [PATCH 206/256] Update getlatestcodacwhl.sh --- scripts/wheels/getlatestcodacwhl.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/wheels/getlatestcodacwhl.sh b/scripts/wheels/getlatestcodacwhl.sh index e4538944e..57d87717a 100644 --- a/scripts/wheels/getlatestcodacwhl.sh +++ b/scripts/wheels/getlatestcodacwhl.sh @@ -8,7 +8,7 @@ export LK_VER=`cat lk_codac_whl.ver` echo Latest: $LATEST_VER echo Last known: $LK_VER -if [[ -z "$LK_VER" || $(printf "%s\n%s" "$LATEST_VER" "$LK_VER" | sort -V | head -n 1) != "$LATEST_VER" && "$LATEST_VER" != "$LK_VER" ]] then +if [[ -z "$LK_VER" || $(printf "%s\n%s" "$LATEST_VER" "$LK_VER" | sort -V | head -n 1) != "$LATEST_VER" && "$LATEST_VER" != "$LK_VER" ]]; then echo $LATEST_VER is newer than $LK_VER echo $LATEST_VER>lk_codac_whl.ver && cat lk_codac_whl.ver | ./gencodacwhl.sh fi From 605233befa5d2f4c1b469de9db4173449590322a Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Thu, 11 Apr 2024 19:57:34 +0200 Subject: [PATCH 207/256] Update Python wheels upload documentation --- doc/doc/dev/info_dev.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/doc/dev/info_dev.rst b/doc/doc/dev/info_dev.rst index eecd2eb68..966363e31 100644 --- a/doc/doc/dev/info_dev.rst +++ b/doc/doc/dev/info_dev.rst @@ -214,8 +214,11 @@ Get all the *wheels* generated by GitHub Actions (the generated release needs to .. code-block:: bash + sudo apt install curl wget sed cd scripts/wheels + chmod a+x *.sh ./getlatestcodacwhl.sh + rm lk_codac_whl.ver # Optional, contain the version number that was last downloaded. Upload the *wheels* on PyPi: From a83fa3a17e6dca19b0c30de3362dcce651b0eca4 Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Sat, 6 Apr 2024 19:35:38 +0200 Subject: [PATCH 208/256] Solve missing version info (from VersionFromGit) when using Codac with CMake on Linux ARM computers --- .github/workflows/dockermatrix.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/dockermatrix.yml b/.github/workflows/dockermatrix.yml index 9a16e78f2..c34675223 100644 --- a/.github/workflows/dockermatrix.yml +++ b/.github/workflows/dockermatrix.yml @@ -48,7 +48,8 @@ jobs: docker run --rm --privileged multiarch/qemu-user-static --reset -p yes if: (matrix.cfg.arch!='x86_64')&&(matrix.cfg.arch!='i386') - run: | - docker run ${{ matrix.cfg.docker_flags }} -i -v "${PWD}/..:${PWD}/.." ${{ matrix.cfg.img }} /bin/bash -c "uname -a ; cat /etc/os-release ; cd ${PWD} && pwd && lsb_release -a ; \ + docker run ${{ matrix.cfg.docker_flags }} -i -v "${PWD}/..:${PWD}/.." ${{ matrix.cfg.img }} /bin/bash -c "uname -a ; cat /etc/os-release ; lsb_release -a ; cd ${PWD} && pwd && \ + git config --global --add safe.directory ${PWD} && \ if [ ${{ matrix.cfg.deb }} = true ]; then \ sudo apt-get -q update --allow-releaseinfo-change ; sudo apt-get -y install libeigen3-dev dpkg-dev || true && \ wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20240224/libibex-dev-2.8.9.20240224-0${{ matrix.cfg.runtime }}0_${{ matrix.cfg.arch }}.deb --no-check-certificate -nv && \ From d814dcf3acf45a497f065a4e0330286d04693c9a Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Mon, 1 Apr 2024 22:23:44 +0200 Subject: [PATCH 209/256] Temporary scripts to be able to manually build Codac on specific platforms --- .../temporary/gennewcodacmanylinux2014_aarch64.sh | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 packages/temporary/gennewcodacmanylinux2014_aarch64.sh diff --git a/packages/temporary/gennewcodacmanylinux2014_aarch64.sh b/packages/temporary/gennewcodacmanylinux2014_aarch64.sh new file mode 100644 index 000000000..018e711db --- /dev/null +++ b/packages/temporary/gennewcodacmanylinux2014_aarch64.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +# Need to login in Docker Desktop before...? + +mkdir -p ~/Downloads/newcodac + +cd ~/Downloads/newcodac +rm -Rf codac* +git clone https://github.com/lebarsfa/codac +cd codac +git submodule init +git submodule update +chmod a+x scripts/docker/build_pybinding.sh +docker pull lebarsfa/manylinux2014_aarch64-for-codac +docker run --rm -v `pwd`:/io lebarsfa/manylinux2014_aarch64-for-codac /io/scripts/docker/build_pybinding.sh From 88100aa99a5e521324983c44e24b5a5e112a8271 Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Sat, 6 Apr 2024 13:15:55 +0200 Subject: [PATCH 210/256] Update Docker documentation --- doc/doc/dev/info_dev.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/doc/dev/info_dev.rst b/doc/doc/dev/info_dev.rst index 966363e31..ad90ad24e 100644 --- a/doc/doc/dev/info_dev.rst +++ b/doc/doc/dev/info_dev.rst @@ -228,7 +228,7 @@ Upload the *wheels* on PyPi: .. rubric:: Testing the Linux *wheels* with Docker -In the :file:`codac` directory, test the Linux configuration locally using Docker: +In the :file:`codac` directory, test the x86_64 Linux configuration locally using Docker: .. code-block:: bash @@ -236,6 +236,8 @@ In the :file:`codac` directory, test the Linux configuration locally using Docke docker pull lebarsfa/manylinux2014_x86_64-for-codac docker run --rm -v `pwd`:/io lebarsfa/manylinux2014_x86_64-for-codac /io/scripts/docker/build_pybinding.sh +The same can be done for the ARM Linux configurations (preferably from a powerful ARM computer, such as a Mac with Apple Silicon), see `packages/temporary` folder. + .. rubric:: MATLAB compatibility Some operators or special functions need special attention: From c6f247c81c0cdda07e7bf40de3b4f0e2d04cb315 Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Thu, 11 Apr 2024 22:37:11 +0200 Subject: [PATCH 211/256] Improve OS detection on Linux --- .github/workflows/dockermatrix.yml | 2 +- .github/workflows/unixmatrix.yml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/dockermatrix.yml b/.github/workflows/dockermatrix.yml index c34675223..9e21823f7 100644 --- a/.github/workflows/dockermatrix.yml +++ b/.github/workflows/dockermatrix.yml @@ -81,7 +81,7 @@ jobs: if [ ${{ matrix.cfg.deb }} = true ]; then \ cd packages && \ chmod +x ./genlibcodac-dev.sh && \ - ./genlibcodac-dev.sh \$(lsb_release -is | tr [:upper:] [:lower:]) ${{ matrix.cfg.runtime }} \$(dpkg --print-architecture) $SOFTWARE_VERSION $DEBIAN_PACKAGE_REV $PACKAGE_REV && \ + ./genlibcodac-dev.sh \$(if [ -z \"\$(. /etc/os-release && echo \$UBUNTU_CODENAME)\" ]; then echo debian; else echo ubuntu; fi) ${{ matrix.cfg.runtime }} \$(dpkg --print-architecture) $SOFTWARE_VERSION $DEBIAN_PACKAGE_REV $PACKAGE_REV && \ cd .. && \ sudo dpkg -i libcodac-dev-$PACKAGE_VERSION\_\$(dpkg --print-architecture).deb ; \ else \ diff --git a/.github/workflows/unixmatrix.yml b/.github/workflows/unixmatrix.yml index adc52837a..5b2c01b93 100644 --- a/.github/workflows/unixmatrix.yml +++ b/.github/workflows/unixmatrix.yml @@ -122,7 +122,7 @@ jobs: if: runner.os=='Windows' - run: | # Replace these 2 lines by the next ones to test a specific binary package of IBEX. - #sudo sh -c 'echo "deb [trusted=yes] https://packages.ensta-bretagne.fr/`lsb_release --id -s | tr [:upper:] [:lower:]`/`lsb_release -cs` ./" > /etc/apt/sources.list.d/ensta-bretagne.list' + #sudo sh -c 'echo "deb [trusted=yes] https://packages.ensta-bretagne.fr/$(if [ -z "$(. /etc/os-release && echo $UBUNTU_CODENAME)" ]; then echo debian/$(. /etc/os-release && echo $VERSION_CODENAME); else echo ubuntu/$(. /etc/os-release && echo $UBUNTU_CODENAME); fi) ./" > /etc/apt/sources.list.d/ensta-bretagne.list' #sudo apt-get -q update ; sudo apt-get -y install libibex-dev libeigen3-dev dpkg-dev || true sudo apt-get -q update ; sudo apt-get -y install libeigen3-dev dpkg-dev || true wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20240224/libibex-dev-2.8.9.20240224-0${{ matrix.cfg.runtime }}0_${{ matrix.cfg.arch }}.deb --no-check-certificate -nv @@ -193,7 +193,7 @@ jobs: - run: | cd packages chmod +x ./genlibcodac-dev.sh - ./genlibcodac-dev.sh $(lsb_release -is | tr [:upper:] [:lower:]) ${{ matrix.cfg.runtime }} $(dpkg --print-architecture) $SOFTWARE_VERSION $DEBIAN_PACKAGE_REV $PACKAGE_REV + ./genlibcodac-dev.sh $(if [ -z "$(. /etc/os-release && echo $UBUNTU_CODENAME)" ]; then echo debian; else echo ubuntu; fi) ${{ matrix.cfg.runtime }} $(dpkg --print-architecture) $SOFTWARE_VERSION $DEBIAN_PACKAGE_REV $PACKAGE_REV cd .. sudo dpkg -i libcodac-dev-$PACKAGE_VERSION\_$(dpkg --print-architecture).deb shell: bash From c5de06a6e30d329309a0cbf9a21cd28afb415b98 Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Sun, 14 Apr 2024 18:15:35 +0200 Subject: [PATCH 212/256] Update workflows --- .github/workflows/dockermatrix.yml | 10 +++++++--- .github/workflows/unixmatrix.yml | 6 +++--- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/.github/workflows/dockermatrix.yml b/.github/workflows/dockermatrix.yml index 9e21823f7..b6376f880 100644 --- a/.github/workflows/dockermatrix.yml +++ b/.github/workflows/dockermatrix.yml @@ -18,8 +18,12 @@ jobs: - { img: 'lebarsfa/manylinux2014_x86_64-for-codac', shell: bash, arch: x86_64, bitness: 64, runtime: manylinux2014, cmake_flags: '-fPIC', desc: 'CentOS manylinux2014 x86_64' } - { img: 'lebarsfa/manylinux2014_aarch64-for-codac', shell: bash, arch: aarch64, bitness: 64, runtime: manylinux2014, cmake_flags: '-fPIC', docker_flags: '--platform linux/arm64', desc: 'CentOS manylinux2014 aarch64' } - { img: 'lebarsfa/pi-64:jammy-for-codac', shell: bash, arch: arm64, bitness: 64, runtime: jammy, cmake_flags: '-fPIC', deb: true, desc: 'Ubuntu 22.04 arm64' } + - { img: 'lebarsfa/pi-64:focal-for-codac', shell: bash, arch: arm64, bitness: 64, runtime: focal, cmake_flags: '-fPIC', deb: true, desc: 'Ubuntu 20.04 arm64' } - { img: 'lebarsfa/pi-64:bookworm-for-codac', shell: bash, arch: arm64, bitness: 64, runtime: bookworm, cmake_flags: '-fPIC', deb: true, desc: 'Debian Bookworm arm64' } + - { img: 'lebarsfa/pi-64:bullseye-for-codac', shell: bash, arch: arm64, bitness: 64, runtime: bullseye, cmake_flags: '-fPIC', deb: true, desc: 'Debian Bullseye arm64' } + - { img: 'lebarsfa/pi-64:buster-for-codac', shell: bash, arch: arm64, bitness: 64, runtime: buster, cmake_flags: '-fPIC', deb: true, desc: 'Debian Buster arm64' } - { img: 'lebarsfa/pi:bookworm-for-codac', shell: bash, arch: armhf, bitness: 32, runtime: bookworm, cmake_flags: '-fPIC', deb: true, desc: 'Raspbian Bookworm armv6hf' } + - { img: 'lebarsfa/pi:bullseye-for-codac', shell: bash, arch: armhf, bitness: 32, runtime: bullseye, cmake_flags: '-fPIC', deb: true, desc: 'Raspbian Bullseye armv6hf' } - { img: 'lebarsfa/pi:buster-for-codac', shell: bash, arch: armhf, bitness: 32, runtime: buster, cmake_flags: '-fPIC', deb: true, desc: 'Raspbian Buster armv6hf' } name: ${{ matrix.cfg.desc }} steps: @@ -52,9 +56,9 @@ jobs: git config --global --add safe.directory ${PWD} && \ if [ ${{ matrix.cfg.deb }} = true ]; then \ sudo apt-get -q update --allow-releaseinfo-change ; sudo apt-get -y install libeigen3-dev dpkg-dev || true && \ - wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20240224/libibex-dev-2.8.9.20240224-0${{ matrix.cfg.runtime }}0_${{ matrix.cfg.arch }}.deb --no-check-certificate -nv && \ - sudo dpkg -i libibex-dev-2.8.9.20240224-0${{ matrix.cfg.runtime }}0_${{ matrix.cfg.arch }}.deb && \ - rm -Rf libibex-dev-2.8.9.20240224-0${{ matrix.cfg.runtime }}0_${{ matrix.cfg.arch }}.deb ; \ + wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20240224/libibex-dev-2.8.9.20240224-0${{ matrix.cfg.runtime }}0_\$(dpkg --print-architecture).deb --no-check-certificate -nv && \ + sudo dpkg -i libibex-dev-2.8.9.20240224-0${{ matrix.cfg.runtime }}0_\$(dpkg --print-architecture).deb && \ + rm -Rf libibex-dev-2.8.9.20240224-0${{ matrix.cfg.runtime }}0_\$(dpkg --print-architecture).deb ; \ else \ wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20240224/ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip --no-check-certificate -nv && \ unzip -q ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip && \ diff --git a/.github/workflows/unixmatrix.yml b/.github/workflows/unixmatrix.yml index 5b2c01b93..1d021aa28 100644 --- a/.github/workflows/unixmatrix.yml +++ b/.github/workflows/unixmatrix.yml @@ -125,9 +125,9 @@ jobs: #sudo sh -c 'echo "deb [trusted=yes] https://packages.ensta-bretagne.fr/$(if [ -z "$(. /etc/os-release && echo $UBUNTU_CODENAME)" ]; then echo debian/$(. /etc/os-release && echo $VERSION_CODENAME); else echo ubuntu/$(. /etc/os-release && echo $UBUNTU_CODENAME); fi) ./" > /etc/apt/sources.list.d/ensta-bretagne.list' #sudo apt-get -q update ; sudo apt-get -y install libibex-dev libeigen3-dev dpkg-dev || true sudo apt-get -q update ; sudo apt-get -y install libeigen3-dev dpkg-dev || true - wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20240224/libibex-dev-2.8.9.20240224-0${{ matrix.cfg.runtime }}0_${{ matrix.cfg.arch }}.deb --no-check-certificate -nv - sudo dpkg -i libibex-dev-2.8.9.20240224-0${{ matrix.cfg.runtime }}0_${{ matrix.cfg.arch }}.deb - rm -Rf libibex-dev-2.8.9.20240224-0${{ matrix.cfg.runtime }}0_${{ matrix.cfg.arch }}.deb + wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20240224/libibex-dev-2.8.9.20240224-0${{ matrix.cfg.runtime }}0_$(dpkg --print-architecture).deb --no-check-certificate -nv + sudo dpkg -i libibex-dev-2.8.9.20240224-0${{ matrix.cfg.runtime }}0_$(dpkg --print-architecture).deb + rm -Rf libibex-dev-2.8.9.20240224-0${{ matrix.cfg.runtime }}0_$(dpkg --print-architecture).deb shell: bash if: matrix.cfg.deb==true - run: | From 8305295dc4c59e6acddafd3e05b9af6f4e9d5e96 Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Sun, 7 Apr 2024 17:54:34 +0200 Subject: [PATCH 213/256] Update 01-installation-full-linux.rst --- .../install/01-installation-full-linux.rst | 23 +++++++++++++------ 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/doc/doc/install/01-installation-full-linux.rst b/doc/doc/install/01-installation-full-linux.rst index d69113f5a..e1ede2b05 100644 --- a/doc/doc/install/01-installation-full-linux.rst +++ b/doc/doc/install/01-installation-full-linux.rst @@ -7,14 +7,14 @@ Installing Codac on Linux for C++ use ##################################### -Install from package (latest release, for Ubuntu amd64) +Install from package (latest release, for Ubuntu (amd64, arm64), Debian (arm64, armhf) and possibly others) --------------------------------------------------------- A Debian package is available for the last release |version| of the library: .. code-block:: bash - sudo sh -c 'echo "deb [trusted=yes] https://packages.ensta-bretagne.fr/`lsb_release --id -s | tr [:upper:] [:lower:]`/`lsb_release -cs` ./" > /etc/apt/sources.list.d/ensta-bretagne.list' + sudo sh -c 'echo "deb [trusted=yes] https://packages.ensta-bretagne.fr/$(if [ -z "$(. /etc/os-release && echo $UBUNTU_CODENAME)" ]; then echo debian/$(. /etc/os-release && echo $VERSION_CODENAME); else echo ubuntu/$(. /etc/os-release && echo $UBUNTU_CODENAME); fi) ./" > /etc/apt/sources.list.d/ensta-bretagne.list' sudo apt update sudo apt install libcodac-dev @@ -22,12 +22,23 @@ Then, check your installation `with the instructions of this page <03-start-cpp- .. warning:: - | **URL changed**: - | Please update :code:`/etc/apt/sources.list.d/ensta-bretagne.list` as above. + | **URL changed**: Please uninstall before. .. note:: - For a Raspberry Pi running Raspbian Buster 32 bit, download and extract ``codac_standalone_armv6hf_buster.zip`` from ``_, then in the ``example`` folder run: + To uninstall Codac, you might want to do the following: + + .. code-block:: bash + + sudo apt remove libcodac-dev libibex-dev + sudo rm -f /etc/apt/sources.list.d/ensta-bretagne.list + sudo apt update + + Note also that ``libeigen3-dev`` might have been installed as a dependency of Codac but might be also used by other software. You might want to keep it. + +.. note:: + + Standalone archives exist also for all the supported configurations, e.g. for a Raspberry Pi running Raspberry Pi OS Bookworm 32 bit, download and extract ``codac_standalone_armhf_bookworm.zip`` from ``_, then in the ``example`` folder run: .. code-block:: bash @@ -35,8 +46,6 @@ Then, check your installation `with the instructions of this page <03-start-cpp- and check that "My first tube:Tube [0, 10]" appears. - Similar archives exist also for all the supported configurations. - Install from sources (latest development) ----------------------------------------- From db1c744feade7238329f67da927da88d32582c4b Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Sat, 6 Apr 2024 19:35:42 +0200 Subject: [PATCH 214/256] Create gennewcodacbuster_armhf.sh --- packages/temporary/gennewcodacbuster_armhf.sh | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 packages/temporary/gennewcodacbuster_armhf.sh diff --git a/packages/temporary/gennewcodacbuster_armhf.sh b/packages/temporary/gennewcodacbuster_armhf.sh new file mode 100644 index 000000000..2d079fbc6 --- /dev/null +++ b/packages/temporary/gennewcodacbuster_armhf.sh @@ -0,0 +1,43 @@ +#!/bin/bash + +# Need to login in Docker Desktop before...? + +mkdir -p ~/Downloads/newcodac + +cd ~/Downloads/newcodac +rm -Rf codac* +git clone https://github.com/lebarsfa/codac +cd codac +git submodule init +git submodule update +docker pull lebarsfa/pi:buster-for-codac +docker run --rm -v `pwd`:/io lebarsfa/pi:buster-for-codac /bin/bash -c "uname -a ; cat /etc/os-release ; lsb_release -a ; \ +python3 -m pip install --upgrade auditwheel==5.1.2 && \ +sudo apt-get -q update --allow-releaseinfo-change ; sudo apt-get -y install libeigen3-dev python3-dev patchelf || true && \ +wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20240224/ibex_armhf_buster.zip --no-check-certificate -nv && \ +unzip -q ibex_armhf_buster.zip && \ +rm -Rf ibex_armhf_buster.zip && \ +sudo cp -Rf ibex/* /usr/local/ && \ +\ +git config --global --add safe.directory /io && \ +cd /io && \ +\ +python3 -m pip install --upgrade pip && \ +mkdir -p build_dir && cd build_dir && \ +cmake -E env CXXFLAGS=\"-fPIC\" CFLAGS=\"-fPIC\" cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTS=ON -DWITH_TUBE_TREE=OFF -DWITH_CAPD=OFF -DWITH_PYTHON=ON .. && \ +make -j4 && \ +\ +make test ARGS=\"-V --output-on-failure\" && \ +echo \"start of Testing/Temporary/LastTest.log\" && \ +cat Testing/Temporary/LastTest.log && \ +echo \"end of Testing/Temporary/LastTest.log\" && \ +make pip_package && \ +echo \"copy wheel and clean build_dir\" && \ +for whl in *.whl; do \ +auditwheel repair \"\$whl\" -w /io/wheelhouse/ ; \ +done && \ +\ +python3 -m pip install numpy && \ +python3 -m pip install codac --no-deps --no-index -f /io/wheelhouse && \ +(cd \"\$HOME\"; python3 -m unittest discover codac.tests) && \ +cd /io" From 08f373b21e38ea940b79b379bfa4c15998406029 Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Thu, 18 Apr 2024 00:02:44 +0200 Subject: [PATCH 215/256] Test specific IBEX version --- .github/workflows/dockermatrix.yml | 8 ++++---- .github/workflows/macosmatrix.yml | 2 +- .github/workflows/unixmatrix.yml | 14 +++++++------- .github/workflows/vcmatrix.yml | 6 +++--- packages/temporary/gennewcodacbuster_armhf.sh | 2 +- scripts/docker/build_pybinding.sh | 2 +- 6 files changed, 17 insertions(+), 17 deletions(-) diff --git a/.github/workflows/dockermatrix.yml b/.github/workflows/dockermatrix.yml index b6376f880..d29394d1a 100644 --- a/.github/workflows/dockermatrix.yml +++ b/.github/workflows/dockermatrix.yml @@ -56,11 +56,11 @@ jobs: git config --global --add safe.directory ${PWD} && \ if [ ${{ matrix.cfg.deb }} = true ]; then \ sudo apt-get -q update --allow-releaseinfo-change ; sudo apt-get -y install libeigen3-dev dpkg-dev || true && \ - wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20240224/libibex-dev-2.8.9.20240224-0${{ matrix.cfg.runtime }}0_\$(dpkg --print-architecture).deb --no-check-certificate -nv && \ - sudo dpkg -i libibex-dev-2.8.9.20240224-0${{ matrix.cfg.runtime }}0_\$(dpkg --print-architecture).deb && \ - rm -Rf libibex-dev-2.8.9.20240224-0${{ matrix.cfg.runtime }}0_\$(dpkg --print-architecture).deb ; \ + wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20240417/libibex-dev-2.8.9.20240417-0${{ matrix.cfg.runtime }}0_\$(dpkg --print-architecture).deb --no-check-certificate -nv && \ + sudo dpkg -i libibex-dev-2.8.9.20240417-0${{ matrix.cfg.runtime }}0_\$(dpkg --print-architecture).deb && \ + rm -Rf libibex-dev-2.8.9.20240417-0${{ matrix.cfg.runtime }}0_\$(dpkg --print-architecture).deb ; \ else \ - wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20240224/ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip --no-check-certificate -nv && \ + wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20240417/ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip --no-check-certificate -nv && \ unzip -q ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip && \ rm -Rf ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip && \ sudo cp -Rf ibex/* /usr/ ; \ diff --git a/.github/workflows/macosmatrix.yml b/.github/workflows/macosmatrix.yml index 804506aa9..3c312a36a 100644 --- a/.github/workflows/macosmatrix.yml +++ b/.github/workflows/macosmatrix.yml @@ -57,7 +57,7 @@ jobs: - run: brew install graphviz ; wget https://github.com/Homebrew/homebrew-core/raw/d2267b9f2ad247bc9c8273eb755b39566a474a70/Formula/doxygen.rb ; brew reinstall ./doxygen.rb ; brew pin doxygen ; python -m pip install --upgrade pip ; pip install --upgrade wheel setuptools sphinx breathe sphinx_rtd_theme sphinx-tabs sphinx-issues sphinx-reredirects if: runner.os=='macOS' - run: | - wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20240224/ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip --no-check-certificate -nv + wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20240417/ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip --no-check-certificate -nv unzip -q ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip rm -Rf ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip sudo cp -Rf ibex/* /usr/local/ diff --git a/.github/workflows/unixmatrix.yml b/.github/workflows/unixmatrix.yml index 1d021aa28..30b45aa34 100644 --- a/.github/workflows/unixmatrix.yml +++ b/.github/workflows/unixmatrix.yml @@ -116,23 +116,23 @@ jobs: if: (matrix.cfg.runtime=='mingw13') - run: | choco install -y -r --no-progress eigen --version=3.4.0.20240224 ${{ matrix.cfg.choco_flags }} - wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20240224/ibex.2.8.9.20240224.nupkg --no-check-certificate -nv - choco install -y -r --no-progress --ignore-dependencies -s . ibex --version=2.8.9.20240224 ${{ matrix.cfg.choco_flags }} --params "'/url:https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20240224/ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip'" - del /f /q ibex.2.8.9.20240224.nupkg + wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20240417/ibex.2.8.9.20240417.nupkg --no-check-certificate -nv + choco install -y -r --no-progress --ignore-dependencies -s . ibex --version=2.8.9.20240417 ${{ matrix.cfg.choco_flags }} --params "'/url:https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20240417/ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip'" + del /f /q ibex.2.8.9.20240417.nupkg if: runner.os=='Windows' - run: | # Replace these 2 lines by the next ones to test a specific binary package of IBEX. #sudo sh -c 'echo "deb [trusted=yes] https://packages.ensta-bretagne.fr/$(if [ -z "$(. /etc/os-release && echo $UBUNTU_CODENAME)" ]; then echo debian/$(. /etc/os-release && echo $VERSION_CODENAME); else echo ubuntu/$(. /etc/os-release && echo $UBUNTU_CODENAME); fi) ./" > /etc/apt/sources.list.d/ensta-bretagne.list' #sudo apt-get -q update ; sudo apt-get -y install libibex-dev libeigen3-dev dpkg-dev || true sudo apt-get -q update ; sudo apt-get -y install libeigen3-dev dpkg-dev || true - wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20240224/libibex-dev-2.8.9.20240224-0${{ matrix.cfg.runtime }}0_$(dpkg --print-architecture).deb --no-check-certificate -nv - sudo dpkg -i libibex-dev-2.8.9.20240224-0${{ matrix.cfg.runtime }}0_$(dpkg --print-architecture).deb - rm -Rf libibex-dev-2.8.9.20240224-0${{ matrix.cfg.runtime }}0_$(dpkg --print-architecture).deb + wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20240417/libibex-dev-2.8.9.20240417-0${{ matrix.cfg.runtime }}0_$(dpkg --print-architecture).deb --no-check-certificate -nv + sudo dpkg -i libibex-dev-2.8.9.20240417-0${{ matrix.cfg.runtime }}0_$(dpkg --print-architecture).deb + rm -Rf libibex-dev-2.8.9.20240417-0${{ matrix.cfg.runtime }}0_$(dpkg --print-architecture).deb shell: bash if: matrix.cfg.deb==true - run: | brew install eigen - wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20240224/ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip --no-check-certificate -nv + wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20240417/ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip --no-check-certificate -nv unzip -q ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip rm -Rf ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip sudo cp -Rf ibex/* /usr/local/ diff --git a/.github/workflows/vcmatrix.yml b/.github/workflows/vcmatrix.yml index 300f985f4..6727cea0b 100644 --- a/.github/workflows/vcmatrix.yml +++ b/.github/workflows/vcmatrix.yml @@ -59,9 +59,9 @@ jobs: - run: wget https://packages.ensta-bretagne.fr/choco/doxygen.install.1.9.6.nupkg --no-check-certificate -nv & choco upgrade -y -r --no-progress -s . doxygen.install --version=1.9.6 & del /f /q doxygen.install.1.9.6.nupkg & choco install -y -r --no-progress graphviz & pip install --upgrade wheel setuptools sphinx breathe sphinx-issues sphinx-tabs sphinx_rtd_theme sphinx-reredirects if: runner.os=='Windows' - run: | - wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20240224/ibex.2.8.9.20240224.nupkg --no-check-certificate -nv - choco install -y -r --no-progress --ignore-dependencies -s . ibex --version=2.8.9.20240224 ${{ matrix.cfg.choco_flags }} --params "'/url:https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20240224/ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip'" - del /f /q ibex.2.8.9.20240224.nupkg + wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20240417/ibex.2.8.9.20240417.nupkg --no-check-certificate -nv + choco install -y -r --no-progress --ignore-dependencies -s . ibex --version=2.8.9.20240417 ${{ matrix.cfg.choco_flags }} --params "'/url:https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20240417/ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip'" + del /f /q ibex.2.8.9.20240417.nupkg - run: | mkdir build ; cd build cmake -E env CXXFLAGS=" /wd4267 /wd4244 /wd4305 /wd4996" CFLAGS=" /wd4267 /wd4244 /wd4305 /wd4996" cmake ${{ matrix.cfg.cmake_params }} -D CMAKE_INSTALL_PREFIX="../codac" -D WITH_PYTHON=ON .. diff --git a/packages/temporary/gennewcodacbuster_armhf.sh b/packages/temporary/gennewcodacbuster_armhf.sh index 2d079fbc6..f9610fa06 100644 --- a/packages/temporary/gennewcodacbuster_armhf.sh +++ b/packages/temporary/gennewcodacbuster_armhf.sh @@ -14,7 +14,7 @@ docker pull lebarsfa/pi:buster-for-codac docker run --rm -v `pwd`:/io lebarsfa/pi:buster-for-codac /bin/bash -c "uname -a ; cat /etc/os-release ; lsb_release -a ; \ python3 -m pip install --upgrade auditwheel==5.1.2 && \ sudo apt-get -q update --allow-releaseinfo-change ; sudo apt-get -y install libeigen3-dev python3-dev patchelf || true && \ -wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20240224/ibex_armhf_buster.zip --no-check-certificate -nv && \ +wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20240417/ibex_armhf_buster.zip --no-check-certificate -nv && \ unzip -q ibex_armhf_buster.zip && \ rm -Rf ibex_armhf_buster.zip && \ sudo cp -Rf ibex/* /usr/local/ && \ diff --git a/scripts/docker/build_pybinding.sh b/scripts/docker/build_pybinding.sh index 46eb240d3..5775e4a76 100755 --- a/scripts/docker/build_pybinding.sh +++ b/scripts/docker/build_pybinding.sh @@ -2,7 +2,7 @@ set -e -x -wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20240224/ibex_$(uname -m)_manylinux2014.zip --no-check-certificate -nv +wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20240417/ibex_$(uname -m)_manylinux2014.zip --no-check-certificate -nv unzip -q ibex_$(uname -m)_manylinux2014.zip rm -Rf ibex_$(uname -m)_manylinux2014.zip sudo cp -Rf ibex/* /usr/local/ From f04651393909d3bd8aadcb92272f56fc97462199 Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Wed, 17 Apr 2024 23:50:36 +0200 Subject: [PATCH 216/256] Add manylinux2014 aarch64 wheels generation --- .github/workflows/dockercentos.yml | 18 ++++++++++++++++-- scripts/wheels/gencodacwhl.sh | 4 ++-- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/.github/workflows/dockercentos.yml b/.github/workflows/dockercentos.yml index 7b4d5ee08..4dcd600e9 100644 --- a/.github/workflows/dockercentos.yml +++ b/.github/workflows/dockercentos.yml @@ -7,17 +7,31 @@ on: jobs: dockercentos: - name: CentOS Docker runs-on: ubuntu-latest + defaults: + run: + shell: ${{ matrix.cfg.shell }} + strategy: + fail-fast: false + matrix: + cfg: + - { img: 'lebarsfa/manylinux2014_x86_64-for-codac', shell: bash, arch: x86_64, bitness: 64, runtime: manylinux2014, desc: 'CentOS manylinux2014 x86_64' } + - { img: 'lebarsfa/manylinux2014_aarch64-for-codac', shell: bash, arch: aarch64, bitness: 64, runtime: manylinux2014, docker_flags: '--platform linux/arm64', desc: 'CentOS manylinux2014 aarch64' } + name: ${{ matrix.cfg.desc }} steps: - uses: actions/checkout@v4 with: submodules: true fetch-depth: 0 clean: false + - run: | + sudo apt-get -y install qemu binfmt-support qemu-user-static || true + #docker run --rm --privileged multiarch/qemu-user-static:register --reset + docker run --rm --privileged multiarch/qemu-user-static --reset -p yes + if: (matrix.cfg.arch!='x86_64')&&(matrix.cfg.arch!='i386') - run: | chmod a+x scripts/docker/build_pybinding.sh - docker run --rm -v `pwd`:/io lebarsfa/manylinux2014_x86_64-for-codac /io/scripts/docker/build_pybinding.sh + docker run ${{ matrix.cfg.docker_flags }} --rm -v `pwd`:/io ${{ matrix.cfg.img }} /io/scripts/docker/build_pybinding.sh ls wheelhouse - uses: xresloader/upload-to-github-release@v1 env: diff --git a/scripts/wheels/gencodacwhl.sh b/scripts/wheels/gencodacwhl.sh index 14ab8af57..f58cf7821 100644 --- a/scripts/wheels/gencodacwhl.sh +++ b/scripts/wheels/gencodacwhl.sh @@ -9,13 +9,13 @@ CPCFG=m-macosx_10_14_x86_64 PY_V_MIN=6 ; rm -Rf codac-$PACKAGE_VERSION-cp$PY_V_M CPCFG=m-macosx_10_9_x86_64 PY_V_MIN=7 ; rm -Rf codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl ; wget https://github.com/codac-team/codac/releases/download/v$PACKAGE_VERSION/codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl --no-check-certificate -nv for PY_V_MIN in 6 7; do - for CPCFG in "m-macosx_11_0_arm64" "m-manylinux_2_17_x86_64.manylinux2014_x86_64" "m-win32" "m-win_amd64"; do + for CPCFG in "m-macosx_11_0_arm64" "-manylinux_2_17_aarch64.manylinux2014_aarch64" "m-manylinux_2_17_x86_64.manylinux2014_x86_64" "m-win32" "m-win_amd64"; do rm -Rf codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl ; wget https://github.com/codac-team/codac/releases/download/v$PACKAGE_VERSION/codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl --no-check-certificate -nv done done for PY_V_MIN in 8 9 10 11 12; do - for CPCFG in "-macosx_10_9_x86_64" "-macosx_11_0_arm64" "-manylinux_2_17_x86_64.manylinux2014_x86_64" "-win32" "-win_amd64"; do + for CPCFG in "-macosx_10_9_x86_64" "-macosx_11_0_arm64" "-manylinux_2_17_aarch64.manylinux2014_aarch64" "-manylinux_2_17_x86_64.manylinux2014_x86_64" "-win32" "-win_amd64"; do rm -Rf codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl ; wget https://github.com/codac-team/codac/releases/download/v$PACKAGE_VERSION/codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl --no-check-certificate -nv done done From 8d7a13cba1d6c31be23aaafb27c2261067d6395b Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Thu, 18 Apr 2024 15:40:38 +0200 Subject: [PATCH 217/256] Disable manylinux2014 aarch64 wheels generation since it is too long on GitHub hosted runners --- .github/workflows/dockercentos.yml | 2 +- scripts/wheels/gencodacwhl.sh | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/dockercentos.yml b/.github/workflows/dockercentos.yml index 4dcd600e9..9992b8ee9 100644 --- a/.github/workflows/dockercentos.yml +++ b/.github/workflows/dockercentos.yml @@ -16,7 +16,7 @@ jobs: matrix: cfg: - { img: 'lebarsfa/manylinux2014_x86_64-for-codac', shell: bash, arch: x86_64, bitness: 64, runtime: manylinux2014, desc: 'CentOS manylinux2014 x86_64' } - - { img: 'lebarsfa/manylinux2014_aarch64-for-codac', shell: bash, arch: aarch64, bitness: 64, runtime: manylinux2014, docker_flags: '--platform linux/arm64', desc: 'CentOS manylinux2014 aarch64' } + # - { img: 'lebarsfa/manylinux2014_aarch64-for-codac', shell: bash, arch: aarch64, bitness: 64, runtime: manylinux2014, docker_flags: '--platform linux/arm64', desc: 'CentOS manylinux2014 aarch64' } name: ${{ matrix.cfg.desc }} steps: - uses: actions/checkout@v4 diff --git a/scripts/wheels/gencodacwhl.sh b/scripts/wheels/gencodacwhl.sh index f58cf7821..14ab8af57 100644 --- a/scripts/wheels/gencodacwhl.sh +++ b/scripts/wheels/gencodacwhl.sh @@ -9,13 +9,13 @@ CPCFG=m-macosx_10_14_x86_64 PY_V_MIN=6 ; rm -Rf codac-$PACKAGE_VERSION-cp$PY_V_M CPCFG=m-macosx_10_9_x86_64 PY_V_MIN=7 ; rm -Rf codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl ; wget https://github.com/codac-team/codac/releases/download/v$PACKAGE_VERSION/codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl --no-check-certificate -nv for PY_V_MIN in 6 7; do - for CPCFG in "m-macosx_11_0_arm64" "-manylinux_2_17_aarch64.manylinux2014_aarch64" "m-manylinux_2_17_x86_64.manylinux2014_x86_64" "m-win32" "m-win_amd64"; do + for CPCFG in "m-macosx_11_0_arm64" "m-manylinux_2_17_x86_64.manylinux2014_x86_64" "m-win32" "m-win_amd64"; do rm -Rf codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl ; wget https://github.com/codac-team/codac/releases/download/v$PACKAGE_VERSION/codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl --no-check-certificate -nv done done for PY_V_MIN in 8 9 10 11 12; do - for CPCFG in "-macosx_10_9_x86_64" "-macosx_11_0_arm64" "-manylinux_2_17_aarch64.manylinux2014_aarch64" "-manylinux_2_17_x86_64.manylinux2014_x86_64" "-win32" "-win_amd64"; do + for CPCFG in "-macosx_10_9_x86_64" "-macosx_11_0_arm64" "-manylinux_2_17_x86_64.manylinux2014_x86_64" "-win32" "-win_amd64"; do rm -Rf codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl ; wget https://github.com/codac-team/codac/releases/download/v$PACKAGE_VERSION/codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl --no-check-certificate -nv done done From 0fe640b1a9ba4a7d62d7ada0b1c48a1dae0b1bc0 Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Mon, 22 Apr 2024 18:48:24 +0200 Subject: [PATCH 218/256] Add --prefer-binary when installing numpy --- .github/workflows/macosmatrix.yml | 2 +- .github/workflows/vcmatrix.yml | 2 +- scripts/docker/build_pybinding.sh | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/macosmatrix.yml b/.github/workflows/macosmatrix.yml index 3c312a36a..0519b4ce4 100644 --- a/.github/workflows/macosmatrix.yml +++ b/.github/workflows/macosmatrix.yml @@ -69,7 +69,7 @@ jobs: cmake --build . --config Release --target pip_package ; cp `ls *.whl` ../`ls *.whl | sed "s/py3-none-any/cp${{ matrix.cfg.py_v_maj }}${{ matrix.cfg.py_v_min }}-cp${{ matrix.cfg.py_v_maj }}${{ matrix.cfg.py_v_min }}${{ matrix.cfg.cpcfg }}/"` cd .. shell: bash - - run: pip install *.whl ; python -c "import sys; print(sys.version)" ; python examples/tuto/01_getting_started/01_getting_started.py ; pip install numpy ; python -m unittest discover codac.tests + - run: pip install *.whl ; python -c "import sys; print(sys.version)" ; python examples/tuto/01_getting_started/01_getting_started.py ; pip install numpy --prefer-binary ; python -m unittest discover codac.tests shell: bash if: matrix.cfg.cross!=true - uses: xresloader/upload-to-github-release@v1 diff --git a/.github/workflows/vcmatrix.yml b/.github/workflows/vcmatrix.yml index 6727cea0b..269eef29f 100644 --- a/.github/workflows/vcmatrix.yml +++ b/.github/workflows/vcmatrix.yml @@ -69,7 +69,7 @@ jobs: cmake --build . --config Release --target pip_package ; cp `ls *.whl` ../`ls *.whl | sed "s/py3-none-any/cp${{ matrix.cfg.py_v_maj }}${{ matrix.cfg.py_v_min }}-cp${{ matrix.cfg.py_v_maj }}${{ matrix.cfg.py_v_min }}${{ matrix.cfg.cpcfg }}/"` cd .. shell: bash - - run: pip install *.whl ; python -c "import sys; print(sys.version)" ; python examples/tuto/01_getting_started/01_getting_started.py ; pip install numpy ; python -m unittest discover codac.tests + - run: pip install *.whl ; python -c "import sys; print(sys.version)" ; python examples/tuto/01_getting_started/01_getting_started.py ; pip install numpy --prefer-binary ; python -m unittest discover codac.tests shell: bash - uses: xresloader/upload-to-github-release@v1 env: diff --git a/scripts/docker/build_pybinding.sh b/scripts/docker/build_pybinding.sh index 5775e4a76..054887d14 100755 --- a/scripts/docker/build_pybinding.sh +++ b/scripts/docker/build_pybinding.sh @@ -30,7 +30,7 @@ for PYBIN in /opt/python/cp3*/bin; do auditwheel repair "$whl" -w /io/wheelhouse/ done - "${PYBIN}/python" -m pip install numpy + "${PYBIN}/python" -m pip install numpy --prefer-binary "${PYBIN}/python" -m pip install codac --no-deps --no-index -f /io/wheelhouse (cd "$HOME"; "${PYBIN}/python" -m unittest discover codac.tests) cd /io From deb1d2dbc7cd0072bc9748d917b06cf1243b30e1 Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Fri, 19 Apr 2024 00:46:18 +0200 Subject: [PATCH 219/256] Update temporary scripts to be able to manually build Codac on specific platforms --- packages/temporary/gennewcodacbuster_armhf.sh | 43 ----------- .../gennewcodacmanylinux2014_aarch64.sh | 9 ++- packages/temporary/gennewcodacpi_armhf.sh | 73 +++++++++++++++++++ 3 files changed, 80 insertions(+), 45 deletions(-) delete mode 100644 packages/temporary/gennewcodacbuster_armhf.sh create mode 100644 packages/temporary/gennewcodacpi_armhf.sh diff --git a/packages/temporary/gennewcodacbuster_armhf.sh b/packages/temporary/gennewcodacbuster_armhf.sh deleted file mode 100644 index f9610fa06..000000000 --- a/packages/temporary/gennewcodacbuster_armhf.sh +++ /dev/null @@ -1,43 +0,0 @@ -#!/bin/bash - -# Need to login in Docker Desktop before...? - -mkdir -p ~/Downloads/newcodac - -cd ~/Downloads/newcodac -rm -Rf codac* -git clone https://github.com/lebarsfa/codac -cd codac -git submodule init -git submodule update -docker pull lebarsfa/pi:buster-for-codac -docker run --rm -v `pwd`:/io lebarsfa/pi:buster-for-codac /bin/bash -c "uname -a ; cat /etc/os-release ; lsb_release -a ; \ -python3 -m pip install --upgrade auditwheel==5.1.2 && \ -sudo apt-get -q update --allow-releaseinfo-change ; sudo apt-get -y install libeigen3-dev python3-dev patchelf || true && \ -wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20240417/ibex_armhf_buster.zip --no-check-certificate -nv && \ -unzip -q ibex_armhf_buster.zip && \ -rm -Rf ibex_armhf_buster.zip && \ -sudo cp -Rf ibex/* /usr/local/ && \ -\ -git config --global --add safe.directory /io && \ -cd /io && \ -\ -python3 -m pip install --upgrade pip && \ -mkdir -p build_dir && cd build_dir && \ -cmake -E env CXXFLAGS=\"-fPIC\" CFLAGS=\"-fPIC\" cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTS=ON -DWITH_TUBE_TREE=OFF -DWITH_CAPD=OFF -DWITH_PYTHON=ON .. && \ -make -j4 && \ -\ -make test ARGS=\"-V --output-on-failure\" && \ -echo \"start of Testing/Temporary/LastTest.log\" && \ -cat Testing/Temporary/LastTest.log && \ -echo \"end of Testing/Temporary/LastTest.log\" && \ -make pip_package && \ -echo \"copy wheel and clean build_dir\" && \ -for whl in *.whl; do \ -auditwheel repair \"\$whl\" -w /io/wheelhouse/ ; \ -done && \ -\ -python3 -m pip install numpy && \ -python3 -m pip install codac --no-deps --no-index -f /io/wheelhouse && \ -(cd \"\$HOME\"; python3 -m unittest discover codac.tests) && \ -cd /io" diff --git a/packages/temporary/gennewcodacmanylinux2014_aarch64.sh b/packages/temporary/gennewcodacmanylinux2014_aarch64.sh index 018e711db..2c7863b45 100644 --- a/packages/temporary/gennewcodacmanylinux2014_aarch64.sh +++ b/packages/temporary/gennewcodacmanylinux2014_aarch64.sh @@ -1,15 +1,20 @@ #!/bin/bash -# Need to login in Docker Desktop before...? +set -e -x mkdir -p ~/Downloads/newcodac cd ~/Downloads/newcodac rm -Rf codac* git clone https://github.com/lebarsfa/codac +#git clone https://github.com/codac-team/codac cd codac git submodule init git submodule update -chmod a+x scripts/docker/build_pybinding.sh docker pull lebarsfa/manylinux2014_aarch64-for-codac + +chmod a+x scripts/docker/build_pybinding.sh docker run --rm -v `pwd`:/io lebarsfa/manylinux2014_aarch64-for-codac /io/scripts/docker/build_pybinding.sh +ls wheelhouse + +cd .. diff --git a/packages/temporary/gennewcodacpi_armhf.sh b/packages/temporary/gennewcodacpi_armhf.sh new file mode 100644 index 000000000..4ef86486f --- /dev/null +++ b/packages/temporary/gennewcodacpi_armhf.sh @@ -0,0 +1,73 @@ +#!/bin/bash + +set -e -x + +mkdir -p ~/Downloads/newcodac + +cd ~/Downloads/newcodac +sudo rm -Rf codac* +git clone https://github.com/lebarsfa/codac +#git clone https://github.com/codac-team/codac +cd codac +git submodule init +git submodule update + +for DIST in bookworm bullseye buster; do + +docker pull lebarsfa/pi:$DIST-for-codac +docker run --rm -v `pwd`:/io lebarsfa/pi:$DIST-for-codac /bin/bash -c "uname -a ; cat /etc/os-release ; lsb_release -a ; \ +if [ \"\$(lsb_release -cs)\" = \"bookworm\" ]; then \ + PIP_OPTIONS=--break-system-packages ; \ + #python3 -m pip install \$PIP_OPTIONS --upgrade \"auditwheel\" ; \\ +else \ + PIP_OPTIONS= ; \ + #python3 -m pip install \$PIP_OPTIONS --upgrade \"auditwheel==5.1.2\" ; \\ +fi && \ +sudo apt-get -q update --allow-releaseinfo-change ; sudo apt-get -y install libeigen3-dev python3-dev patchelf || true && \ +python3 -m pip install \$PIP_OPTIONS --upgrade patchelf --prefer-binary --extra-index-url https://www.piwheels.org/simple && \ +python3 -m pip install \$PIP_OPTIONS --upgrade auditwheel --prefer-binary --extra-index-url https://www.piwheels.org/simple && \ +# wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20240417/ibex_armhf_\$(lsb_release -cs).zip --no-check-certificate -nv is causing illegal instruction on a Mac M1... \\ +curl -L -O https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20240417/ibex_armhf_\$(lsb_release -cs).zip --insecure && \ +unzip -q ibex_armhf_\$(lsb_release -cs).zip && \ +rm -Rf ibex_armhf_\$(lsb_release -cs).zip && \ +sudo cp -Rf ibex/* /usr/local/ && \ +\ +git config --global --add safe.directory /io && \ +cd /io && \ +\ +python3 -m pip install \$PIP_OPTIONS --upgrade pip && \ +mkdir -p build_dir_\$(lsb_release -cs) && cd build_dir_\$(lsb_release -cs) && \ +cmake -E env CXXFLAGS=\"-fPIC\" CFLAGS=\"-fPIC\" cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTS=ON -DWITH_TUBE_TREE=OFF -DWITH_CAPD=OFF -DWITH_PYTHON=ON .. && \ +make -j4 && \ +\ +make test ARGS=\"-V --output-on-failure\" && \ +echo \"start of Testing/Temporary/LastTest.log\" && \ +cat Testing/Temporary/LastTest.log && \ +echo \"end of Testing/Temporary/LastTest.log\" && \ +make pip_package && \ +echo \"copy wheel and clean build_dir_\$(lsb_release -cs)\" && \ +for whl in *.whl; do \ + # auditwheel might fail, so instead just copy... \\ + auditwheel repair \"\$whl\" -w /io/wheelhouse/ || cp -f \"\$whl\" /io/wheelhouse/\"\$whl\" ; \ +done ; \ +#for file in /io/wheelhouse/*armv6l.whl; do \\ +# cp \"\$file\" \"\${file/armv6l/armv7l}\" ; \\ +#done ; \\ +#for file in /io/wheelhouse/*armv7l.whl; do \\ +# cp \"\$file\" \"\${file/armv7l/armv6l}\" ; \\ +#done ; \\ +\ +# Prerequisites for numpy. \\ +sudo apt-get -y install libatlas3-base || true && \ +sudo apt-get -y install libopenblas0-pthread || true && \ +sudo apt-get -y install libgfortran5 || true && \ +python3 -m pip install \$PIP_OPTIONS numpy --prefer-binary --extra-index-url https://www.piwheels.org/simple && \ +python3 -m pip install \$PIP_OPTIONS codac --no-deps --no-index -f /io/wheelhouse && \ +(cd \"\$HOME\"; python3 -m unittest discover codac.tests) && \ +cd /io && \ +rm -fr build_dir_\$(lsb_release -cs)" +ls wheelhouse + +done + +cd .. From de9fed97620a37b752a6248faf0a58d64bd29ef0 Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Thu, 18 Apr 2024 18:03:59 +0200 Subject: [PATCH 220/256] Update documentation --- doc/doc/dev/info_dev.rst | 18 +++++++- .../install/01-installation-full-linux.rst | 2 +- doc/doc/install/01-installation-python.rst | 43 ++++++++++++------- doc/doc/install/01-installation.rst | 38 ++++++++-------- 4 files changed, 65 insertions(+), 36 deletions(-) diff --git a/doc/doc/dev/info_dev.rst b/doc/doc/dev/info_dev.rst index ad90ad24e..233f3cff6 100644 --- a/doc/doc/dev/info_dev.rst +++ b/doc/doc/dev/info_dev.rst @@ -210,6 +210,10 @@ Tag the current version: git tag -a v3.0.0-beta1 git push origin v3.0.0-beta1 +.. warning:: + + | ``-beta1`` part in the version might not be always supported. + Get all the *wheels* generated by GitHub Actions (the generated release needs to be explicitely set as latest): .. code-block:: bash @@ -226,6 +230,7 @@ Upload the *wheels* on PyPi: python3 -m twine upload --repository pypi * + .. rubric:: Testing the Linux *wheels* with Docker In the :file:`codac` directory, test the x86_64 Linux configuration locally using Docker: @@ -236,7 +241,18 @@ In the :file:`codac` directory, test the x86_64 Linux configuration locally usin docker pull lebarsfa/manylinux2014_x86_64-for-codac docker run --rm -v `pwd`:/io lebarsfa/manylinux2014_x86_64-for-codac /io/scripts/docker/build_pybinding.sh -The same can be done for the ARM Linux configurations (preferably from a powerful ARM computer, such as a Mac with Apple Silicon), see `packages/temporary` folder. +The same can be done for the ARM Linux configurations (preferably from a powerful ARM computer, such as a Mac with Apple Silicon), see ``packages/temporary`` folder. + + +.. rubric:: [For admins] Upload Ubuntu packages + +Get access to https://packages.ensta-bretagne.fr/ server as a network drive from an Ubuntu computer. If not already done, run ``sudo apt install dpkg-dev curl`` to install some prerequisites. Then, run ``sudo ./getlatestcodacrelease.sh`` to download automatically the ``.deb`` files from the latest GitHub release for all the supported configurations and update the :ref:`repository database ` (or just ``sudo ./gencodac.sh`` to download automatically a specific version without updating the database). If you downloaded manually the ``.deb`` files, you can update the repository database with ``sudo ./scanpackages.sh``. + + +.. rubric:: [For admins] Upload Windows packages + +Get access to https://packages.ensta-bretagne.fr/ server as a network drive from a Windows computer. If not already done, install `Chocolatey `_ and then run ``choco install -y wget curl 7z checksum`` to install some prerequisites. Then, in ``choco`` folder, run from the Command Prompt ``getlatestcodacrelease.bat`` to generate a ``.nupkg`` from the latest GitHub release (or just ``gencodacpackages.bat`` to be able to test a specific version), then ``choco push codac.X.Y.Z.nupkg`` (need logged in Chocolatey maintainer) when the version is considered as OK to upload to the `Chocolatey repository `_. + .. rubric:: MATLAB compatibility diff --git a/doc/doc/install/01-installation-full-linux.rst b/doc/doc/install/01-installation-full-linux.rst index e1ede2b05..c006f25a5 100644 --- a/doc/doc/install/01-installation-full-linux.rst +++ b/doc/doc/install/01-installation-full-linux.rst @@ -8,7 +8,7 @@ Installing Codac on Linux for C++ use Install from package (latest release, for Ubuntu (amd64, arm64), Debian (arm64, armhf) and possibly others) ---------------------------------------------------------- +----------------------------------------------------------------------------------------------------------- A Debian package is available for the last release |version| of the library: diff --git a/doc/doc/install/01-installation-python.rst b/doc/doc/install/01-installation-python.rst index e11e65ba5..1693b0841 100644 --- a/doc/doc/install/01-installation-python.rst +++ b/doc/doc/install/01-installation-python.rst @@ -35,24 +35,35 @@ In case you want to use Codac only with Python, then the installation procedure The :gbg:`✓` configurations are officially supported at the moment: -+---------------+----------------+-----------------+-----------------+----------------+----------------+----------------+ -|Language |Linux (amd64) |Windows (x64) |Windows (x86) |macOS (arm64) |macOS (x86_64) |Online | -+===============+================+=================+=================+================+================+================+ -|Python 3.6 |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` ||online-py|_ | -+---------------+----------------+-----------------+-----------------+----------------+----------------+ + -|Python 3.7 |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` | | -+---------------+----------------+-----------------+-----------------+----------------+----------------+ + -|Python 3.8 |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` | | -+---------------+----------------+-----------------+-----------------+----------------+----------------+ + -|Python 3.9 |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` | | -+---------------+----------------+-----------------+-----------------+----------------+----------------+ + -|Python 3.10 |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` | | -+---------------+----------------+-----------------+-----------------+----------------+----------------+ + -|Python 3.11 |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` | | -+---------------+----------------+-----------------+-----------------+----------------+----------------+----------------+ ++---------------+----------------+----------------+-----------------+-----------------+----------------+----------------+----------------+ +|Language |Linux (amd64) |Linux (arm64) |Windows (x64) |Windows (x86) |macOS (arm64) |macOS (x86_64) |Online | ++===============+================+================+=================+=================+================+================+================+ +|Python 3.6 |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` ||online-py|_ | ++---------------+----------------+----------------+-----------------+-----------------+----------------+----------------+ + +|Python 3.7 |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` | | ++---------------+----------------+----------------+-----------------+-----------------+----------------+----------------+ + +|Python 3.8 |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` | | ++---------------+----------------+----------------+-----------------+-----------------+----------------+----------------+ + +|Python 3.9 |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` | | ++---------------+----------------+----------------+-----------------+-----------------+----------------+----------------+ + +|Python 3.10 |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` | | ++---------------+----------------+----------------+-----------------+-----------------+----------------+----------------+ + +|Python 3.11 |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` | | ++---------------+----------------+----------------+-----------------+-----------------+----------------+----------------+----------------+ +|Python 3.12 |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` |:gbg:`✓` | | ++---------------+----------------+----------------+-----------------+-----------------+----------------+----------------+----------------+ If a configuration in this table does not work, please `contact us `_. +.. warning:: + + | **Debian Bookworm and possibly other configurations:** + | ``pip install ...`` or ``pip3 install ...`` commands may only work inside `virtual environments `_ or with ``--break-system-packages`` parameter, e.g.: + + .. code-block:: bash + + pip3 install --break-system-packages codac + .. warning:: | **macOS Big Sur and later (x86_64):** @@ -67,7 +78,7 @@ If a configuration in this table does not work, please `contact us `_ (not tested). + Unsupported computers can still probably follow `Installing local Python binding <../dev/info_dev.html>`_ . Update your Codac Python package diff --git a/doc/doc/install/01-installation.rst b/doc/doc/install/01-installation.rst index 0fb0dae8d..c7898f72e 100644 --- a/doc/doc/install/01-installation.rst +++ b/doc/doc/install/01-installation.rst @@ -37,28 +37,30 @@ Codac is available in both C++17 and Python3 (:ref:`as well as MATLAB through it The :gbg:`✓` configurations are officially supported at the moment: -+---------------+----------------+-----------------+-----------------+----------------+----------------+----------------+ -|Language |Linux (amd64) |Windows (x64) |Windows (x86) |macOS (arm64) |macOS (x86_64) |Online | -+===============+================+=================+=================+================+================+================+ -|C++17 ||linux-cpp|_ ||win-cpp|_ ||win-cpp|_ ||macos-cpp|_ ||macos-cpp|_ | | -+---------------+----------------+-----------------+-----------------+----------------+----------------+----------------+ -|Python 3.6 ||linux-py|_ ||win-py|_ ||win-py|_ ||macos-py|_ ||macos-py|_ ||online-py|_ | -+---------------+----------------+-----------------+-----------------+----------------+----------------+ + -|Python 3.7 ||linux-py|_ ||win-py|_ ||win-py|_ ||macos-py|_ ||macos-py|_ | | -+---------------+----------------+-----------------+-----------------+----------------+----------------+ + -|Python 3.8 ||linux-py|_ ||win-py|_ ||win-py|_ ||macos-py|_ ||macos-py|_ | | -+---------------+----------------+-----------------+-----------------+----------------+----------------+ + -|Python 3.9 ||linux-py|_ ||win-py|_ ||win-py|_ ||macos-py|_ ||macos-py|_ | | -+---------------+----------------+-----------------+-----------------+----------------+----------------+ + -|Python 3.10 ||linux-py|_ ||win-py|_ ||win-py|_ ||macos-py|_ ||macos-py|_ | | -+---------------+----------------+-----------------+-----------------+----------------+----------------+ + -|Python 3.11 ||linux-py|_ ||win-py|_ ||win-py|_ ||macos-py|_ ||macos-py|_ | | -+---------------+----------------+-----------------+-----------------+----------------+----------------+----------------+ ++---------------+----------------+----------------+-----------------+-----------------+----------------+----------------+----------------+ +|Language |Linux (amd64) |Linux (arm64) |Windows (x64) |Windows (x86) |macOS (arm64) |macOS (x86_64) |Online | ++===============+================+================+=================+=================+================+================+================+ +|C++17 ||linux-cpp|_ ||linux-cpp|_ ||win-cpp|_ ||win-cpp|_ ||macos-cpp|_ ||macos-cpp|_ | | ++---------------+----------------+----------------+-----------------+-----------------+----------------+----------------+----------------+ +|Python 3.6 ||linux-py|_ ||linux-py|_ ||win-py|_ ||win-py|_ ||macos-py|_ ||macos-py|_ ||online-py|_ | ++---------------+----------------+----------------+-----------------+-----------------+----------------+----------------+ + +|Python 3.7 ||linux-py|_ ||linux-py|_ ||win-py|_ ||win-py|_ ||macos-py|_ ||macos-py|_ | | ++---------------+----------------+----------------+-----------------+-----------------+----------------+----------------+ + +|Python 3.8 ||linux-py|_ ||linux-py|_ ||win-py|_ ||win-py|_ ||macos-py|_ ||macos-py|_ | | ++---------------+----------------+----------------+-----------------+-----------------+----------------+----------------+ + +|Python 3.9 ||linux-py|_ ||linux-py|_ ||win-py|_ ||win-py|_ ||macos-py|_ ||macos-py|_ | | ++---------------+----------------+----------------+-----------------+-----------------+----------------+----------------+ + +|Python 3.10 ||linux-py|_ ||linux-py|_ ||win-py|_ ||win-py|_ ||macos-py|_ ||macos-py|_ | | ++---------------+----------------+----------------+-----------------+-----------------+----------------+----------------+ + +|Python 3.11 ||linux-py|_ ||linux-py|_ ||win-py|_ ||win-py|_ ||macos-py|_ ||macos-py|_ | | ++---------------+----------------+----------------+-----------------+-----------------+----------------+----------------+----------------+ +|Python 3.12 ||linux-py|_ ||linux-py|_ ||win-py|_ ||win-py|_ ||macos-py|_ ||macos-py|_ | | ++---------------+----------------+----------------+-----------------+-----------------+----------------+----------------+----------------+ | **Click on the links in the table to access the related installation procedures.** | If a configuration in this table does not work, please `contact us `_. -Note that if you want to contribute to Codac, you have to make the full C++ installation. +Note that if you want to contribute to Codac, you have to make the full C++ installation from sources. From 4673b49a3ec7ea1dbcb3984856f7b2ffb3c76e9a Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Thu, 25 Apr 2024 19:50:35 +0200 Subject: [PATCH 221/256] Update temporary scripts to be able to manually build Codac on specific platforms (point from lebarsfa repo to codac-team repo) --- packages/temporary/gennewcodacmanylinux2014_aarch64.sh | 4 ++-- packages/temporary/gennewcodacpi_armhf.sh | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/temporary/gennewcodacmanylinux2014_aarch64.sh b/packages/temporary/gennewcodacmanylinux2014_aarch64.sh index 2c7863b45..d0f4d1381 100644 --- a/packages/temporary/gennewcodacmanylinux2014_aarch64.sh +++ b/packages/temporary/gennewcodacmanylinux2014_aarch64.sh @@ -6,8 +6,8 @@ mkdir -p ~/Downloads/newcodac cd ~/Downloads/newcodac rm -Rf codac* -git clone https://github.com/lebarsfa/codac -#git clone https://github.com/codac-team/codac +#git clone https://github.com/lebarsfa/codac +git clone https://github.com/codac-team/codac cd codac git submodule init git submodule update diff --git a/packages/temporary/gennewcodacpi_armhf.sh b/packages/temporary/gennewcodacpi_armhf.sh index 4ef86486f..5fdf29a22 100644 --- a/packages/temporary/gennewcodacpi_armhf.sh +++ b/packages/temporary/gennewcodacpi_armhf.sh @@ -6,8 +6,8 @@ mkdir -p ~/Downloads/newcodac cd ~/Downloads/newcodac sudo rm -Rf codac* -git clone https://github.com/lebarsfa/codac -#git clone https://github.com/codac-team/codac +#git clone https://github.com/lebarsfa/codac +git clone https://github.com/codac-team/codac cd codac git submodule init git submodule update From 1980a83c4bbac0b97cce689b23c9d9b798dcd7fa Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Sat, 27 Apr 2024 16:20:36 +0200 Subject: [PATCH 222/256] Add preliminary Ubuntu 24.04 amd64 support from a container, and Debian amd64 support --- .github/workflows/dockermatrix.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/dockermatrix.yml b/.github/workflows/dockermatrix.yml index d29394d1a..674a8d2ca 100644 --- a/.github/workflows/dockermatrix.yml +++ b/.github/workflows/dockermatrix.yml @@ -17,13 +17,17 @@ jobs: cfg: - { img: 'lebarsfa/manylinux2014_x86_64-for-codac', shell: bash, arch: x86_64, bitness: 64, runtime: manylinux2014, cmake_flags: '-fPIC', desc: 'CentOS manylinux2014 x86_64' } - { img: 'lebarsfa/manylinux2014_aarch64-for-codac', shell: bash, arch: aarch64, bitness: 64, runtime: manylinux2014, cmake_flags: '-fPIC', docker_flags: '--platform linux/arm64', desc: 'CentOS manylinux2014 aarch64' } + - { img: 'lebarsfa/amd64:noble-for-codac', shell: bash, arch: amd64, bitness: 64, runtime: noble, cmake_flags: '-fPIC', deb: true, desc: 'Ubuntu 24.04 amd64' } - { img: 'lebarsfa/pi-64:jammy-for-codac', shell: bash, arch: arm64, bitness: 64, runtime: jammy, cmake_flags: '-fPIC', deb: true, desc: 'Ubuntu 22.04 arm64' } - { img: 'lebarsfa/pi-64:focal-for-codac', shell: bash, arch: arm64, bitness: 64, runtime: focal, cmake_flags: '-fPIC', deb: true, desc: 'Ubuntu 20.04 arm64' } + - { img: 'lebarsfa/amd64:bookworm-for-codac', shell: bash, arch: amd64, bitness: 64, runtime: bookworm, cmake_flags: '-fPIC', deb: true, desc: 'Debian Bookworm amd64' } - { img: 'lebarsfa/pi-64:bookworm-for-codac', shell: bash, arch: arm64, bitness: 64, runtime: bookworm, cmake_flags: '-fPIC', deb: true, desc: 'Debian Bookworm arm64' } - - { img: 'lebarsfa/pi-64:bullseye-for-codac', shell: bash, arch: arm64, bitness: 64, runtime: bullseye, cmake_flags: '-fPIC', deb: true, desc: 'Debian Bullseye arm64' } - - { img: 'lebarsfa/pi-64:buster-for-codac', shell: bash, arch: arm64, bitness: 64, runtime: buster, cmake_flags: '-fPIC', deb: true, desc: 'Debian Buster arm64' } - { img: 'lebarsfa/pi:bookworm-for-codac', shell: bash, arch: armhf, bitness: 32, runtime: bookworm, cmake_flags: '-fPIC', deb: true, desc: 'Raspbian Bookworm armv6hf' } + - { img: 'lebarsfa/amd64:bullseye-for-codac', shell: bash, arch: amd64, bitness: 64, runtime: bullseye, cmake_flags: '-fPIC', deb: true, desc: 'Debian Bullseye amd64' } + - { img: 'lebarsfa/pi-64:bullseye-for-codac', shell: bash, arch: arm64, bitness: 64, runtime: bullseye, cmake_flags: '-fPIC', deb: true, desc: 'Debian Bullseye arm64' } - { img: 'lebarsfa/pi:bullseye-for-codac', shell: bash, arch: armhf, bitness: 32, runtime: bullseye, cmake_flags: '-fPIC', deb: true, desc: 'Raspbian Bullseye armv6hf' } + - { img: 'lebarsfa/amd64:buster-for-codac', shell: bash, arch: amd64, bitness: 64, runtime: buster, cmake_flags: '-fPIC', deb: true, desc: 'Debian Buster amd64' } + - { img: 'lebarsfa/pi-64:buster-for-codac', shell: bash, arch: arm64, bitness: 64, runtime: buster, cmake_flags: '-fPIC', deb: true, desc: 'Debian Buster arm64' } - { img: 'lebarsfa/pi:buster-for-codac', shell: bash, arch: armhf, bitness: 32, runtime: buster, cmake_flags: '-fPIC', deb: true, desc: 'Raspbian Buster armv6hf' } name: ${{ matrix.cfg.desc }} steps: From 3a0457608a91b211e8fc98b24b1a53a0885b74ed Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Sun, 5 May 2024 14:16:52 +0200 Subject: [PATCH 223/256] Update workflows --- .github/workflows/dockermatrix.yml | 4 ++-- .github/workflows/macosmatrix.yml | 5 ++--- .github/workflows/unixmatrix.yml | 2 +- .github/workflows/vcmatrix.yml | 3 +-- 4 files changed, 6 insertions(+), 8 deletions(-) diff --git a/.github/workflows/dockermatrix.yml b/.github/workflows/dockermatrix.yml index 674a8d2ca..b27617a9d 100644 --- a/.github/workflows/dockermatrix.yml +++ b/.github/workflows/dockermatrix.yml @@ -54,12 +54,12 @@ jobs: sudo apt-get -y install qemu binfmt-support qemu-user-static || true #docker run --rm --privileged multiarch/qemu-user-static:register --reset docker run --rm --privileged multiarch/qemu-user-static --reset -p yes - if: (matrix.cfg.arch!='x86_64')&&(matrix.cfg.arch!='i386') + if: (matrix.cfg.arch!='amd64')&&(matrix.cfg.arch!='x86_64')&&(matrix.cfg.arch!='i386') - run: | docker run ${{ matrix.cfg.docker_flags }} -i -v "${PWD}/..:${PWD}/.." ${{ matrix.cfg.img }} /bin/bash -c "uname -a ; cat /etc/os-release ; lsb_release -a ; cd ${PWD} && pwd && \ git config --global --add safe.directory ${PWD} && \ if [ ${{ matrix.cfg.deb }} = true ]; then \ - sudo apt-get -q update --allow-releaseinfo-change ; sudo apt-get -y install libeigen3-dev dpkg-dev || true && \ + sudo apt-get -q update ; sudo apt-get -y install libeigen3-dev dpkg-dev || true && \ wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20240417/libibex-dev-2.8.9.20240417-0${{ matrix.cfg.runtime }}0_\$(dpkg --print-architecture).deb --no-check-certificate -nv && \ sudo dpkg -i libibex-dev-2.8.9.20240417-0${{ matrix.cfg.runtime }}0_\$(dpkg --print-architecture).deb && \ rm -Rf libibex-dev-2.8.9.20240417-0${{ matrix.cfg.runtime }}0_\$(dpkg --print-architecture).deb ; \ diff --git a/.github/workflows/macosmatrix.yml b/.github/workflows/macosmatrix.yml index 0519b4ce4..0d91501e1 100644 --- a/.github/workflows/macosmatrix.yml +++ b/.github/workflows/macosmatrix.yml @@ -1,5 +1,4 @@ -# This file generates Python wheels for Windows -# (and zip for having Codac and IBEX binaries for several Visual Studio versions) +# This file generates Python wheels for macOS on: push: branches: 'master' @@ -69,7 +68,7 @@ jobs: cmake --build . --config Release --target pip_package ; cp `ls *.whl` ../`ls *.whl | sed "s/py3-none-any/cp${{ matrix.cfg.py_v_maj }}${{ matrix.cfg.py_v_min }}-cp${{ matrix.cfg.py_v_maj }}${{ matrix.cfg.py_v_min }}${{ matrix.cfg.cpcfg }}/"` cd .. shell: bash - - run: pip install *.whl ; python -c "import sys; print(sys.version)" ; python examples/tuto/01_getting_started/01_getting_started.py ; pip install numpy --prefer-binary ; python -m unittest discover codac.tests + - run: pip install --no-deps --no-index *.whl ; python -c "import sys; print(sys.version)" ; python examples/tuto/01_getting_started/01_getting_started.py ; pip install numpy --prefer-binary ; python -m unittest discover codac.tests shell: bash if: matrix.cfg.cross!=true - uses: xresloader/upload-to-github-release@v1 diff --git a/.github/workflows/unixmatrix.yml b/.github/workflows/unixmatrix.yml index 30b45aa34..969aa750f 100644 --- a/.github/workflows/unixmatrix.yml +++ b/.github/workflows/unixmatrix.yml @@ -1,4 +1,4 @@ -# This file generates .deb (Unix) and .nupkg (Windows) files +# This file generates .deb (Unix) and .nupkg (Windows) packages (and zip for having Codac and IBEX binaries for several Visual Studio versions) on: push: branches: '**' diff --git a/.github/workflows/vcmatrix.yml b/.github/workflows/vcmatrix.yml index 269eef29f..8df9ff1f3 100644 --- a/.github/workflows/vcmatrix.yml +++ b/.github/workflows/vcmatrix.yml @@ -1,5 +1,4 @@ # This file generates Python wheels for Windows -# (and zip for having Codac and IBEX binaries for several Visual Studio versions) on: push: branches: 'master' @@ -69,7 +68,7 @@ jobs: cmake --build . --config Release --target pip_package ; cp `ls *.whl` ../`ls *.whl | sed "s/py3-none-any/cp${{ matrix.cfg.py_v_maj }}${{ matrix.cfg.py_v_min }}-cp${{ matrix.cfg.py_v_maj }}${{ matrix.cfg.py_v_min }}${{ matrix.cfg.cpcfg }}/"` cd .. shell: bash - - run: pip install *.whl ; python -c "import sys; print(sys.version)" ; python examples/tuto/01_getting_started/01_getting_started.py ; pip install numpy --prefer-binary ; python -m unittest discover codac.tests + - run: pip install --no-deps --no-index *.whl ; python -c "import sys; print(sys.version)" ; python examples/tuto/01_getting_started/01_getting_started.py ; pip install numpy --prefer-binary ; python -m unittest discover codac.tests shell: bash - uses: xresloader/upload-to-github-release@v1 env: From 76250338ad194c60973dc5e9c2a5f05aba3f4722 Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Wed, 29 May 2024 21:26:18 +0200 Subject: [PATCH 224/256] macOS doxygen formula warnings --- .github/workflows/macosmatrix.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/macosmatrix.yml b/.github/workflows/macosmatrix.yml index 0d91501e1..33fb9d167 100644 --- a/.github/workflows/macosmatrix.yml +++ b/.github/workflows/macosmatrix.yml @@ -53,7 +53,7 @@ jobs: - run: brew install eigen if: runner.os=='macOS' # doxygen v1.9.7 causes issues... - - run: brew install graphviz ; wget https://github.com/Homebrew/homebrew-core/raw/d2267b9f2ad247bc9c8273eb755b39566a474a70/Formula/doxygen.rb ; brew reinstall ./doxygen.rb ; brew pin doxygen ; python -m pip install --upgrade pip ; pip install --upgrade wheel setuptools sphinx breathe sphinx_rtd_theme sphinx-tabs sphinx-issues sphinx-reredirects + - run: brew install graphviz ; wget https://github.com/Homebrew/homebrew-core/raw/d2267b9f2ad247bc9c8273eb755b39566a474a70/Formula/doxygen.rb ; brew reinstall --formula ./doxygen.rb ; brew pin doxygen ; python -m pip install --upgrade pip ; pip install --upgrade wheel setuptools sphinx breathe sphinx_rtd_theme sphinx-tabs sphinx-issues sphinx-reredirects if: runner.os=='macOS' - run: | wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20240417/ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip --no-check-certificate -nv From 87d5811801f9f7ddd54ee0b6baccf9814e09c1be Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Thu, 30 May 2024 17:12:11 +0200 Subject: [PATCH 225/256] Update workflows --- .github/workflows/unixmatrix.yml | 4 ++-- .github/workflows/vcmatrix.yml | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/unixmatrix.yml b/.github/workflows/unixmatrix.yml index 969aa750f..54cbc662c 100644 --- a/.github/workflows/unixmatrix.yml +++ b/.github/workflows/unixmatrix.yml @@ -32,8 +32,8 @@ jobs: - { os: windows-2022, shell: cmd, arch: x86, bitness: 32, runtime: vc17, cmake_params: '-G "Visual Studio 17" -T v143 -A Win32', cmake_flags: ' /wd4267 /wd4244 /wd4305 /wd4996', test_config: 'Release/', choco_flags: '--x86', desc: 'Windows Visual Studio 2022 x86' } - { os: windows-2022, shell: cmd, arch: x64, bitness: 64, runtime: vc16, cmake_params: '-G "Visual Studio 17" -T v142 -A x64', cmake_flags: ' /wd4267 /wd4244 /wd4305 /wd4996', test_config: 'Release/', desc: 'Windows Visual Studio 2019 x64' } - { os: windows-2022, shell: cmd, arch: x86, bitness: 32, runtime: vc16, cmake_params: '-G "Visual Studio 17" -T v142 -A Win32', cmake_flags: ' /wd4267 /wd4244 /wd4305 /wd4996', test_config: 'Release/', choco_flags: '--x86', desc: 'Windows Visual Studio 2019 x86' } - - { os: windows-2022, shell: cmd, arch: x64, bitness: 64, runtime: vc15, cmake_params: '-G "Visual Studio 17" -T v141 -A x64', cmake_flags: ' /wd4267 /wd4244 /wd4305 /wd4996', test_config: 'Release/', desc: 'Windows Visual Studio 2017 x64' } - - { os: windows-2022, shell: cmd, arch: x86, bitness: 32, runtime: vc15, cmake_params: '-G "Visual Studio 17" -T v141 -A Win32', cmake_flags: ' /wd4267 /wd4244 /wd4305 /wd4996', test_config: 'Release/', choco_flags: '--x86', desc: 'Windows Visual Studio 2017 x86' } + - { os: windows-2019, shell: cmd, arch: x64, bitness: 64, runtime: vc15, cmake_params: '-G "Visual Studio 16" -T v141 -A x64', cmake_flags: ' /wd4267 /wd4244 /wd4305 /wd4996', test_config: 'Release/', desc: 'Windows Visual Studio 2017 x64' } + - { os: windows-2019, shell: cmd, arch: x86, bitness: 32, runtime: vc15, cmake_params: '-G "Visual Studio 16" -T v141 -A Win32', cmake_flags: ' /wd4267 /wd4244 /wd4305 /wd4996', test_config: 'Release/', choco_flags: '--x86', desc: 'Windows Visual Studio 2017 x86' } - { os: windows-2022, shell: cmd, arch: x64, bitness: 64, runtime: mingw13, cmake_params: '-G "MinGW Makefiles"', cmake_flags: '-fPIC', desc: 'Windows MinGW 13.2.0 x64' } - { os: windows-2022, shell: cmd, arch: x86, bitness: 32, runtime: mingw13, cmake_params: '-G "MinGW Makefiles"', cmake_flags: '-fPIC', choco_flags: '--x86', desc: 'Windows MinGW 13.2.0 x86' } - { os: windows-2022, shell: cmd, arch: x64, bitness: 64, runtime: mingw12, cmake_params: '-G "MinGW Makefiles"', cmake_flags: '-fPIC', desc: 'Windows MinGW 12.2.0 x64' } diff --git a/.github/workflows/vcmatrix.yml b/.github/workflows/vcmatrix.yml index 8df9ff1f3..414ad10f6 100644 --- a/.github/workflows/vcmatrix.yml +++ b/.github/workflows/vcmatrix.yml @@ -26,10 +26,10 @@ jobs: - { os: windows-2022, shell: cmd, arch: x64, runtime: vc16, cmake_params: '-G "Visual Studio 17" -T v142 -A x64', cpcfg: '-win_amd64', py_v_maj: 3, py_v_min: 9, desc: 'Windows Visual Studio 2019 x64 Python 3.9' } - { os: windows-2022, shell: cmd, arch: x64, runtime: vc16, cmake_params: '-G "Visual Studio 17" -T v142 -A x64', cpcfg: '-win_amd64', py_v_maj: 3, py_v_min: 8, desc: 'Windows Visual Studio 2019 x64 Python 3.8' } # Should be Visual Studio 2015 for Python 3.5-3.7, but need Visual Studio 2017 for C++17 compatibility...? - - { os: windows-2022, shell: cmd, arch: x86, runtime: vc15, cmake_params: '-G "Visual Studio 17" -T v141 -A Win32', choco_flags: '--x86', cpcfg: 'm-win32', py_v_maj: 3, py_v_min: 7, desc: 'Windows Visual Studio 2017 x86 Python 3.7' } - - { os: windows-2022, shell: cmd, arch: x86, runtime: vc15, cmake_params: '-G "Visual Studio 17" -T v141 -A Win32', choco_flags: '--x86', cpcfg: 'm-win32', py_v_maj: 3, py_v_min: 6, desc: 'Windows Visual Studio 2017 x86 Python 3.6' } - - { os: windows-2022, shell: cmd, arch: x64, runtime: vc15, cmake_params: '-G "Visual Studio 17" -T v141 -A x64', cpcfg: 'm-win_amd64', py_v_maj: 3, py_v_min: 7, desc: 'Windows Visual Studio 2017 x64 Python 3.7' } - - { os: windows-2022, shell: cmd, arch: x64, runtime: vc15, cmake_params: '-G "Visual Studio 17" -T v141 -A x64', cpcfg: 'm-win_amd64', py_v_maj: 3, py_v_min: 6, desc: 'Windows Visual Studio 2017 x64 Python 3.6' } + - { os: windows-2019, shell: cmd, arch: x86, runtime: vc15, cmake_params: '-G "Visual Studio 16" -T v141 -A Win32', choco_flags: '--x86', cpcfg: 'm-win32', py_v_maj: 3, py_v_min: 7, desc: 'Windows Visual Studio 2017 x86 Python 3.7' } + - { os: windows-2019, shell: cmd, arch: x86, runtime: vc15, cmake_params: '-G "Visual Studio 16" -T v141 -A Win32', choco_flags: '--x86', cpcfg: 'm-win32', py_v_maj: 3, py_v_min: 6, desc: 'Windows Visual Studio 2017 x86 Python 3.6' } + - { os: windows-2019, shell: cmd, arch: x64, runtime: vc15, cmake_params: '-G "Visual Studio 16" -T v141 -A x64', cpcfg: 'm-win_amd64', py_v_maj: 3, py_v_min: 7, desc: 'Windows Visual Studio 2017 x64 Python 3.7' } + - { os: windows-2019, shell: cmd, arch: x64, runtime: vc15, cmake_params: '-G "Visual Studio 16" -T v141 -A x64', cpcfg: 'm-win_amd64', py_v_maj: 3, py_v_min: 6, desc: 'Windows Visual Studio 2017 x64 Python 3.6' } name: ${{ matrix.cfg.desc }} steps: - uses: actions/checkout@v4 From c8b5378bf7cd22298323ad82d705c684d357674e Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Sun, 20 Oct 2024 18:41:13 +0200 Subject: [PATCH 226/256] Update install_ibex.sh --- scripts/dependencies/install_ibex.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/dependencies/install_ibex.sh b/scripts/dependencies/install_ibex.sh index da3cb7cfe..e115fb321 100644 --- a/scripts/dependencies/install_ibex.sh +++ b/scripts/dependencies/install_ibex.sh @@ -7,7 +7,7 @@ if [ ! -e "ibex-lib/README.md" ]; then cd ibex-lib ; mkdir build && cd build ; cmake -E env CXXFLAGS="-fPIC" CFLAGS="-fPIC" cmake -DCMAKE_INSTALL_PREFIX=$HOME/ibex-lib/build_install -DCMAKE_BUILD_TYPE=Debug .. ; - make ; + make -j 4 ; else echo 'Using cached directory.' ; fi From 4990feef8d2ed5efda416b01f058be855c7c7a68 Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Sun, 20 Oct 2024 20:14:32 +0200 Subject: [PATCH 227/256] Update gencodacwhl.sh --- scripts/wheels/gencodacwhl.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/wheels/gencodacwhl.sh b/scripts/wheels/gencodacwhl.sh index 14ab8af57..014e0812c 100644 --- a/scripts/wheels/gencodacwhl.sh +++ b/scripts/wheels/gencodacwhl.sh @@ -14,7 +14,7 @@ for PY_V_MIN in 6 7; do done done -for PY_V_MIN in 8 9 10 11 12; do +for PY_V_MIN in 8 9 10 11 12 13; do for CPCFG in "-macosx_10_9_x86_64" "-macosx_11_0_arm64" "-manylinux_2_17_x86_64.manylinux2014_x86_64" "-win32" "-win_amd64"; do rm -Rf codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl ; wget https://github.com/codac-team/codac/releases/download/v$PACKAGE_VERSION/codac-$PACKAGE_VERSION-cp$PY_V_MAJ$PY_V_MIN-cp$PY_V_MAJ$PY_V_MIN$CPCFG.whl --no-check-certificate -nv done From 42d9d6147be243d291b1c6a058392ec357abcde5 Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Sun, 20 Oct 2024 17:49:12 +0200 Subject: [PATCH 228/256] Update build_pybinding.sh --- scripts/docker/build_pybinding.sh | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/scripts/docker/build_pybinding.sh b/scripts/docker/build_pybinding.sh index 054887d14..4970615ba 100755 --- a/scripts/docker/build_pybinding.sh +++ b/scripts/docker/build_pybinding.sh @@ -16,23 +16,27 @@ for PYBIN in /opt/python/cp3*/bin; do #fi "${PYBIN}/python" -m pip install --upgrade pip + "${PYBIN}/python" -m pip install --upgrade wheel setuptools mkdir -p build_dir && cd build_dir cmake -E env CXXFLAGS="-fPIC" CFLAGS="-fPIC" cmake -DPYTHON_EXECUTABLE=${PYBIN}/python -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTS=ON -DWITH_TUBE_TREE=OFF -DWITH_CAPD=OFF -DWITH_PYTHON=ON .. make -j4 - make test ARGS="-V --output-on-failure" - echo "start of Testing/Temporary/LastTest.log" - cat Testing/Temporary/LastTest.log - echo "end of Testing/Temporary/LastTest.log" make pip_package echo "copy wheel and clean build_dir" for whl in *.whl; do auditwheel repair "$whl" -w /io/wheelhouse/ done - "${PYBIN}/python" -m pip install numpy --prefer-binary "${PYBIN}/python" -m pip install codac --no-deps --no-index -f /io/wheelhouse - (cd "$HOME"; "${PYBIN}/python" -m unittest discover codac.tests) + "${PYBIN}/python" ../examples/tuto/01_getting_started/01_getting_started.py + "${PYBIN}/python" -m pip install numpy --prefer-binary + "${PYBIN}/python" -m unittest discover codac.tests + + make test ARGS="-V --output-on-failure" + echo "start of Testing/Temporary/LastTest.log" + cat Testing/Temporary/LastTest.log + echo "end of Testing/Temporary/LastTest.log" + cd /io rm -fr build_dir From 7298adc25294700b49a010634597c24c2418f06c Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Sun, 20 Oct 2024 18:44:23 +0200 Subject: [PATCH 229/256] Update gennewcodacpi_armhf.sh --- packages/temporary/gennewcodacpi_armhf.sh | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/packages/temporary/gennewcodacpi_armhf.sh b/packages/temporary/gennewcodacpi_armhf.sh index 5fdf29a22..0c4e1992f 100644 --- a/packages/temporary/gennewcodacpi_armhf.sh +++ b/packages/temporary/gennewcodacpi_armhf.sh @@ -36,14 +36,11 @@ git config --global --add safe.directory /io && \ cd /io && \ \ python3 -m pip install \$PIP_OPTIONS --upgrade pip && \ +python3 -m pip install \$PIP_OPTIONS --upgrade wheel setuptools && \ mkdir -p build_dir_\$(lsb_release -cs) && cd build_dir_\$(lsb_release -cs) && \ cmake -E env CXXFLAGS=\"-fPIC\" CFLAGS=\"-fPIC\" cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTS=ON -DWITH_TUBE_TREE=OFF -DWITH_CAPD=OFF -DWITH_PYTHON=ON .. && \ make -j4 && \ \ -make test ARGS=\"-V --output-on-failure\" && \ -echo \"start of Testing/Temporary/LastTest.log\" && \ -cat Testing/Temporary/LastTest.log && \ -echo \"end of Testing/Temporary/LastTest.log\" && \ make pip_package && \ echo \"copy wheel and clean build_dir_\$(lsb_release -cs)\" && \ for whl in *.whl; do \ @@ -57,13 +54,20 @@ done ; \ # cp \"\$file\" \"\${file/armv7l/armv6l}\" ; \\ #done ; \\ \ +python3 -m pip install \$PIP_OPTIONS codac --no-deps --no-index -f /io/wheelhouse && \ +python3 ../examples/tuto/01_getting_started/01_getting_started.py && \ # Prerequisites for numpy. \\ sudo apt-get -y install libatlas3-base || true && \ sudo apt-get -y install libopenblas0-pthread || true && \ sudo apt-get -y install libgfortran5 || true && \ python3 -m pip install \$PIP_OPTIONS numpy --prefer-binary --extra-index-url https://www.piwheels.org/simple && \ -python3 -m pip install \$PIP_OPTIONS codac --no-deps --no-index -f /io/wheelhouse && \ -(cd \"\$HOME\"; python3 -m unittest discover codac.tests) && \ +python3 -m unittest discover codac.tests && \ +\ +make test ARGS=\"-V --output-on-failure\" && \ +echo \"start of Testing/Temporary/LastTest.log\" && \ +cat Testing/Temporary/LastTest.log && \ +echo \"end of Testing/Temporary/LastTest.log\" ; \ +\ cd /io && \ rm -fr build_dir_\$(lsb_release -cs)" ls wheelhouse From e455113da6a40712c86c7cadb432d1d6667aac66 Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Sun, 20 Oct 2024 19:20:13 +0200 Subject: [PATCH 230/256] Update dockercentos.yml --- .github/workflows/dockercentos.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/dockercentos.yml b/.github/workflows/dockercentos.yml index 9992b8ee9..309b3d6db 100644 --- a/.github/workflows/dockercentos.yml +++ b/.github/workflows/dockercentos.yml @@ -1,7 +1,9 @@ # This files generates Python wheels for Linux on: push: - branches: 'master' + branches: + - master + - codac4matlab tags: '' # Restrict to blank tags pull_request: @@ -40,4 +42,4 @@ jobs: file: "wheelhouse/*.whl" overwrite: true tag_name: autotagname-${{ github.sha }} - if: (github.event_name != 'pull_request')&&(github.ref_name == 'master') + if: (github.event_name!='pull_request')&&((github.ref_name=='master')||(github.ref_name=='codac4matlab')) From 3d08c2a69352db23e288192408d8fc3f3e3dcc92 Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Sun, 20 Oct 2024 19:58:43 +0200 Subject: [PATCH 231/256] Update macosmatrix.yml --- .github/workflows/macosmatrix.yml | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/.github/workflows/macosmatrix.yml b/.github/workflows/macosmatrix.yml index 33fb9d167..d4d2c2e2f 100644 --- a/.github/workflows/macosmatrix.yml +++ b/.github/workflows/macosmatrix.yml @@ -1,7 +1,9 @@ # This file generates Python wheels for macOS on: push: - branches: 'master' + branches: + - master + - codac4matlab tags: '' # Restrict to blank tags pull_request: @@ -18,6 +20,7 @@ jobs: fail-fast: false matrix: cfg: + - { os: macos-14, shell: bash, arch: arm64, runtime: sonoma, cmake_flags: '-fPIC', trgt: '11.0', cpcfg: '-macosx_11_0_arm64', py_v_maj: 3, py_v_min: 13, desc: 'macOS Sonoma Python 3.13 arm64' } - { os: macos-14, shell: bash, arch: arm64, runtime: sonoma, cmake_flags: '-fPIC', trgt: '11.0', cpcfg: '-macosx_11_0_arm64', py_v_maj: 3, py_v_min: 12, desc: 'macOS Sonoma Python 3.12 arm64' } - { os: macos-14, shell: bash, arch: arm64, runtime: sonoma, cmake_flags: '-fPIC', trgt: '11.0', cpcfg: '-macosx_11_0_arm64', py_v_maj: 3, py_v_min: 11, desc: 'macOS Sonoma Python 3.11 arm64' } - { os: macos-12, shell: bash, arch: arm64, runtime: monterey, cmake_flags: '-fPIC', trgt: '11.0', cpcfg: '-macosx_11_0_arm64', py_v_maj: 3, py_v_min: 10, cross: true, desc: 'macOS Monterey Python 3.10 arm64 (cross)' } @@ -25,6 +28,7 @@ jobs: - { os: macos-12, shell: bash, arch: arm64, runtime: monterey, cmake_flags: '-fPIC', trgt: '11.0', cpcfg: '-macosx_11_0_arm64', py_v_maj: 3, py_v_min: 8, cross: true, desc: 'macOS Monterey Python 3.8 arm64 (cross)' } - { os: macos-12, shell: bash, arch: arm64, runtime: monterey, cmake_flags: '-fPIC', trgt: '11.0', cpcfg: 'm-macosx_11_0_arm64', py_v_maj: 3, py_v_min: 7, cross: true, desc: 'macOS Monterey Python 3.7 arm64 (cross)' } - { os: macos-12, shell: bash, arch: arm64, runtime: monterey, cmake_flags: '-fPIC', trgt: '11.0', cpcfg: 'm-macosx_11_0_arm64', py_v_maj: 3, py_v_min: 6, cross: true, desc: 'macOS Monterey Python 3.6 arm64 (cross)' } + - { os: macos-12, shell: bash, arch: x86_64, runtime: monterey, cmake_flags: '-fPIC', trgt: '10.9', cpcfg: '-macosx_10_9_x86_64', py_v_maj: 3, py_v_min: 13, desc: 'macOS Monterey Python 3.13 x86_64' } - { os: macos-12, shell: bash, arch: x86_64, runtime: monterey, cmake_flags: '-fPIC', trgt: '10.9', cpcfg: '-macosx_10_9_x86_64', py_v_maj: 3, py_v_min: 12, desc: 'macOS Monterey Python 3.12 x86_64' } - { os: macos-12, shell: bash, arch: x86_64, runtime: monterey, cmake_flags: '-fPIC', trgt: '10.9', cpcfg: '-macosx_10_9_x86_64', py_v_maj: 3, py_v_min: 11, desc: 'macOS Monterey Python 3.11 x86_64' } - { os: macos-12, shell: bash, arch: x86_64, runtime: monterey, cmake_flags: '-fPIC', trgt: '10.9', cpcfg: '-macosx_10_9_x86_64', py_v_maj: 3, py_v_min: 10, desc: 'macOS Monterey Python 3.10 x86_64' } @@ -68,9 +72,6 @@ jobs: cmake --build . --config Release --target pip_package ; cp `ls *.whl` ../`ls *.whl | sed "s/py3-none-any/cp${{ matrix.cfg.py_v_maj }}${{ matrix.cfg.py_v_min }}-cp${{ matrix.cfg.py_v_maj }}${{ matrix.cfg.py_v_min }}${{ matrix.cfg.cpcfg }}/"` cd .. shell: bash - - run: pip install --no-deps --no-index *.whl ; python -c "import sys; print(sys.version)" ; python examples/tuto/01_getting_started/01_getting_started.py ; pip install numpy --prefer-binary ; python -m unittest discover codac.tests - shell: bash - if: matrix.cfg.cross!=true - uses: xresloader/upload-to-github-release@v1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -78,4 +79,13 @@ jobs: file: "*.whl" overwrite: true tag_name: autotagname-${{ github.sha }} - if: (github.event_name != 'pull_request')&&(github.ref_name == 'master') + if: (github.event_name!='pull_request')&&((github.ref_name=='master')||(github.ref_name=='codac4matlab')) + - run: | + pip install --no-deps --no-index *.whl + python -c "import sys; print(sys.version)" ; python examples/tuto/01_getting_started/01_getting_started.py + pip install numpy --prefer-binary + python -m unittest discover codac.tests + #cd build && ctest -C Release -V --output-on-failure + #cd .. + shell: bash + if: (matrix.cfg.cross!=true)&&(github.ref_name!='codac4matlab') From a943290072a2667dccc94a0604466ebd2e16e1b8 Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Sun, 20 Oct 2024 19:23:26 +0200 Subject: [PATCH 232/256] Update vcmatrix.yml --- .github/workflows/vcmatrix.yml | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/.github/workflows/vcmatrix.yml b/.github/workflows/vcmatrix.yml index 414ad10f6..5cf28d0fa 100644 --- a/.github/workflows/vcmatrix.yml +++ b/.github/workflows/vcmatrix.yml @@ -1,7 +1,9 @@ # This file generates Python wheels for Windows on: push: - branches: 'master' + branches: + - master + - codac4matlab tags: '' # Restrict to blank tags pull_request: @@ -15,11 +17,13 @@ jobs: fail-fast: false matrix: cfg: + - { os: windows-2022, shell: cmd, arch: x86, runtime: vc17, cmake_params: '-G "Visual Studio 17" -T v143 -A Win32', choco_flags: '--x86', cpcfg: '-win32', py_v_maj: 3, py_v_min: 13, desc: 'Windows Visual Studio 2022 x86 Python 3.13' } - { os: windows-2022, shell: cmd, arch: x86, runtime: vc17, cmake_params: '-G "Visual Studio 17" -T v143 -A Win32', choco_flags: '--x86', cpcfg: '-win32', py_v_maj: 3, py_v_min: 12, desc: 'Windows Visual Studio 2022 x86 Python 3.12' } - { os: windows-2022, shell: cmd, arch: x86, runtime: vc17, cmake_params: '-G "Visual Studio 17" -T v143 -A Win32', choco_flags: '--x86', cpcfg: '-win32', py_v_maj: 3, py_v_min: 11, desc: 'Windows Visual Studio 2022 x86 Python 3.11' } - { os: windows-2022, shell: cmd, arch: x86, runtime: vc16, cmake_params: '-G "Visual Studio 17" -T v142 -A Win32', choco_flags: '--x86', cpcfg: '-win32', py_v_maj: 3, py_v_min: 10, desc: 'Windows Visual Studio 2019 x86 Python 3.10' } - { os: windows-2022, shell: cmd, arch: x86, runtime: vc16, cmake_params: '-G "Visual Studio 17" -T v142 -A Win32', choco_flags: '--x86', cpcfg: '-win32', py_v_maj: 3, py_v_min: 9, desc: 'Windows Visual Studio 2019 x86 Python 3.9' } - { os: windows-2022, shell: cmd, arch: x86, runtime: vc16, cmake_params: '-G "Visual Studio 17" -T v142 -A Win32', choco_flags: '--x86', cpcfg: '-win32', py_v_maj: 3, py_v_min: 8, desc: 'Windows Visual Studio 2019 x86 Python 3.8' } + - { os: windows-2022, shell: cmd, arch: x64, runtime: vc17, cmake_params: '-G "Visual Studio 17" -T v143 -A x64', cpcfg: '-win_amd64', py_v_maj: 3, py_v_min: 13, desc: 'Windows Visual Studio 2022 x64 Python 3.13' } - { os: windows-2022, shell: cmd, arch: x64, runtime: vc17, cmake_params: '-G "Visual Studio 17" -T v143 -A x64', cpcfg: '-win_amd64', py_v_maj: 3, py_v_min: 12, desc: 'Windows Visual Studio 2022 x64 Python 3.12' } - { os: windows-2022, shell: cmd, arch: x64, runtime: vc17, cmake_params: '-G "Visual Studio 17" -T v143 -A x64', cpcfg: '-win_amd64', py_v_maj: 3, py_v_min: 11, desc: 'Windows Visual Studio 2022 x64 Python 3.11' } - { os: windows-2022, shell: cmd, arch: x64, runtime: vc16, cmake_params: '-G "Visual Studio 17" -T v142 -A x64', cpcfg: '-win_amd64', py_v_maj: 3, py_v_min: 10, desc: 'Windows Visual Studio 2019 x64 Python 3.10' } @@ -55,7 +59,7 @@ jobs: if: runner.os=='Windows' - run: choco install -y -r --no-progress eigen --version=3.4.0.20240224 ${{ matrix.cfg.choco_flags }} if: runner.os=='Windows' - - run: wget https://packages.ensta-bretagne.fr/choco/doxygen.install.1.9.6.nupkg --no-check-certificate -nv & choco upgrade -y -r --no-progress -s . doxygen.install --version=1.9.6 & del /f /q doxygen.install.1.9.6.nupkg & choco install -y -r --no-progress graphviz & pip install --upgrade wheel setuptools sphinx breathe sphinx-issues sphinx-tabs sphinx_rtd_theme sphinx-reredirects + - run: wget https://packages.ensta-bretagne.fr/choco/doxygen.install.1.9.6.nupkg --no-check-certificate -nv & choco upgrade -y -r --no-progress -s . doxygen.install --version=1.9.6 & del /f /q doxygen.install.1.9.6.nupkg & choco install -y -r --no-progress graphviz & python -m pip install --upgrade pip & pip install --upgrade wheel setuptools sphinx breathe sphinx-issues sphinx-tabs sphinx_rtd_theme sphinx-reredirects if: runner.os=='Windows' - run: | wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20240417/ibex.2.8.9.20240417.nupkg --no-check-certificate -nv @@ -68,8 +72,6 @@ jobs: cmake --build . --config Release --target pip_package ; cp `ls *.whl` ../`ls *.whl | sed "s/py3-none-any/cp${{ matrix.cfg.py_v_maj }}${{ matrix.cfg.py_v_min }}-cp${{ matrix.cfg.py_v_maj }}${{ matrix.cfg.py_v_min }}${{ matrix.cfg.cpcfg }}/"` cd .. shell: bash - - run: pip install --no-deps --no-index *.whl ; python -c "import sys; print(sys.version)" ; python examples/tuto/01_getting_started/01_getting_started.py ; pip install numpy --prefer-binary ; python -m unittest discover codac.tests - shell: bash - uses: xresloader/upload-to-github-release@v1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -77,4 +79,13 @@ jobs: file: "*.whl" overwrite: true tag_name: autotagname-${{ github.sha }} - if: (github.event_name != 'pull_request')&&(github.ref_name == 'master') + if: (github.event_name!='pull_request')&&((github.ref_name=='master')||(github.ref_name=='codac4matlab')) + - run: | + pip install --no-deps --no-index *.whl + python -c "import sys; print(sys.version)" ; python examples/tuto/01_getting_started/01_getting_started.py + pip install numpy --prefer-binary + python -m unittest discover codac.tests + #cd build && ctest -C Release -V --output-on-failure + #cd .. + shell: bash + if: (github.ref_name!='codac4matlab') From 6f2a2000d3a16c57f663be89e0150fd50e1558eb Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Sun, 20 Oct 2024 20:03:45 +0200 Subject: [PATCH 233/256] Update tests.yml --- .github/workflows/tests.yml | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index a26b2b890..24de99eac 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -12,7 +12,7 @@ jobs: fail-fast: false matrix: cfg: - - { os: ubuntu-20.04, gcc_v: 8, py_v_maj: 3, py_v_min: 6, desc: 'Ubuntu 20.04 GCC 8 Python 3.6 tests' } + - { os: ubuntu-24.04, gcc_v: 11, py_v_maj: 3, py_v_min: 10, desc: 'Ubuntu 24.04 GCC 11 Python 3.10 tests' } name: ${{ matrix.cfg.desc }} steps: - uses: actions/checkout@v4 @@ -24,11 +24,16 @@ jobs: with: python-version: ${{ matrix.cfg.py_v_maj }}.${{ matrix.cfg.py_v_min }} - run: | + # Installing gcc + sudo apt install build-essential manpages-dev software-properties-common + sudo add-apt-repository ppa:ubuntu-toolchain-r/test + sudo apt-get -q update ; sudo apt-get -y install gcc-${{ matrix.cfg.gcc_v }} g++-${{ matrix.cfg.gcc_v }} || true sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-${{ matrix.cfg.gcc_v }} 100 --slave /usr/bin/g++ g++ /usr/bin/g++-${{ matrix.cfg.gcc_v }} gcc --version g++ --version + pip install --upgrade --force-reinstall setuptools python -c "import sys; print(sys.version)" pip --version @@ -78,7 +83,7 @@ jobs: # Building lib + tests cmake -DCMAKE_INSTALL_PREFIX=$HOME/codac/build_install -DCMAKE_PREFIX_PATH=$HOME/ibex-lib/build_install -DWITH_PYTHON=OFF -DBUILD_TESTS=ON -DWITH_TUBE_TREE=OFF -DWITH_CAPD=OFF -DTEST_EXAMPLES=ON .. ##-DPYTHON_EXECUTABLE=/usr/bin/python3.5 .. - make + make -j 4 #make doc # todo make install #cd python/python_package From e091d35b839c4dbf9a788708fb44344424740ebb Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Sun, 20 Oct 2024 20:12:23 +0200 Subject: [PATCH 234/256] Update unixmatrix.yml --- .github/workflows/unixmatrix.yml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/.github/workflows/unixmatrix.yml b/.github/workflows/unixmatrix.yml index 54cbc662c..659b1e5b2 100644 --- a/.github/workflows/unixmatrix.yml +++ b/.github/workflows/unixmatrix.yml @@ -44,8 +44,11 @@ jobs: - { os: windows-2019, shell: cmd, arch: x86, bitness: 32, runtime: mingw8, cmake_params: '-G "MinGW Makefiles"', cmake_flags: '-fPIC', choco_flags: '--x86', desc: 'Windows MinGW 8.1.0 x86' } - { os: windows-2019, shell: cmd, arch: x64, bitness: 64, runtime: mingw7, cmake_params: '-G "MinGW Makefiles"', cmake_flags: '-fPIC', desc: 'Windows MinGW 7.3.0 x64' } - { os: windows-2019, shell: cmd, arch: x86, bitness: 32, runtime: mingw7, cmake_params: '-G "MinGW Makefiles"', cmake_flags: '-fPIC', choco_flags: '--x86', desc: 'Windows MinGW 7.3.0 x86' } + - { os: ubuntu-24.04, shell: bash, arch: amd64, bitness: 64, runtime: noble, cmake_flags: '-fPIC', deb: true, desc: 'Ubuntu 24.04 amd64' } - { os: ubuntu-22.04, shell: bash, arch: amd64, bitness: 64, runtime: jammy, cmake_flags: '-fPIC', deb: true, desc: 'Ubuntu 22.04 amd64' } - { os: ubuntu-20.04, shell: bash, arch: amd64, bitness: 64, runtime: focal, cmake_flags: '-fPIC', deb: true, desc: 'Ubuntu 20.04 amd64' } + - { os: macos-15, shell: bash, arch: arm64, bitness: 64, runtime: sequoia, cmake_params: '-D CMAKE_SYSTEM_NAME=Darwin -D CMAKE_OSX_ARCHITECTURES=arm64', cmake_flags: '-fPIC', desc: 'macOS Sequoia arm64' } + - { os: macos-15, shell: bash, arch: x86_64, bitness: 64, runtime: sequoia, cmake_params: '-D CMAKE_SYSTEM_NAME=Darwin -D CMAKE_OSX_ARCHITECTURES=x86_64', cmake_flags: '-fPIC', cross: true, desc: 'macOS Sequoia x86_64 (cross)' } - { os: macos-14, shell: bash, arch: arm64, bitness: 64, runtime: sonoma, cmake_params: '-D CMAKE_SYSTEM_NAME=Darwin -D CMAKE_OSX_ARCHITECTURES=arm64', cmake_flags: '-fPIC', desc: 'macOS Sonoma arm64' } - { os: macos-14, shell: bash, arch: x86_64, bitness: 64, runtime: sonoma, cmake_params: '-D CMAKE_SYSTEM_NAME=Darwin -D CMAKE_OSX_ARCHITECTURES=x86_64', cmake_flags: '-fPIC', cross: true, desc: 'macOS Sonoma x86_64 (cross)' } - { os: macos-13, shell: bash, arch: arm64, bitness: 64, runtime: ventura, cmake_params: '-D CMAKE_SYSTEM_NAME=Darwin -D CMAKE_OSX_ARCHITECTURES=arm64', cmake_flags: '-fPIC', cross: true, desc: 'macOS Ventura arm64 (cross)' } @@ -70,7 +73,7 @@ jobs: echo "CHOCO_PACKAGE_REV=" >> $GITHUB_ENV echo "VERBOSE=1" >> $GITHUB_ENV shell: bash - - run: if [ -z "$CHOCO_PACKAGE_REV" ]; then echo "PACKAGE_VERSION=$SOFTWARE_VERSION" >> $GITHUB_ENV ; else echo "PACKAGE_VERSION=$SOFTWARE_VERSION.$CHOCO_PACKAGE_REV" >> $GITHUB_ENV ; fi + - run: if [ -z "$CHOCO_PACKAGE_REV" ]; then echo "PACKAGE_VERSION=${SOFTWARE_VERSION/.dev*/-dev.$(date +%Y%m%d)}" >> $GITHUB_ENV ; else echo "PACKAGE_VERSION=$SOFTWARE_VERSION.$CHOCO_PACKAGE_REV" >> $GITHUB_ENV ; fi shell: bash if: runner.os=='Windows' - run: echo "PACKAGE_VERSION=$SOFTWARE_VERSION-${DEBIAN_PACKAGE_REV}${{ matrix.cfg.runtime }}$PACKAGE_REV" >> $GITHUB_ENV @@ -168,7 +171,7 @@ jobs: ./${{ matrix.cfg.test_config }}my_project cd ../.. shell: bash - if: matrix.cfg.cross!=true + if: (matrix.cfg.cross!=true)&&(github.ref_name!='codac4matlab') - run: | source ~/refreshenv.bashrc ; refreshenv ; export PATH=$BASHMINGWPATH:$PATH cd packages/choco @@ -220,4 +223,4 @@ jobs: ./${{ matrix.cfg.test_config }}my_project cd ../.. shell: bash - if: matrix.cfg.cross!=true + if: (matrix.cfg.cross!=true)&&(github.ref_name!='codac4matlab') From 28432caced171f93cc3c2408964918f5fb124bfe Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Sun, 20 Oct 2024 19:20:19 +0200 Subject: [PATCH 235/256] Update dockermatrix.yml --- .github/workflows/dockermatrix.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/dockermatrix.yml b/.github/workflows/dockermatrix.yml index b27617a9d..f36eba26f 100644 --- a/.github/workflows/dockermatrix.yml +++ b/.github/workflows/dockermatrix.yml @@ -17,7 +17,7 @@ jobs: cfg: - { img: 'lebarsfa/manylinux2014_x86_64-for-codac', shell: bash, arch: x86_64, bitness: 64, runtime: manylinux2014, cmake_flags: '-fPIC', desc: 'CentOS manylinux2014 x86_64' } - { img: 'lebarsfa/manylinux2014_aarch64-for-codac', shell: bash, arch: aarch64, bitness: 64, runtime: manylinux2014, cmake_flags: '-fPIC', docker_flags: '--platform linux/arm64', desc: 'CentOS manylinux2014 aarch64' } - - { img: 'lebarsfa/amd64:noble-for-codac', shell: bash, arch: amd64, bitness: 64, runtime: noble, cmake_flags: '-fPIC', deb: true, desc: 'Ubuntu 24.04 amd64' } + - { img: 'lebarsfa/pi-64:noble-for-codac', shell: bash, arch: arm64, bitness: 64, runtime: noble, cmake_flags: '-fPIC', deb: true, desc: 'Ubuntu 24.04 arm64' } - { img: 'lebarsfa/pi-64:jammy-for-codac', shell: bash, arch: arm64, bitness: 64, runtime: jammy, cmake_flags: '-fPIC', deb: true, desc: 'Ubuntu 22.04 arm64' } - { img: 'lebarsfa/pi-64:focal-for-codac', shell: bash, arch: arm64, bitness: 64, runtime: focal, cmake_flags: '-fPIC', deb: true, desc: 'Ubuntu 20.04 arm64' } - { img: 'lebarsfa/amd64:bookworm-for-codac', shell: bash, arch: amd64, bitness: 64, runtime: bookworm, cmake_flags: '-fPIC', deb: true, desc: 'Debian Bookworm amd64' } @@ -59,6 +59,7 @@ jobs: docker run ${{ matrix.cfg.docker_flags }} -i -v "${PWD}/..:${PWD}/.." ${{ matrix.cfg.img }} /bin/bash -c "uname -a ; cat /etc/os-release ; lsb_release -a ; cd ${PWD} && pwd && \ git config --global --add safe.directory ${PWD} && \ if [ ${{ matrix.cfg.deb }} = true ]; then \ + #sudo sh -c 'echo \"deb [trusted=yes] https://packages.ensta-bretagne.fr/\$(if [ -z \"\$(. /etc/os-release && echo \$UBUNTU_CODENAME)\" ]; then echo debian/\$(. /etc/os-release && echo \$VERSION_CODENAME); else echo ubuntu/\$(. /etc/os-release && echo \$UBUNTU_CODENAME); fi) ./\" > /etc/apt/sources.list.d/ensta-bretagne.list' && \\ sudo apt-get -q update ; sudo apt-get -y install libeigen3-dev dpkg-dev || true && \ wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20240417/libibex-dev-2.8.9.20240417-0${{ matrix.cfg.runtime }}0_\$(dpkg --print-architecture).deb --no-check-certificate -nv && \ sudo dpkg -i libibex-dev-2.8.9.20240417-0${{ matrix.cfg.runtime }}0_\$(dpkg --print-architecture).deb && \ @@ -75,7 +76,6 @@ jobs: cd .. && \ zip -q -r codac_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip codac && \ mkdir -p codac_standalone/example ; cd codac_standalone && \ - wget https://community.chocolatey.org/api/v2/package/eigen/3.4.0.20240224 --no-check-certificate -nv ; unzip -q 3.4.0.20240224 -d eigen ; rm -Rf 3.4.0.20240224 eigen/*.xml eigen/*.nuspec eigen/_* eigen/package eigen/tools && \ if [ ${{ matrix.cfg.deb }} = true ]; then mkdir -p ibex/include ; mkdir -p ibex/lib ; mkdir -p ibex/share ; mkdir -p ibex/bin ; cp -Rf /usr/include/ibex* ibex/include/ ; cp -Rf /usr/lib/*ibex* ibex/lib/ ; cp -Rf /usr/share/*ibex* ibex/share/ ; cp -Rf /usr/share/pkgconfig ibex/share/ ; cp -Rf /usr/bin/ibex* ibex/bin/ ; \ else cp -Rf ../ibex . ; \ fi && \ From 7fba83d289e6a5fb8919a35fd44ceb43c0343780 Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Sun, 20 Oct 2024 20:29:03 +0200 Subject: [PATCH 236/256] Branches management --- .github/workflows/dockercentos.yml | 13 ++++++++----- .github/workflows/dockermatrix.yml | 11 +++++++++-- .github/workflows/macosmatrix.yml | 9 +++++---- .github/workflows/tests.yml | 5 ++++- .github/workflows/unixmatrix.yml | 10 +++++----- .github/workflows/vcmatrix.yml | 9 +++++---- 6 files changed, 36 insertions(+), 21 deletions(-) diff --git a/.github/workflows/dockercentos.yml b/.github/workflows/dockercentos.yml index 309b3d6db..770f2b303 100644 --- a/.github/workflows/dockercentos.yml +++ b/.github/workflows/dockercentos.yml @@ -2,8 +2,9 @@ on: push: branches: - - master - - codac4matlab + - codac1 + - codac2 + - codac2_codac4matlab tags: '' # Restrict to blank tags pull_request: @@ -32,8 +33,10 @@ jobs: docker run --rm --privileged multiarch/qemu-user-static --reset -p yes if: (matrix.cfg.arch!='x86_64')&&(matrix.cfg.arch!='i386') - run: | - chmod a+x scripts/docker/build_pybinding.sh - docker run ${{ matrix.cfg.docker_flags }} --rm -v `pwd`:/io ${{ matrix.cfg.img }} /io/scripts/docker/build_pybinding.sh + chmod a+x scripts/docker/*.sh + if [ ${{ github.ref_name }} = 'codac2_codac4matlab' ] || [ ${{ github.event.pull_request.base.ref }} = 'codac2_codac4matlab' ]; then docker run ${{ matrix.cfg.docker_flags }} --rm -v `pwd`:/io ${{ matrix.cfg.img }} /io/scripts/docker/build_pybinding_codac4matlab.sh + else docker run ${{ matrix.cfg.docker_flags }} --rm -v `pwd`:/io ${{ matrix.cfg.img }} /io/scripts/docker/build_pybinding.sh + fi ls wheelhouse - uses: xresloader/upload-to-github-release@v1 env: @@ -42,4 +45,4 @@ jobs: file: "wheelhouse/*.whl" overwrite: true tag_name: autotagname-${{ github.sha }} - if: (github.event_name!='pull_request')&&((github.ref_name=='master')||(github.ref_name=='codac4matlab')) + if: (github.event_name!='pull_request')&&((github.ref_name=='codac1')||(github.ref_name=='codac2')||(github.ref_name=='codac2_codac4matlab')) diff --git a/.github/workflows/dockermatrix.yml b/.github/workflows/dockermatrix.yml index f36eba26f..194aa9c60 100644 --- a/.github/workflows/dockermatrix.yml +++ b/.github/workflows/dockermatrix.yml @@ -1,9 +1,16 @@ # This file checks that the lib runs on ARM on: push: - branches: 'master' + branches: + - codac1 + - codac2 + # The following is implied by above selection... + #branches-ignore: + #- codac2_codac4matlab tags: '' # Restrict to blank tags pull_request: + branches-ignore: + - codac2_codac4matlab jobs: dockermatrix: @@ -109,4 +116,4 @@ jobs: file: "*.zip;*.deb" overwrite: true tag_name: autotagname-${{ github.sha }} - if: (github.event_name != 'pull_request') + if: (github.event_name!='pull_request')&&((github.ref_name=='codac1')||(github.ref_name=='codac2')||(github.ref_name=='codac2_codac4matlab')) diff --git a/.github/workflows/macosmatrix.yml b/.github/workflows/macosmatrix.yml index d4d2c2e2f..1742f8827 100644 --- a/.github/workflows/macosmatrix.yml +++ b/.github/workflows/macosmatrix.yml @@ -2,8 +2,9 @@ on: push: branches: - - master - - codac4matlab + - codac1 + - codac2 + - codac2_codac4matlab tags: '' # Restrict to blank tags pull_request: @@ -79,7 +80,7 @@ jobs: file: "*.whl" overwrite: true tag_name: autotagname-${{ github.sha }} - if: (github.event_name!='pull_request')&&((github.ref_name=='master')||(github.ref_name=='codac4matlab')) + if: (github.event_name!='pull_request')&&((github.ref_name=='codac1')||(github.ref_name=='codac2')||(github.ref_name=='codac2_codac4matlab')) - run: | pip install --no-deps --no-index *.whl python -c "import sys; print(sys.version)" ; python examples/tuto/01_getting_started/01_getting_started.py @@ -88,4 +89,4 @@ jobs: #cd build && ctest -C Release -V --output-on-failure #cd .. shell: bash - if: (matrix.cfg.cross!=true)&&(github.ref_name!='codac4matlab') + if: (matrix.cfg.cross!=true)&&(github.ref_name!='codac2_codac4matlab')&&(github.event.pull_request.base.ref!='codac2_codac4matlab') diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 24de99eac..93e3b198d 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -1,9 +1,12 @@ # Unit tests on: push: - branches: '**' + branches-ignore: + - codac2_codac4matlab tags: '' # Restrict to blank tags pull_request: + branches-ignore: + - codac2_codac4matlab jobs: tests: diff --git a/.github/workflows/unixmatrix.yml b/.github/workflows/unixmatrix.yml index 659b1e5b2..a191e0232 100644 --- a/.github/workflows/unixmatrix.yml +++ b/.github/workflows/unixmatrix.yml @@ -6,9 +6,9 @@ on: pull_request: jobs: - # This job may be commented if a new release should not be created... deploy: runs-on: ubuntu-latest + if: (github.event_name!='pull_request')&&((github.ref_name=='codac1')||(github.ref_name=='codac2')||(github.ref_name=='codac2_codac4matlab')) steps: - uses: softprops/action-gh-release@v2 id: create_release @@ -17,10 +17,10 @@ jobs: with: draft: true tag_name: autotagname-${{ github.sha }} - if: (github.event_name != 'pull_request')&&(github.ref_name == 'master') unixmatrix: runs-on: ${{ matrix.cfg.os }} + if: (github.ref_name!='codac2_codac4matlab')&&(github.event.pull_request.base.ref!='codac2_codac4matlab') defaults: run: shell: ${{ matrix.cfg.shell }} @@ -171,7 +171,7 @@ jobs: ./${{ matrix.cfg.test_config }}my_project cd ../.. shell: bash - if: (matrix.cfg.cross!=true)&&(github.ref_name!='codac4matlab') + if: (matrix.cfg.cross!=true) - run: | source ~/refreshenv.bashrc ; refreshenv ; export PATH=$BASHMINGWPATH:$PATH cd packages/choco @@ -212,7 +212,7 @@ jobs: file: "*.zip;*.nupkg;*.deb" overwrite: true tag_name: autotagname-${{ github.sha }} - if: (github.event_name != 'pull_request')&&(github.ref_name == 'master') + if: (github.event_name!='pull_request')&&((github.ref_name=='codac1')||(github.ref_name=='codac2')||(github.ref_name=='codac2_codac4matlab')) - run: | if [ ${{ runner.os }} = Windows ]; then source ~/refreshenv.bashrc ; refreshenv ; export PATH=$BASHMINGWPATH:$PATH ; fi rm -Rf codac @@ -223,4 +223,4 @@ jobs: ./${{ matrix.cfg.test_config }}my_project cd ../.. shell: bash - if: (matrix.cfg.cross!=true)&&(github.ref_name!='codac4matlab') + if: (matrix.cfg.cross!=true) diff --git a/.github/workflows/vcmatrix.yml b/.github/workflows/vcmatrix.yml index 5cf28d0fa..7d9d0460a 100644 --- a/.github/workflows/vcmatrix.yml +++ b/.github/workflows/vcmatrix.yml @@ -2,8 +2,9 @@ on: push: branches: - - master - - codac4matlab + - codac1 + - codac2 + - codac2_codac4matlab tags: '' # Restrict to blank tags pull_request: @@ -79,7 +80,7 @@ jobs: file: "*.whl" overwrite: true tag_name: autotagname-${{ github.sha }} - if: (github.event_name!='pull_request')&&((github.ref_name=='master')||(github.ref_name=='codac4matlab')) + if: (github.event_name!='pull_request')&&((github.ref_name=='codac1')||(github.ref_name=='codac2')||(github.ref_name=='codac2_codac4matlab')) - run: | pip install --no-deps --no-index *.whl python -c "import sys; print(sys.version)" ; python examples/tuto/01_getting_started/01_getting_started.py @@ -88,4 +89,4 @@ jobs: #cd build && ctest -C Release -V --output-on-failure #cd .. shell: bash - if: (github.ref_name!='codac4matlab') + if: (github.ref_name!='codac2_codac4matlab')&&(github.event.pull_request.base.ref!='codac2_codac4matlab') From 7e843914e15101289cb3d1a1b95d6bf25fff48b0 Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Sun, 20 Oct 2024 15:45:35 +0200 Subject: [PATCH 237/256] macOS Monterey EOL --- .github/workflows/macosmatrix.yml | 26 +++++++++++++------------- .github/workflows/unixmatrix.yml | 2 -- 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/.github/workflows/macosmatrix.yml b/.github/workflows/macosmatrix.yml index 1742f8827..a99812df2 100644 --- a/.github/workflows/macosmatrix.yml +++ b/.github/workflows/macosmatrix.yml @@ -24,19 +24,19 @@ jobs: - { os: macos-14, shell: bash, arch: arm64, runtime: sonoma, cmake_flags: '-fPIC', trgt: '11.0', cpcfg: '-macosx_11_0_arm64', py_v_maj: 3, py_v_min: 13, desc: 'macOS Sonoma Python 3.13 arm64' } - { os: macos-14, shell: bash, arch: arm64, runtime: sonoma, cmake_flags: '-fPIC', trgt: '11.0', cpcfg: '-macosx_11_0_arm64', py_v_maj: 3, py_v_min: 12, desc: 'macOS Sonoma Python 3.12 arm64' } - { os: macos-14, shell: bash, arch: arm64, runtime: sonoma, cmake_flags: '-fPIC', trgt: '11.0', cpcfg: '-macosx_11_0_arm64', py_v_maj: 3, py_v_min: 11, desc: 'macOS Sonoma Python 3.11 arm64' } - - { os: macos-12, shell: bash, arch: arm64, runtime: monterey, cmake_flags: '-fPIC', trgt: '11.0', cpcfg: '-macosx_11_0_arm64', py_v_maj: 3, py_v_min: 10, cross: true, desc: 'macOS Monterey Python 3.10 arm64 (cross)' } - - { os: macos-12, shell: bash, arch: arm64, runtime: monterey, cmake_flags: '-fPIC', trgt: '11.0', cpcfg: '-macosx_11_0_arm64', py_v_maj: 3, py_v_min: 9, cross: true, desc: 'macOS Monterey Python 3.9 arm64 (cross)' } - - { os: macos-12, shell: bash, arch: arm64, runtime: monterey, cmake_flags: '-fPIC', trgt: '11.0', cpcfg: '-macosx_11_0_arm64', py_v_maj: 3, py_v_min: 8, cross: true, desc: 'macOS Monterey Python 3.8 arm64 (cross)' } - - { os: macos-12, shell: bash, arch: arm64, runtime: monterey, cmake_flags: '-fPIC', trgt: '11.0', cpcfg: 'm-macosx_11_0_arm64', py_v_maj: 3, py_v_min: 7, cross: true, desc: 'macOS Monterey Python 3.7 arm64 (cross)' } - - { os: macos-12, shell: bash, arch: arm64, runtime: monterey, cmake_flags: '-fPIC', trgt: '11.0', cpcfg: 'm-macosx_11_0_arm64', py_v_maj: 3, py_v_min: 6, cross: true, desc: 'macOS Monterey Python 3.6 arm64 (cross)' } - - { os: macos-12, shell: bash, arch: x86_64, runtime: monterey, cmake_flags: '-fPIC', trgt: '10.9', cpcfg: '-macosx_10_9_x86_64', py_v_maj: 3, py_v_min: 13, desc: 'macOS Monterey Python 3.13 x86_64' } - - { os: macos-12, shell: bash, arch: x86_64, runtime: monterey, cmake_flags: '-fPIC', trgt: '10.9', cpcfg: '-macosx_10_9_x86_64', py_v_maj: 3, py_v_min: 12, desc: 'macOS Monterey Python 3.12 x86_64' } - - { os: macos-12, shell: bash, arch: x86_64, runtime: monterey, cmake_flags: '-fPIC', trgt: '10.9', cpcfg: '-macosx_10_9_x86_64', py_v_maj: 3, py_v_min: 11, desc: 'macOS Monterey Python 3.11 x86_64' } - - { os: macos-12, shell: bash, arch: x86_64, runtime: monterey, cmake_flags: '-fPIC', trgt: '10.9', cpcfg: '-macosx_10_9_x86_64', py_v_maj: 3, py_v_min: 10, desc: 'macOS Monterey Python 3.10 x86_64' } - - { os: macos-12, shell: bash, arch: x86_64, runtime: monterey, cmake_flags: '-fPIC', trgt: '10.9', cpcfg: '-macosx_10_9_x86_64', py_v_maj: 3, py_v_min: 9, desc: 'macOS Monterey Python 3.9 x86_64' } - - { os: macos-12, shell: bash, arch: x86_64, runtime: monterey, cmake_flags: '-fPIC', trgt: '10.9', cpcfg: '-macosx_10_9_x86_64', py_v_maj: 3, py_v_min: 8, desc: 'macOS Monterey Python 3.8 x86_64' } - - { os: macos-12, shell: bash, arch: x86_64, runtime: monterey, cmake_flags: '-fPIC', trgt: '10.9', cpcfg: 'm-macosx_10_9_x86_64', py_v_maj: 3, py_v_min: 7, desc: 'macOS Monterey Python 3.7 x86_64' } - - { os: macos-12, shell: bash, arch: x86_64, runtime: monterey, cmake_flags: '-fPIC', trgt: '10.14', cpcfg: 'm-macosx_10_14_x86_64', py_v_maj: 3, py_v_min: 6, desc: 'macOS Monterey Python 3.6 x86_64' } # 10.14 because of error $MACOSX_DEPLOYMENT_TARGET mismatch: now "10.9" but "10.14" during configure. + - { os: macos-13, shell: bash, arch: arm64, runtime: ventura, cmake_flags: '-fPIC', trgt: '11.0', cpcfg: '-macosx_11_0_arm64', py_v_maj: 3, py_v_min: 10, cross: true, desc: 'macOS Ventura Python 3.10 arm64 (cross)' } + - { os: macos-13, shell: bash, arch: arm64, runtime: ventura, cmake_flags: '-fPIC', trgt: '11.0', cpcfg: '-macosx_11_0_arm64', py_v_maj: 3, py_v_min: 9, cross: true, desc: 'macOS Ventura Python 3.9 arm64 (cross)' } + - { os: macos-13, shell: bash, arch: arm64, runtime: ventura, cmake_flags: '-fPIC', trgt: '11.0', cpcfg: '-macosx_11_0_arm64', py_v_maj: 3, py_v_min: 8, cross: true, desc: 'macOS Ventura Python 3.8 arm64 (cross)' } + - { os: macos-13, shell: bash, arch: arm64, runtime: ventura, cmake_flags: '-fPIC', trgt: '11.0', cpcfg: 'm-macosx_11_0_arm64', py_v_maj: 3, py_v_min: 7, cross: true, desc: 'macOS Ventura Python 3.7 arm64 (cross)' } + - { os: macos-13, shell: bash, arch: arm64, runtime: ventura, cmake_flags: '-fPIC', trgt: '11.0', cpcfg: 'm-macosx_11_0_arm64', py_v_maj: 3, py_v_min: 6, cross: true, desc: 'macOS Ventura Python 3.6 arm64 (cross)' } + - { os: macos-13, shell: bash, arch: x86_64, runtime: ventura, cmake_flags: '-fPIC', trgt: '10.9', cpcfg: '-macosx_10_9_x86_64', py_v_maj: 3, py_v_min: 13, desc: 'macOS Ventura Python 3.13 x86_64' } + - { os: macos-13, shell: bash, arch: x86_64, runtime: ventura, cmake_flags: '-fPIC', trgt: '10.9', cpcfg: '-macosx_10_9_x86_64', py_v_maj: 3, py_v_min: 12, desc: 'macOS Ventura Python 3.12 x86_64' } + - { os: macos-13, shell: bash, arch: x86_64, runtime: ventura, cmake_flags: '-fPIC', trgt: '10.9', cpcfg: '-macosx_10_9_x86_64', py_v_maj: 3, py_v_min: 11, desc: 'macOS Ventura Python 3.11 x86_64' } + - { os: macos-13, shell: bash, arch: x86_64, runtime: ventura, cmake_flags: '-fPIC', trgt: '10.9', cpcfg: '-macosx_10_9_x86_64', py_v_maj: 3, py_v_min: 10, desc: 'macOS Ventura Python 3.10 x86_64' } + - { os: macos-13, shell: bash, arch: x86_64, runtime: ventura, cmake_flags: '-fPIC', trgt: '10.9', cpcfg: '-macosx_10_9_x86_64', py_v_maj: 3, py_v_min: 9, desc: 'macOS Ventura Python 3.9 x86_64' } + - { os: macos-13, shell: bash, arch: x86_64, runtime: ventura, cmake_flags: '-fPIC', trgt: '10.9', cpcfg: '-macosx_10_9_x86_64', py_v_maj: 3, py_v_min: 8, desc: 'macOS Ventura Python 3.8 x86_64' } + - { os: macos-13, shell: bash, arch: x86_64, runtime: ventura, cmake_flags: '-fPIC', trgt: '10.9', cpcfg: 'm-macosx_10_9_x86_64', py_v_maj: 3, py_v_min: 7, desc: 'macOS Ventura Python 3.7 x86_64' } + - { os: macos-13, shell: bash, arch: x86_64, runtime: ventura, cmake_flags: '-fPIC', trgt: '10.14', cpcfg: 'm-macosx_10_14_x86_64', py_v_maj: 3, py_v_min: 6, desc: 'macOS Ventura Python 3.6 x86_64' } # 10.14 because of error $MACOSX_DEPLOYMENT_TARGET mismatch: now "10.9" but "10.14" during configure. name: ${{ matrix.cfg.desc }} steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/unixmatrix.yml b/.github/workflows/unixmatrix.yml index a191e0232..8e0ea167e 100644 --- a/.github/workflows/unixmatrix.yml +++ b/.github/workflows/unixmatrix.yml @@ -53,8 +53,6 @@ jobs: - { os: macos-14, shell: bash, arch: x86_64, bitness: 64, runtime: sonoma, cmake_params: '-D CMAKE_SYSTEM_NAME=Darwin -D CMAKE_OSX_ARCHITECTURES=x86_64', cmake_flags: '-fPIC', cross: true, desc: 'macOS Sonoma x86_64 (cross)' } - { os: macos-13, shell: bash, arch: arm64, bitness: 64, runtime: ventura, cmake_params: '-D CMAKE_SYSTEM_NAME=Darwin -D CMAKE_OSX_ARCHITECTURES=arm64', cmake_flags: '-fPIC', cross: true, desc: 'macOS Ventura arm64 (cross)' } - { os: macos-13, shell: bash, arch: x86_64, bitness: 64, runtime: ventura, cmake_params: '-D CMAKE_SYSTEM_NAME=Darwin -D CMAKE_OSX_ARCHITECTURES=x86_64', cmake_flags: '-fPIC', desc: 'macOS Ventura x86_64' } - - { os: macos-12, shell: bash, arch: arm64, bitness: 64, runtime: monterey, cmake_params: '-D CMAKE_SYSTEM_NAME=Darwin -D CMAKE_OSX_ARCHITECTURES=arm64', cmake_flags: '-fPIC', cross: true, desc: 'macOS Monterey arm64 (cross)' } - - { os: macos-12, shell: bash, arch: x86_64, bitness: 64, runtime: monterey, cmake_params: '-D CMAKE_SYSTEM_NAME=Darwin -D CMAKE_OSX_ARCHITECTURES=x86_64', cmake_flags: '-fPIC', desc: 'macOS Monterey x86_64' } name: ${{ matrix.cfg.desc }} steps: - uses: actions/checkout@v4 From ebf52d39899dc3e66f3600a8d5b9f3e78cc67e04 Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Sun, 3 Nov 2024 15:58:09 +0100 Subject: [PATCH 238/256] Update doc about CMake project install problem on Windows --- doc/doc/install/01-installation-full-windows.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/doc/install/01-installation-full-windows.rst b/doc/doc/install/01-installation-full-windows.rst index d52a27757..3580d6fd9 100644 --- a/doc/doc/install/01-installation-full-windows.rst +++ b/doc/doc/install/01-installation-full-windows.rst @@ -49,4 +49,8 @@ The logic to follow will then be similar to `Linux <01-installation-full-linux.h cmake --build . --config Release --target install +.. warning:: + + | You might need to replace all occurences of :literal:`PATH_SUFFIXES \ ` with something similar to :literal:`PATHS ${CMAKE_CURRENT_LIST_FILE}/../../../../ PATH_SUFFIXES \ ` in all ``.cmake`` in ``codac/share/codac/cmake/`` (where Codac was installed) if a CMake project that tries to use Codac appears to find its installation location but fails to configure the project properly. + See also `Information for developers `_. From 18fc8d1d0d1c85b82eb9d713b24f69b75850029a Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Sun, 3 Nov 2024 19:44:20 +0100 Subject: [PATCH 239/256] Test specific IBEX version --- .github/workflows/dockermatrix.yml | 8 ++++---- .github/workflows/macosmatrix.yml | 2 +- .github/workflows/unixmatrix.yml | 14 +++++++------- .github/workflows/vcmatrix.yml | 6 +++--- packages/temporary/gennewcodacpi_armhf.sh | 4 ++-- scripts/dependencies/install_ibex.sh | 3 ++- scripts/docker/build_pybinding.sh | 2 +- 7 files changed, 20 insertions(+), 19 deletions(-) diff --git a/.github/workflows/dockermatrix.yml b/.github/workflows/dockermatrix.yml index 194aa9c60..6f6386100 100644 --- a/.github/workflows/dockermatrix.yml +++ b/.github/workflows/dockermatrix.yml @@ -68,11 +68,11 @@ jobs: if [ ${{ matrix.cfg.deb }} = true ]; then \ #sudo sh -c 'echo \"deb [trusted=yes] https://packages.ensta-bretagne.fr/\$(if [ -z \"\$(. /etc/os-release && echo \$UBUNTU_CODENAME)\" ]; then echo debian/\$(. /etc/os-release && echo \$VERSION_CODENAME); else echo ubuntu/\$(. /etc/os-release && echo \$UBUNTU_CODENAME); fi) ./\" > /etc/apt/sources.list.d/ensta-bretagne.list' && \\ sudo apt-get -q update ; sudo apt-get -y install libeigen3-dev dpkg-dev || true && \ - wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20240417/libibex-dev-2.8.9.20240417-0${{ matrix.cfg.runtime }}0_\$(dpkg --print-architecture).deb --no-check-certificate -nv && \ - sudo dpkg -i libibex-dev-2.8.9.20240417-0${{ matrix.cfg.runtime }}0_\$(dpkg --print-architecture).deb && \ - rm -Rf libibex-dev-2.8.9.20240417-0${{ matrix.cfg.runtime }}0_\$(dpkg --print-architecture).deb ; \ + wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20241103/libibex-dev-2.8.9.20241103-0${{ matrix.cfg.runtime }}0_\$(dpkg --print-architecture).deb --no-check-certificate -nv && \ + sudo dpkg -i libibex-dev-2.8.9.20241103-0${{ matrix.cfg.runtime }}0_\$(dpkg --print-architecture).deb && \ + rm -Rf libibex-dev-2.8.9.20241103-0${{ matrix.cfg.runtime }}0_\$(dpkg --print-architecture).deb ; \ else \ - wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20240417/ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip --no-check-certificate -nv && \ + wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20241103/ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip --no-check-certificate -nv && \ unzip -q ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip && \ rm -Rf ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip && \ sudo cp -Rf ibex/* /usr/ ; \ diff --git a/.github/workflows/macosmatrix.yml b/.github/workflows/macosmatrix.yml index a99812df2..c41d53e96 100644 --- a/.github/workflows/macosmatrix.yml +++ b/.github/workflows/macosmatrix.yml @@ -61,7 +61,7 @@ jobs: - run: brew install graphviz ; wget https://github.com/Homebrew/homebrew-core/raw/d2267b9f2ad247bc9c8273eb755b39566a474a70/Formula/doxygen.rb ; brew reinstall --formula ./doxygen.rb ; brew pin doxygen ; python -m pip install --upgrade pip ; pip install --upgrade wheel setuptools sphinx breathe sphinx_rtd_theme sphinx-tabs sphinx-issues sphinx-reredirects if: runner.os=='macOS' - run: | - wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20240417/ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip --no-check-certificate -nv + wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20241103/ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip --no-check-certificate -nv unzip -q ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip rm -Rf ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip sudo cp -Rf ibex/* /usr/local/ diff --git a/.github/workflows/unixmatrix.yml b/.github/workflows/unixmatrix.yml index 8e0ea167e..063452282 100644 --- a/.github/workflows/unixmatrix.yml +++ b/.github/workflows/unixmatrix.yml @@ -117,23 +117,23 @@ jobs: if: (matrix.cfg.runtime=='mingw13') - run: | choco install -y -r --no-progress eigen --version=3.4.0.20240224 ${{ matrix.cfg.choco_flags }} - wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20240417/ibex.2.8.9.20240417.nupkg --no-check-certificate -nv - choco install -y -r --no-progress --ignore-dependencies -s . ibex --version=2.8.9.20240417 ${{ matrix.cfg.choco_flags }} --params "'/url:https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20240417/ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip'" - del /f /q ibex.2.8.9.20240417.nupkg + wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20241103/ibex.2.8.9.20241103.nupkg --no-check-certificate -nv + choco install -y -r --no-progress --ignore-dependencies -s . ibex --version=2.8.9.20241103 ${{ matrix.cfg.choco_flags }} --params "'/url:https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20241103/ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip'" + del /f /q ibex.2.8.9.20241103.nupkg if: runner.os=='Windows' - run: | # Replace these 2 lines by the next ones to test a specific binary package of IBEX. #sudo sh -c 'echo "deb [trusted=yes] https://packages.ensta-bretagne.fr/$(if [ -z "$(. /etc/os-release && echo $UBUNTU_CODENAME)" ]; then echo debian/$(. /etc/os-release && echo $VERSION_CODENAME); else echo ubuntu/$(. /etc/os-release && echo $UBUNTU_CODENAME); fi) ./" > /etc/apt/sources.list.d/ensta-bretagne.list' #sudo apt-get -q update ; sudo apt-get -y install libibex-dev libeigen3-dev dpkg-dev || true sudo apt-get -q update ; sudo apt-get -y install libeigen3-dev dpkg-dev || true - wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20240417/libibex-dev-2.8.9.20240417-0${{ matrix.cfg.runtime }}0_$(dpkg --print-architecture).deb --no-check-certificate -nv - sudo dpkg -i libibex-dev-2.8.9.20240417-0${{ matrix.cfg.runtime }}0_$(dpkg --print-architecture).deb - rm -Rf libibex-dev-2.8.9.20240417-0${{ matrix.cfg.runtime }}0_$(dpkg --print-architecture).deb + wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20241103/libibex-dev-2.8.9.20241103-0${{ matrix.cfg.runtime }}0_$(dpkg --print-architecture).deb --no-check-certificate -nv + sudo dpkg -i libibex-dev-2.8.9.20241103-0${{ matrix.cfg.runtime }}0_$(dpkg --print-architecture).deb + rm -Rf libibex-dev-2.8.9.20241103-0${{ matrix.cfg.runtime }}0_$(dpkg --print-architecture).deb shell: bash if: matrix.cfg.deb==true - run: | brew install eigen - wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20240417/ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip --no-check-certificate -nv + wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20241103/ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip --no-check-certificate -nv unzip -q ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip rm -Rf ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip sudo cp -Rf ibex/* /usr/local/ diff --git a/.github/workflows/vcmatrix.yml b/.github/workflows/vcmatrix.yml index 7d9d0460a..5dfecfe79 100644 --- a/.github/workflows/vcmatrix.yml +++ b/.github/workflows/vcmatrix.yml @@ -63,9 +63,9 @@ jobs: - run: wget https://packages.ensta-bretagne.fr/choco/doxygen.install.1.9.6.nupkg --no-check-certificate -nv & choco upgrade -y -r --no-progress -s . doxygen.install --version=1.9.6 & del /f /q doxygen.install.1.9.6.nupkg & choco install -y -r --no-progress graphviz & python -m pip install --upgrade pip & pip install --upgrade wheel setuptools sphinx breathe sphinx-issues sphinx-tabs sphinx_rtd_theme sphinx-reredirects if: runner.os=='Windows' - run: | - wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20240417/ibex.2.8.9.20240417.nupkg --no-check-certificate -nv - choco install -y -r --no-progress --ignore-dependencies -s . ibex --version=2.8.9.20240417 ${{ matrix.cfg.choco_flags }} --params "'/url:https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20240417/ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip'" - del /f /q ibex.2.8.9.20240417.nupkg + wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20241103/ibex.2.8.9.20241103.nupkg --no-check-certificate -nv + choco install -y -r --no-progress --ignore-dependencies -s . ibex --version=2.8.9.20241103 ${{ matrix.cfg.choco_flags }} --params "'/url:https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20241103/ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip'" + del /f /q ibex.2.8.9.20241103.nupkg - run: | mkdir build ; cd build cmake -E env CXXFLAGS=" /wd4267 /wd4244 /wd4305 /wd4996" CFLAGS=" /wd4267 /wd4244 /wd4305 /wd4996" cmake ${{ matrix.cfg.cmake_params }} -D CMAKE_INSTALL_PREFIX="../codac" -D WITH_PYTHON=ON .. diff --git a/packages/temporary/gennewcodacpi_armhf.sh b/packages/temporary/gennewcodacpi_armhf.sh index 0c4e1992f..bd34e6537 100644 --- a/packages/temporary/gennewcodacpi_armhf.sh +++ b/packages/temporary/gennewcodacpi_armhf.sh @@ -26,8 +26,8 @@ fi && \ sudo apt-get -q update --allow-releaseinfo-change ; sudo apt-get -y install libeigen3-dev python3-dev patchelf || true && \ python3 -m pip install \$PIP_OPTIONS --upgrade patchelf --prefer-binary --extra-index-url https://www.piwheels.org/simple && \ python3 -m pip install \$PIP_OPTIONS --upgrade auditwheel --prefer-binary --extra-index-url https://www.piwheels.org/simple && \ -# wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20240417/ibex_armhf_\$(lsb_release -cs).zip --no-check-certificate -nv is causing illegal instruction on a Mac M1... \\ -curl -L -O https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20240417/ibex_armhf_\$(lsb_release -cs).zip --insecure && \ +# wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20241103/ibex_armhf_\$(lsb_release -cs).zip --no-check-certificate -nv is causing illegal instruction on a Mac M1... \\ +curl -L -O https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20241103/ibex_armhf_\$(lsb_release -cs).zip --insecure && \ unzip -q ibex_armhf_\$(lsb_release -cs).zip && \ rm -Rf ibex_armhf_\$(lsb_release -cs).zip && \ sudo cp -Rf ibex/* /usr/local/ && \ diff --git a/scripts/dependencies/install_ibex.sh b/scripts/dependencies/install_ibex.sh index e115fb321..069399baa 100644 --- a/scripts/dependencies/install_ibex.sh +++ b/scripts/dependencies/install_ibex.sh @@ -3,7 +3,8 @@ cd $HOME echo 'Installing IBEX in ' $HOME '...'; if [ ! -e "ibex-lib/README.md" ]; then - git clone -b master https://github.com/lebarsfa/ibex-lib.git ; + #git clone -b master https://github.com/lebarsfa/ibex-lib.git ; + git clone -b ibex-2.8.9.20241103 https://github.com/lebarsfa/ibex-lib.git ; # To test a specific version of IBEX... cd ibex-lib ; mkdir build && cd build ; cmake -E env CXXFLAGS="-fPIC" CFLAGS="-fPIC" cmake -DCMAKE_INSTALL_PREFIX=$HOME/ibex-lib/build_install -DCMAKE_BUILD_TYPE=Debug .. ; diff --git a/scripts/docker/build_pybinding.sh b/scripts/docker/build_pybinding.sh index 4970615ba..3df4a5ee7 100755 --- a/scripts/docker/build_pybinding.sh +++ b/scripts/docker/build_pybinding.sh @@ -2,7 +2,7 @@ set -e -x -wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20240417/ibex_$(uname -m)_manylinux2014.zip --no-check-certificate -nv +wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20241103/ibex_$(uname -m)_manylinux2014.zip --no-check-certificate -nv unzip -q ibex_$(uname -m)_manylinux2014.zip rm -Rf ibex_$(uname -m)_manylinux2014.zip sudo cp -Rf ibex/* /usr/local/ From b30142e2b055ff311beef6e05cbf14681f5693ce Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Sun, 17 Nov 2024 14:29:07 +0100 Subject: [PATCH 240/256] Update doc to remove occurrences of old website www.ibex-lib.org --- doc/doc/index.rst | 6 +++--- doc/doc/install/01-installation-full-linux.rst | 2 +- doc/doc/manual/01-introduction/index.rst | 2 +- doc/doc/manual/02-variables/01-var-static.rst | 2 +- doc/doc/manual/02-variables/02-var-dynamic.rst | 4 ++-- doc/doc/manual/03-domains/01-dom-intervals.rst | 6 +++--- doc/doc/manual/04-static-contractors/01-ctc-function.rst | 4 ++-- doc/doc/tutorial-jnrr23/01-basics/index.rst | 2 +- doc/doc/tutorial/01-basics/index.rst | 2 +- doc/doc/tutorial/03-static-rangebearing/index.rst | 2 +- .../core/domains/interval/codac_py_IntervalVector_docs.h | 2 +- python/src/core/domains/interval/codac_py_Interval_docs.h | 2 +- python/src/core/domains/interval/codac_py_bisectors_docs.h | 2 +- src/core/separators/codac_SepProj.h | 2 +- 14 files changed, 20 insertions(+), 20 deletions(-) diff --git a/doc/doc/index.rst b/doc/doc/index.rst index d9ee818c2..6486a01fb 100644 --- a/doc/doc/index.rst +++ b/doc/doc/index.rst @@ -60,7 +60,7 @@ In a nutshell, Codac is a **constraint programming framework** providing tools t .. **Mobile robotics** has been the initial motivation of this project: the mathematical tools provided in Codac come together with robotic applications. -.. Computations stands on the `IBEX library `_ that provides reliable tools for static systems. +.. Computations stands on the `IBEX library `_ that provides reliable tools for static systems. Getting started: 2 minutes to Codac @@ -254,8 +254,8 @@ Then you have two options: read the details about the features of Codac (domains .. Figure:: img/logo_ibex.jpg :align: center - | Note that Codac stands on the `IBEX library `_ for interval analysis computations and static contractors on boxes. - | `Read the IBEX documentation. `_ + | Note that Codac stands on the `IBEX library `_ for interval analysis computations and static contractors on boxes. + | `Read the IBEX documentation. `_ .. .. rubric:: pyIbex .. diff --git a/doc/doc/install/01-installation-full-linux.rst b/doc/doc/install/01-installation-full-linux.rst index c006f25a5..5047c14c2 100644 --- a/doc/doc/install/01-installation-full-linux.rst +++ b/doc/doc/install/01-installation-full-linux.rst @@ -55,7 +55,7 @@ In case you prefer the latest development version, Codac can be installed by com Requirements ^^^^^^^^^^^^ -Codac uses several features of the `IBEX library `_ that you have to install first. The last version of IBEX is maintained on `this unofficial development repository `_: +Codac uses several features of the `IBEX library `_ that you have to install first. The last version of IBEX is maintained on `this unofficial development repository `_: .. code-block:: bash diff --git a/doc/doc/manual/01-introduction/index.rst b/doc/doc/manual/01-introduction/index.rst index 2daf4c40a..ec6af2c57 100644 --- a/doc/doc/manual/01-introduction/index.rst +++ b/doc/doc/manual/01-introduction/index.rst @@ -41,7 +41,7 @@ What about the IBEX library? .. Figure:: ../../img/logo_ibex.jpg :align: left -The `IBEX library `_ is a C++ software for constraint processing over real numbers. +The `IBEX library `_ is a C++ software for constraint processing over real numbers. As for Codac, it stands on Constraint Programming but focuses on static contexts, providing reliable algorithms for handling non-linear constraints. It also proposes various tools such as the *IbexSolve* and *IbexOpt* plugins that are dedicated to system solving and optimization, and come both with a default black-box solver and global optimizer for immediate usage. diff --git a/doc/doc/manual/02-variables/01-var-static.rst b/doc/doc/manual/02-variables/01-var-static.rst index aa3aaea89..809d86d3b 100644 --- a/doc/doc/manual/02-variables/01-var-static.rst +++ b/doc/doc/manual/02-variables/01-var-static.rst @@ -16,7 +16,7 @@ Note also that they are not involved during the process of constraint propagatio .. Figure:: ../../img/logo_ibex.jpg :align: right - These static variables come from the `IBEX library `_. They are briefly presented here for the sake of consistency. For more information, please refer to the `IBEX documentation `_ for C++ use. + These static variables come from the `IBEX library `_. They are briefly presented here for the sake of consistency. For more information, please refer to the `IBEX documentation `_ for C++ use. .. _sec-manual-varstatic-vectors: diff --git a/doc/doc/manual/02-variables/02-var-dynamic.rst b/doc/doc/manual/02-variables/02-var-dynamic.rst index d728a8b39..b93688dd0 100644 --- a/doc/doc/manual/02-variables/02-var-dynamic.rst +++ b/doc/doc/manual/02-variables/02-var-dynamic.rst @@ -35,7 +35,7 @@ A simple temporal function [#f1]_ can be defined and used for building a traject Interval tdomain(0.,10.); // temporal domain: [t_0,t_f] Trajectory x(tdomain, TFunction("cos(t)+sin(2*t)")); // defining x(·) as: t ↦ cos(t)+sin(2t) -Usual functions such as :math:`\cos`, :math:`\log`, *etc.* can be used to define a ``TFunction`` object. The convention used for these definitions is the one of IBEX (`read more `_). Note that the system variable :math:`t` is a predefined variable of ``TFunction`` objects, that does not appear in IBEX's objects. +Usual functions such as :math:`\cos`, :math:`\log`, *etc.* can be used to define a ``TFunction`` object. The convention used for these definitions is the one of IBEX (`read more `_). Note that the system variable :math:`t` is a predefined variable of ``TFunction`` objects, that does not appear in IBEX's objects. The evaluation of a trajectory at some time :math:`t`, for instance :math:`z=x(t)`, is performed with parentheses: @@ -472,7 +472,7 @@ Next pages will present several methods to use *tubes* that are envelopes of tra .. rubric:: Footnotes -.. [#f1] In Codac, a ``codac::TFunction`` is the extension of IBEX's ``Function`` objects, for the dynamical case (see more `about IBEX's functions `_). +.. [#f1] In Codac, a ``codac::TFunction`` is the extension of IBEX's ``Function`` objects, for the dynamical case (see more `about IBEX's functions `_). .. admonition:: Technical documentation diff --git a/doc/doc/manual/03-domains/01-dom-intervals.rst b/doc/doc/manual/03-domains/01-dom-intervals.rst index 10d9dc541..dbcd2dec2 100644 --- a/doc/doc/manual/03-domains/01-dom-intervals.rst +++ b/doc/doc/manual/03-domains/01-dom-intervals.rst @@ -14,7 +14,7 @@ The vectors :math:`\mathbf{x}` and matrices :math:`\mathbf{X}`, presented :ref:` .. Figure:: ../../img/logo_ibex.jpg :align: right - These interval variables come from the `IBEX library `_. They are briefly presented here for the sake of consistency. For more information, please refer to the `IBEX documentation `_ for C++ use. + These interval variables come from the `IBEX library `_. They are briefly presented here for the sake of consistency. For more information, please refer to the `IBEX documentation `_ for C++ use. .. _sec-manual-intervals-domains: @@ -169,7 +169,7 @@ Intervals, boxes and interval matrices .. _sec-manual-intervals-matrices: * | ``IntervalMatrix`` is also available. - | One can refer to the `documentation of IBEX `_ for more information. + | One can refer to the `documentation of IBEX `_ for more information. .. note:: @@ -227,7 +227,7 @@ For boxes (interval vectors), we have to specify their dimension even in case of Set operations -------------- -Set operations are available for ``Interval``, ``IntervalVector`` and ``IntervalMatrix`` objects (see the `official reference `_). In the following table, if :math:`[x]` is an interval object, :math:`d` is a real value. +Set operations are available for ``Interval``, ``IntervalVector`` and ``IntervalMatrix`` objects (see the `official reference `_). In the following table, if :math:`[x]` is an interval object, :math:`d` is a real value. ==================================== ======================================================= Code Meaning diff --git a/doc/doc/manual/04-static-contractors/01-ctc-function.rst b/doc/doc/manual/04-static-contractors/01-ctc-function.rst index 9b61811ca..30517200c 100644 --- a/doc/doc/manual/04-static-contractors/01-ctc-function.rst +++ b/doc/doc/manual/04-static-contractors/01-ctc-function.rst @@ -53,7 +53,7 @@ Definition .. Figure:: ../../img/logo_ibex.jpg :align: right - This contractor originates from the `IBEX library `_. It is briefly presented here for the sake of consistency. For more information, please refer to the `IBEX documentation `_ for C++. + This contractor originates from the `IBEX library `_. It is briefly presented here for the sake of consistency. For more information, please refer to the `IBEX documentation `_ for C++. .. rubric:: Optimality @@ -221,7 +221,7 @@ The above illustration reveals several contracted boxes with the new ``ctc_f`` c Going further ------------- -This ``CtcFunction`` class is a generic shortcut to deal with :math:`\mathbf{f}(\mathbf{x})=\mathbf{0}` or :math:`\mathbf{f}(\mathbf{x})\in[\mathbf{y}]`. However, several algorithms exist to optimally deal with different classes of problems. A list of static contractors is provided in the IBEX library: `see more `_. +This ``CtcFunction`` class is a generic shortcut to deal with :math:`\mathbf{f}(\mathbf{x})=\mathbf{0}` or :math:`\mathbf{f}(\mathbf{x})\in[\mathbf{y}]`. However, several algorithms exist to optimally deal with different classes of problems. A list of static contractors is provided in the IBEX library: `see more `_. The user is invited to use an appropriate tool to deal with the constraint at stake. The IBEX contractor behind ``CtcFunction`` is a ``CtcFwdBwd`` coupled with a ``Ctc3BCid``. diff --git a/doc/doc/tutorial-jnrr23/01-basics/index.rst b/doc/doc/tutorial-jnrr23/01-basics/index.rst index 32033e2e5..0d4b30ff4 100644 --- a/doc/doc/tutorial-jnrr23/01-basics/index.rst +++ b/doc/doc/tutorial-jnrr23/01-basics/index.rst @@ -459,4 +459,4 @@ We now have all the material to compute a solver for state estimation in the nex .. rubric:: Footnotes -.. [#f1] C++ objects originates from the `IBEX library `_. \ No newline at end of file +.. [#f1] C++ objects originates from the `IBEX library `_. \ No newline at end of file diff --git a/doc/doc/tutorial/01-basics/index.rst b/doc/doc/tutorial/01-basics/index.rst index 86262497c..1fa18afa4 100644 --- a/doc/doc/tutorial/01-basics/index.rst +++ b/doc/doc/tutorial/01-basics/index.rst @@ -443,4 +443,4 @@ We now have all the material to compute a solver for state estimation in the nex .. rubric:: Footnotes -.. [#f1] C++ objects originates from the `IBEX library `_. \ No newline at end of file +.. [#f1] C++ objects originates from the `IBEX library `_. \ No newline at end of file diff --git a/doc/doc/tutorial/03-static-rangebearing/index.rst b/doc/doc/tutorial/03-static-rangebearing/index.rst index e0d5bef36..cf79598cd 100644 --- a/doc/doc/tutorial/03-static-rangebearing/index.rst +++ b/doc/doc/tutorial/03-static-rangebearing/index.rst @@ -78,7 +78,7 @@ Problems involving complex equations can be broken down into a set of **primitiv where :math:`a,b,\dots,e` are intermediate variables used for the decomposition. This constitutes a network made of the :math:`\mathcal{L}_{-}`, :math:`\mathcal{L}_{+}`, :math:`\mathcal{L}_{(\cdot)^2}`, and :math:`\mathcal{L}_{\sqrt{\cdot}}` elementary constraints. -| The library (more precisely: `the IBEX library `_) provides a way to automatically make this decomposition, select already existing **elementary contractors** such as :math:`\mathcal{C}_{+}`, :math:`\mathcal{C}_{(\cdot)^2}`, :math:`\mathcal{C}_{\sqrt{\cdot}}` and build the complex contractor modeling the constraint :math:`\mathcal{L}_{\textrm{dist}}`. +| The library (more precisely: `the IBEX library `_) provides a way to automatically make this decomposition, select already existing **elementary contractors** such as :math:`\mathcal{C}_{+}`, :math:`\mathcal{C}_{(\cdot)^2}`, :math:`\mathcal{C}_{\sqrt{\cdot}}` and build the complex contractor modeling the constraint :math:`\mathcal{L}_{\textrm{dist}}`. | This was implicitly done in the previous :ref:`Lesson A ` with: .. tabs:: diff --git a/python/src/core/domains/interval/codac_py_IntervalVector_docs.h b/python/src/core/domains/interval/codac_py_IntervalVector_docs.h index 500cc19f4..385254b9a 100644 --- a/python/src/core/domains/interval/codac_py_IntervalVector_docs.h +++ b/python/src/core/domains/interval/codac_py_IntervalVector_docs.h @@ -13,7 +13,7 @@ const char* DOCS_INTERVALVECTOR_TYPE = R"doc_itv(An IntervalVector is a cartesian product of Intervals The docs string is taken from ibex_IntervalVector.h source file -For more information read doc from http://www.ibex-lib.org/ +For more information read doc from https://github.com/ibex-team/ibex-lib/ An IntervalVector can be initialized with: diff --git a/python/src/core/domains/interval/codac_py_Interval_docs.h b/python/src/core/domains/interval/codac_py_Interval_docs.h index aba26665c..6097bdc67 100644 --- a/python/src/core/domains/interval/codac_py_Interval_docs.h +++ b/python/src/core/domains/interval/codac_py_Interval_docs.h @@ -14,7 +14,7 @@ const char* DOCS_INTERVAL_TYPE = R"doc_itv(An Interval represents a closed sub set of R The docs string is taken from ibex_Interval.h source file -For more information read doc from http://www.ibex-lib.org/ +For more information read doc from https://github.com/ibex-team/ibex-lib/ Examples: >>> a = Interval(1,2) diff --git a/python/src/core/domains/interval/codac_py_bisectors_docs.h b/python/src/core/domains/interval/codac_py_bisectors_docs.h index b67714b9b..e29827b7a 100644 --- a/python/src/core/domains/interval/codac_py_bisectors_docs.h +++ b/python/src/core/domains/interval/codac_py_bisectors_docs.h @@ -25,7 +25,7 @@ R"_docs(Generic bisector \link ibex::Bsc::prec precision \endlink parameter of this class. The docs string is taken from ibex_Bsc.h source file - For more information read doc from http://www.ibex-lib.org/ + For more information read doc from https://github.com/ibex-team/ibex-lib/ )_docs"; diff --git a/src/core/separators/codac_SepProj.h b/src/core/separators/codac_SepProj.h index d10d0b4be..4b85201f4 100644 --- a/src/core/separators/codac_SepProj.h +++ b/src/core/separators/codac_SepProj.h @@ -239,7 +239,7 @@ typedef struct ImpactStatus_{ * The separator algorythm is inspired from the ibexlib ones * but performs the inner and outer contraction concurently. * - * See Ibexlib (CtcForAll and CtcExist)[http://www.ibex-lib.org/doc/contractor.html#exists-and-forall] + * See Ibexlib (CtcForAll and CtcExist)[https://ibex-team.github.io/ibex-lib/contractor.html#exists-and-forall] * documentation for more details. */ class SepProj : public Sep { From 0389518efda8e935c3daed837362116fc3ec0b49 Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Sun, 17 Nov 2024 15:07:16 +0100 Subject: [PATCH 241/256] Update doc about GAOL --- doc/doc/install/01-installation-full-linux.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/doc/install/01-installation-full-linux.rst b/doc/doc/install/01-installation-full-linux.rst index 5047c14c2..3730d5e29 100644 --- a/doc/doc/install/01-installation-full-linux.rst +++ b/doc/doc/install/01-installation-full-linux.rst @@ -77,6 +77,10 @@ Codac uses several features of the `IBEX library `_ and `GAOL `_ with CMake and `specify where they are `_ to build IBEX successfully and have accurate computations. + .. admonition:: Debug/development mode Note that the :code:`-DCMAKE_BUILD_TYPE=Debug` option will slightly slow down your computations, but display useful error messages in case of failure conditions such as access violations. **It is highly recommended** for your developments. To use it: From 322f8dc2baa0ca5f37e5496b8edce8d15ad2a7f4 Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Sun, 17 Nov 2024 15:39:27 +0100 Subject: [PATCH 242/256] Test specific IBEX version --- .github/workflows/dockermatrix.yml | 8 ++++---- .github/workflows/macosmatrix.yml | 2 +- .github/workflows/unixmatrix.yml | 14 +++++++------- .github/workflows/vcmatrix.yml | 6 +++--- packages/temporary/gennewcodacpi_armhf.sh | 4 ++-- scripts/dependencies/install_ibex.sh | 2 +- scripts/docker/build_pybinding.sh | 2 +- 7 files changed, 19 insertions(+), 19 deletions(-) diff --git a/.github/workflows/dockermatrix.yml b/.github/workflows/dockermatrix.yml index 6f6386100..7f6f62787 100644 --- a/.github/workflows/dockermatrix.yml +++ b/.github/workflows/dockermatrix.yml @@ -68,11 +68,11 @@ jobs: if [ ${{ matrix.cfg.deb }} = true ]; then \ #sudo sh -c 'echo \"deb [trusted=yes] https://packages.ensta-bretagne.fr/\$(if [ -z \"\$(. /etc/os-release && echo \$UBUNTU_CODENAME)\" ]; then echo debian/\$(. /etc/os-release && echo \$VERSION_CODENAME); else echo ubuntu/\$(. /etc/os-release && echo \$UBUNTU_CODENAME); fi) ./\" > /etc/apt/sources.list.d/ensta-bretagne.list' && \\ sudo apt-get -q update ; sudo apt-get -y install libeigen3-dev dpkg-dev || true && \ - wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20241103/libibex-dev-2.8.9.20241103-0${{ matrix.cfg.runtime }}0_\$(dpkg --print-architecture).deb --no-check-certificate -nv && \ - sudo dpkg -i libibex-dev-2.8.9.20241103-0${{ matrix.cfg.runtime }}0_\$(dpkg --print-architecture).deb && \ - rm -Rf libibex-dev-2.8.9.20241103-0${{ matrix.cfg.runtime }}0_\$(dpkg --print-architecture).deb ; \ + wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20241117/libibex-dev-2.8.9.20241117-0${{ matrix.cfg.runtime }}0_\$(dpkg --print-architecture).deb --no-check-certificate -nv && \ + sudo dpkg -i libibex-dev-2.8.9.20241117-0${{ matrix.cfg.runtime }}0_\$(dpkg --print-architecture).deb && \ + rm -Rf libibex-dev-2.8.9.20241117-0${{ matrix.cfg.runtime }}0_\$(dpkg --print-architecture).deb ; \ else \ - wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20241103/ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip --no-check-certificate -nv && \ + wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20241117/ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip --no-check-certificate -nv && \ unzip -q ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip && \ rm -Rf ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip && \ sudo cp -Rf ibex/* /usr/ ; \ diff --git a/.github/workflows/macosmatrix.yml b/.github/workflows/macosmatrix.yml index c41d53e96..532a16379 100644 --- a/.github/workflows/macosmatrix.yml +++ b/.github/workflows/macosmatrix.yml @@ -61,7 +61,7 @@ jobs: - run: brew install graphviz ; wget https://github.com/Homebrew/homebrew-core/raw/d2267b9f2ad247bc9c8273eb755b39566a474a70/Formula/doxygen.rb ; brew reinstall --formula ./doxygen.rb ; brew pin doxygen ; python -m pip install --upgrade pip ; pip install --upgrade wheel setuptools sphinx breathe sphinx_rtd_theme sphinx-tabs sphinx-issues sphinx-reredirects if: runner.os=='macOS' - run: | - wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20241103/ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip --no-check-certificate -nv + wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20241117/ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip --no-check-certificate -nv unzip -q ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip rm -Rf ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip sudo cp -Rf ibex/* /usr/local/ diff --git a/.github/workflows/unixmatrix.yml b/.github/workflows/unixmatrix.yml index 063452282..1b1ec9c2a 100644 --- a/.github/workflows/unixmatrix.yml +++ b/.github/workflows/unixmatrix.yml @@ -117,23 +117,23 @@ jobs: if: (matrix.cfg.runtime=='mingw13') - run: | choco install -y -r --no-progress eigen --version=3.4.0.20240224 ${{ matrix.cfg.choco_flags }} - wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20241103/ibex.2.8.9.20241103.nupkg --no-check-certificate -nv - choco install -y -r --no-progress --ignore-dependencies -s . ibex --version=2.8.9.20241103 ${{ matrix.cfg.choco_flags }} --params "'/url:https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20241103/ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip'" - del /f /q ibex.2.8.9.20241103.nupkg + wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20241117/ibex.2.8.9.20241117.nupkg --no-check-certificate -nv + choco install -y -r --no-progress --ignore-dependencies -s . ibex --version=2.8.9.20241117 ${{ matrix.cfg.choco_flags }} --params "'/url:https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20241117/ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip'" + del /f /q ibex.2.8.9.20241117.nupkg if: runner.os=='Windows' - run: | # Replace these 2 lines by the next ones to test a specific binary package of IBEX. #sudo sh -c 'echo "deb [trusted=yes] https://packages.ensta-bretagne.fr/$(if [ -z "$(. /etc/os-release && echo $UBUNTU_CODENAME)" ]; then echo debian/$(. /etc/os-release && echo $VERSION_CODENAME); else echo ubuntu/$(. /etc/os-release && echo $UBUNTU_CODENAME); fi) ./" > /etc/apt/sources.list.d/ensta-bretagne.list' #sudo apt-get -q update ; sudo apt-get -y install libibex-dev libeigen3-dev dpkg-dev || true sudo apt-get -q update ; sudo apt-get -y install libeigen3-dev dpkg-dev || true - wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20241103/libibex-dev-2.8.9.20241103-0${{ matrix.cfg.runtime }}0_$(dpkg --print-architecture).deb --no-check-certificate -nv - sudo dpkg -i libibex-dev-2.8.9.20241103-0${{ matrix.cfg.runtime }}0_$(dpkg --print-architecture).deb - rm -Rf libibex-dev-2.8.9.20241103-0${{ matrix.cfg.runtime }}0_$(dpkg --print-architecture).deb + wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20241117/libibex-dev-2.8.9.20241117-0${{ matrix.cfg.runtime }}0_$(dpkg --print-architecture).deb --no-check-certificate -nv + sudo dpkg -i libibex-dev-2.8.9.20241117-0${{ matrix.cfg.runtime }}0_$(dpkg --print-architecture).deb + rm -Rf libibex-dev-2.8.9.20241117-0${{ matrix.cfg.runtime }}0_$(dpkg --print-architecture).deb shell: bash if: matrix.cfg.deb==true - run: | brew install eigen - wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20241103/ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip --no-check-certificate -nv + wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20241117/ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip --no-check-certificate -nv unzip -q ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip rm -Rf ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip sudo cp -Rf ibex/* /usr/local/ diff --git a/.github/workflows/vcmatrix.yml b/.github/workflows/vcmatrix.yml index 5dfecfe79..91516af96 100644 --- a/.github/workflows/vcmatrix.yml +++ b/.github/workflows/vcmatrix.yml @@ -63,9 +63,9 @@ jobs: - run: wget https://packages.ensta-bretagne.fr/choco/doxygen.install.1.9.6.nupkg --no-check-certificate -nv & choco upgrade -y -r --no-progress -s . doxygen.install --version=1.9.6 & del /f /q doxygen.install.1.9.6.nupkg & choco install -y -r --no-progress graphviz & python -m pip install --upgrade pip & pip install --upgrade wheel setuptools sphinx breathe sphinx-issues sphinx-tabs sphinx_rtd_theme sphinx-reredirects if: runner.os=='Windows' - run: | - wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20241103/ibex.2.8.9.20241103.nupkg --no-check-certificate -nv - choco install -y -r --no-progress --ignore-dependencies -s . ibex --version=2.8.9.20241103 ${{ matrix.cfg.choco_flags }} --params "'/url:https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20241103/ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip'" - del /f /q ibex.2.8.9.20241103.nupkg + wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20241117/ibex.2.8.9.20241117.nupkg --no-check-certificate -nv + choco install -y -r --no-progress --ignore-dependencies -s . ibex --version=2.8.9.20241117 ${{ matrix.cfg.choco_flags }} --params "'/url:https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20241117/ibex_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip'" + del /f /q ibex.2.8.9.20241117.nupkg - run: | mkdir build ; cd build cmake -E env CXXFLAGS=" /wd4267 /wd4244 /wd4305 /wd4996" CFLAGS=" /wd4267 /wd4244 /wd4305 /wd4996" cmake ${{ matrix.cfg.cmake_params }} -D CMAKE_INSTALL_PREFIX="../codac" -D WITH_PYTHON=ON .. diff --git a/packages/temporary/gennewcodacpi_armhf.sh b/packages/temporary/gennewcodacpi_armhf.sh index bd34e6537..9f7ef3d53 100644 --- a/packages/temporary/gennewcodacpi_armhf.sh +++ b/packages/temporary/gennewcodacpi_armhf.sh @@ -26,8 +26,8 @@ fi && \ sudo apt-get -q update --allow-releaseinfo-change ; sudo apt-get -y install libeigen3-dev python3-dev patchelf || true && \ python3 -m pip install \$PIP_OPTIONS --upgrade patchelf --prefer-binary --extra-index-url https://www.piwheels.org/simple && \ python3 -m pip install \$PIP_OPTIONS --upgrade auditwheel --prefer-binary --extra-index-url https://www.piwheels.org/simple && \ -# wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20241103/ibex_armhf_\$(lsb_release -cs).zip --no-check-certificate -nv is causing illegal instruction on a Mac M1... \\ -curl -L -O https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20241103/ibex_armhf_\$(lsb_release -cs).zip --insecure && \ +# wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20241117/ibex_armhf_\$(lsb_release -cs).zip --no-check-certificate -nv is causing illegal instruction on a Mac M1... \\ +curl -L -O https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20241117/ibex_armhf_\$(lsb_release -cs).zip --insecure && \ unzip -q ibex_armhf_\$(lsb_release -cs).zip && \ rm -Rf ibex_armhf_\$(lsb_release -cs).zip && \ sudo cp -Rf ibex/* /usr/local/ && \ diff --git a/scripts/dependencies/install_ibex.sh b/scripts/dependencies/install_ibex.sh index 069399baa..40c682d41 100644 --- a/scripts/dependencies/install_ibex.sh +++ b/scripts/dependencies/install_ibex.sh @@ -4,7 +4,7 @@ cd $HOME echo 'Installing IBEX in ' $HOME '...'; if [ ! -e "ibex-lib/README.md" ]; then #git clone -b master https://github.com/lebarsfa/ibex-lib.git ; - git clone -b ibex-2.8.9.20241103 https://github.com/lebarsfa/ibex-lib.git ; # To test a specific version of IBEX... + git clone -b ibex-2.8.9.20241117 https://github.com/lebarsfa/ibex-lib.git ; # To test a specific version of IBEX... cd ibex-lib ; mkdir build && cd build ; cmake -E env CXXFLAGS="-fPIC" CFLAGS="-fPIC" cmake -DCMAKE_INSTALL_PREFIX=$HOME/ibex-lib/build_install -DCMAKE_BUILD_TYPE=Debug .. ; diff --git a/scripts/docker/build_pybinding.sh b/scripts/docker/build_pybinding.sh index 3df4a5ee7..9e8b9563c 100755 --- a/scripts/docker/build_pybinding.sh +++ b/scripts/docker/build_pybinding.sh @@ -2,7 +2,7 @@ set -e -x -wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20241103/ibex_$(uname -m)_manylinux2014.zip --no-check-certificate -nv +wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20241117/ibex_$(uname -m)_manylinux2014.zip --no-check-certificate -nv unzip -q ibex_$(uname -m)_manylinux2014.zip rm -Rf ibex_$(uname -m)_manylinux2014.zip sudo cp -Rf ibex/* /usr/local/ From e428172eaf45da2436805164a1354887f8275c62 Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Mon, 25 Nov 2024 11:58:00 +0100 Subject: [PATCH 243/256] Undefining __STRICT_ANSI__ may cause issues on recent MinGW versions --- CMakeLists.txt | 5 ----- 1 file changed, 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7678c8b5e..ec8717dbc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -62,11 +62,6 @@ # add_compile_options(-std=c++17) # else() # message(FATAL_ERROR "Codac needs a compiler with C++17 support") -# endif() - -# if(WIN32) -# # We need this for strdup under Windows (see issue #287 of ibex-lib repo) -# add_definitions(-U__STRICT_ANSI__) # endif() #if(NOT CMAKE_CXX_STANDARD) From b7749cbacb8cdebaa808d5bdc43243983f5d63e3 Mon Sep 17 00:00:00 2001 From: Simon Rohou Date: Fri, 29 Nov 2024 09:52:51 +0100 Subject: [PATCH 244/256] [doc] updated manual (transition v1 .. v2) --- doc/CMakeLists.txt | 2 +- doc/doc/conf.py.in | 3 +++ .../install/01-installation-full-linux.rst | 19 ++++++++++--------- .../install/01-installation-full-macos.rst | 10 +++++----- .../install/01-installation-full-windows.rst | 12 ++++++------ doc/doc/install/01-installation-python.rst | 11 ++++++----- doc/doc/tutorial/01-basics/index.rst | 6 +++--- doc/doc/tutorial/05-tubes/index.rst | 2 +- 8 files changed, 35 insertions(+), 30 deletions(-) diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt index 58ba02ac2..0d9dff3bf 100644 --- a/doc/CMakeLists.txt +++ b/doc/CMakeLists.txt @@ -88,7 +88,7 @@ install(FILES ${CMAKE_CURRENT_BINARY_DIR}/mathjax_stmaryrd.js if(WIN32) set(SPHINX_EXECUTABLE "sphinx-build") else() - set(SPHINX_EXECUTABLE "python3.6" "-msphinx") # todo: replace python3.6 by python3 + set(SPHINX_EXECUTABLE "python3" "-msphinx") endif() add_custom_target(doc diff --git a/doc/doc/conf.py.in b/doc/doc/conf.py.in index 211ece693..ef8ca2071 100644 --- a/doc/doc/conf.py.in +++ b/doc/doc/conf.py.in @@ -63,6 +63,9 @@ rst_prolog = """ :class: underline .. role:: right-aligned-note :class: right-aligned-note + +.. seealso:: + This manual refers to Codac v1, but a new v2 implementation is currently in progress... an update of this manual will be available soon. `See more `_. """ # GitHub repo diff --git a/doc/doc/install/01-installation-full-linux.rst b/doc/doc/install/01-installation-full-linux.rst index 3730d5e29..7f3c19ed6 100644 --- a/doc/doc/install/01-installation-full-linux.rst +++ b/doc/doc/install/01-installation-full-linux.rst @@ -2,15 +2,15 @@ .. _sec-installation-full-linux: -##################################### -Installing Codac on Linux for C++ use -##################################### +######################################## +Installing Codac v1 on Linux for C++ use +######################################## Install from package (latest release, for Ubuntu (amd64, arm64), Debian (arm64, armhf) and possibly others) ----------------------------------------------------------------------------------------------------------- -A Debian package is available for the last release |version| of the library: +A Debian package is available for the last release 1.5.6 of the library: .. code-block:: bash @@ -18,7 +18,7 @@ A Debian package is available for the last release |version| of the library: sudo apt update sudo apt install libcodac-dev -Then, check your installation `with the instructions of this page <03-start-cpp-project.html>`_. +Then, check your installation :ref:`with the instructions of this page `. .. warning:: @@ -47,8 +47,8 @@ Then, check your installation `with the instructions of this page <03-start-cpp- and check that "My first tube:Tube [0, 10]" appears. -Install from sources (latest development) ------------------------------------------ +Install from sources (latest development of v1) +----------------------------------------------- In case you prefer the latest development version, Codac can be installed by compiling the sources. @@ -100,6 +100,7 @@ The last sources are available on `the official Codac development repository `_ before running your program. +Do not forget to launch the :ref:`VIBes viewer ` before running your program. (for experts) Additional installation options @@ -179,4 +180,4 @@ Do not forget to launch the `VIBes viewer <01-installation.html#graphical-tools> export CMAKE_PREFIX_PATH=$CMAKE_PREFIX_PATH:$HOME/ibex-lib/build_install export CMAKE_PREFIX_PATH=$CMAKE_PREFIX_PATH:$HOME/codac/build_install -See also `Information for developers `_. +See also :ref:`Information for developers `. diff --git a/doc/doc/install/01-installation-full-macos.rst b/doc/doc/install/01-installation-full-macos.rst index c0ca9f64c..b8790c9f9 100644 --- a/doc/doc/install/01-installation-full-macos.rst +++ b/doc/doc/install/01-installation-full-macos.rst @@ -2,9 +2,9 @@ .. _sec-installation-full-macos: -##################################### -Installing Codac on macOS for C++ use -##################################### +######################################## +Installing Codac v1 on macOS for C++ use +######################################## Quick start @@ -18,7 +18,7 @@ Install `Homebrew package manager `_ and build tools: brew install wget autoconf automake libtool brew install --cask cmake -Download and extract *e.g.* ``codac_standalone_arm64_monterey.zip`` (for macOS 12 Monterey on a Mac with Apple silicon (arm64 processor), use ``codac_standalone_x86_64_monterey.zip`` for a Mac with an Intel processor (x86_64 processor), see https://support.apple.com/en-us/116943) from ``_, then in ``example`` folder run: +Download and extract *e.g.* ``codac_standalone_arm64_monterey.zip`` (for macOS 12 Monterey on a Mac with Apple silicon (arm64 processor), use ``codac_standalone_x86_64_monterey.zip`` for a Mac with an Intel processor (x86_64 processor), see https://support.apple.com/en-us/116943) from ``_, then in ``example`` folder run: .. code-block:: bash @@ -47,4 +47,4 @@ Optionally, for Python and documentation: python -m pip install --upgrade pip pip install --upgrade wheel setuptools sphinx breathe sphinx-issues sphinx-tabs sphinx_rtd_theme sphinx-reredirects -The logic to follow will then be similar to `Linux <01-installation-full-linux.html>`_. See also `Information for developers `_. +The logic to follow will then be similar to :ref:`Linux `. See also :ref:`Information for developers `. \ No newline at end of file diff --git a/doc/doc/install/01-installation-full-windows.rst b/doc/doc/install/01-installation-full-windows.rst index 3580d6fd9..238a12ee4 100644 --- a/doc/doc/install/01-installation-full-windows.rst +++ b/doc/doc/install/01-installation-full-windows.rst @@ -2,9 +2,9 @@ .. _sec-installation-full-windows: -####################################### -Installing Codac on Windows for C++ use -####################################### +########################################## +Installing Codac v1 on Windows for C++ use +########################################## Quick start @@ -17,7 +17,7 @@ Check https://community.chocolatey.org/packages/codac. .. rubric:: Using Visual Studio -Download and extract *e.g.* ``codac_standalone_x64_vc17.zip`` (for Visual Studio 2022) from ``_, open ``example\CMakelists.txt``, choose ``x64-Release`` configuration in Visual Studio (instead of ``x64-Debug``), double-click on ``main.cpp`` in the Solution Explorer and then click on the green Start button, finally check that "My first tube:Tube [0, 10]" appears. +Download and extract *e.g.* ``codac_standalone_x64_vc17.zip`` (for Visual Studio 2022) from ``_, open ``example\CMakelists.txt``, choose ``x64-Release`` configuration in Visual Studio (instead of ``x64-Debug``), double-click on ``main.cpp`` in the Solution Explorer and then click on the green Start button, finally check that "My first tube:Tube [0, 10]" appears. Optionally, download and run ``_ before running the project, and check that a tube appears in :ref:`VIBes viewer `. @@ -43,7 +43,7 @@ Optionally, for Python (*e.g.* ``choco install python --version=3.10.4``) and do python -m pip install --upgrade pip pip install --upgrade wheel setuptools sphinx breathe sphinx-issues sphinx-tabs sphinx_rtd_theme sphinx-reredirects -The logic to follow will then be similar to `Linux <01-installation-full-linux.html>`_ (note that for Visual Studio, commands such as ``make install`` need to be replaced with something similar to: +The logic to follow will then be similar to :ref:`Linux ` (note that for Visual Studio, commands such as ``make install`` need to be replaced with something similar to: .. code-block:: bash @@ -53,4 +53,4 @@ The logic to follow will then be similar to `Linux <01-installation-full-linux.h | You might need to replace all occurences of :literal:`PATH_SUFFIXES \ ` with something similar to :literal:`PATHS ${CMAKE_CURRENT_LIST_FILE}/../../../../ PATH_SUFFIXES \ ` in all ``.cmake`` in ``codac/share/codac/cmake/`` (where Codac was installed) if a CMake project that tries to use Codac appears to find its installation location but fails to configure the project properly. -See also `Information for developers `_. +See also :ref:`Information for developers `. diff --git a/doc/doc/install/01-installation-python.rst b/doc/doc/install/01-installation-python.rst index 1693b0841..36de03e19 100644 --- a/doc/doc/install/01-installation-python.rst +++ b/doc/doc/install/01-installation-python.rst @@ -2,9 +2,9 @@ .. _sec-installation-py: -############################### -Installing Codac for Python use -############################### +################################## +Installing Codac v1 for Python use +################################## .. note:: @@ -19,7 +19,8 @@ In case you want to use Codac only with Python, then the installation procedure # You may have to upgrade pip (19.0.0 required at least) pip3 install --upgrade pip - pip3 install codac + pip3 install codac==1.5.6 + # latest release of Codac v1 is 1.5.6 .. role:: gbg @@ -78,7 +79,7 @@ If a configuration in this table does not work, please `contact us `_ . + Unsupported computers can still probably follow :ref:`Installing local Python binding `. Update your Codac Python package diff --git a/doc/doc/tutorial/01-basics/index.rst b/doc/doc/tutorial/01-basics/index.rst index 1fa18afa4..d78c10358 100644 --- a/doc/doc/tutorial/01-basics/index.rst +++ b/doc/doc/tutorial/01-basics/index.rst @@ -1,13 +1,13 @@ .. _sec-tuto-01: +Lesson A: Getting started with intervals and contractors +======================================================== + .. # define a hard line break for HTML .. |br| raw:: html
-Lesson A: Getting started with intervals and contractors -======================================================== - Now that Codac has been installed on your computer or usable online, we will get used to intervals, constraints and networks of contractors. This will allow you to perform the state estimation of a static robot between some landmarks by the end of :ref:`Lesson B `. diff --git a/doc/doc/tutorial/05-tubes/index.rst b/doc/doc/tutorial/05-tubes/index.rst index 3928e8ffb..3e248a27a 100644 --- a/doc/doc/tutorial/05-tubes/index.rst +++ b/doc/doc/tutorial/05-tubes/index.rst @@ -299,7 +299,7 @@ For instance, one can contract three tubes :math:`[a](\cdot)`, :math:`[b](\cdot) \mathbf{y}(t)=\mathbf{g}\big(\mathbf{x}(t)\big) & & \textrm{(observation equation)} \end{array}\right. - **E.10.** We first focus on the observation equation :math:`\mathbf{y}(t)=\mathbf{g}\big(\mathbf{x}(t)\big)`. Build a contractor network and contract the tube :math:`[\mathbf{x}](\cdot)` with the distance contractor, that expresses :math:`\mathbf{g}`. Note that this contractor is already defined in the library. You developed your own version as an exercise in :ref:`sec-tuto-01`, but you can also use: + **E.10.** We first focus on the observation equation :math:`\mathbf{y}(t)=\mathbf{g}\big(\mathbf{x}(t)\big)`. Build a contractor network and contract the tube :math:`[\mathbf{x}](\cdot)` with the distance contractor, that expresses :math:`\mathbf{g}`. Note that this contractor is already defined in the library. You developed your own version as an exercise in :ref:`Lesson A `, but you can also use: .. tabs:: From 577cd18ff8b0df76fba5f2289acdaa707da7361f Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Sun, 1 Dec 2024 15:11:07 +0100 Subject: [PATCH 245/256] /MP flag for Visual Studio may allow more parallelization possibilities --- .github/workflows/unixmatrix.yml | 12 ++++++------ .github/workflows/vcmatrix.yml | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/unixmatrix.yml b/.github/workflows/unixmatrix.yml index 1b1ec9c2a..a7c76922c 100644 --- a/.github/workflows/unixmatrix.yml +++ b/.github/workflows/unixmatrix.yml @@ -28,12 +28,12 @@ jobs: fail-fast: false matrix: cfg: - - { os: windows-2022, shell: cmd, arch: x64, bitness: 64, runtime: vc17, cmake_params: '-G "Visual Studio 17" -T v143 -A x64', cmake_flags: ' /wd4267 /wd4244 /wd4305 /wd4996', test_config: 'Release/', desc: 'Windows Visual Studio 2022 x64' } - - { os: windows-2022, shell: cmd, arch: x86, bitness: 32, runtime: vc17, cmake_params: '-G "Visual Studio 17" -T v143 -A Win32', cmake_flags: ' /wd4267 /wd4244 /wd4305 /wd4996', test_config: 'Release/', choco_flags: '--x86', desc: 'Windows Visual Studio 2022 x86' } - - { os: windows-2022, shell: cmd, arch: x64, bitness: 64, runtime: vc16, cmake_params: '-G "Visual Studio 17" -T v142 -A x64', cmake_flags: ' /wd4267 /wd4244 /wd4305 /wd4996', test_config: 'Release/', desc: 'Windows Visual Studio 2019 x64' } - - { os: windows-2022, shell: cmd, arch: x86, bitness: 32, runtime: vc16, cmake_params: '-G "Visual Studio 17" -T v142 -A Win32', cmake_flags: ' /wd4267 /wd4244 /wd4305 /wd4996', test_config: 'Release/', choco_flags: '--x86', desc: 'Windows Visual Studio 2019 x86' } - - { os: windows-2019, shell: cmd, arch: x64, bitness: 64, runtime: vc15, cmake_params: '-G "Visual Studio 16" -T v141 -A x64', cmake_flags: ' /wd4267 /wd4244 /wd4305 /wd4996', test_config: 'Release/', desc: 'Windows Visual Studio 2017 x64' } - - { os: windows-2019, shell: cmd, arch: x86, bitness: 32, runtime: vc15, cmake_params: '-G "Visual Studio 16" -T v141 -A Win32', cmake_flags: ' /wd4267 /wd4244 /wd4305 /wd4996', test_config: 'Release/', choco_flags: '--x86', desc: 'Windows Visual Studio 2017 x86' } + - { os: windows-2022, shell: cmd, arch: x64, bitness: 64, runtime: vc17, cmake_params: '-G "Visual Studio 17" -T v143 -A x64', cmake_flags: ' /MP4 /wd4267 /wd4244 /wd4305 /wd4996', test_config: 'Release/', desc: 'Windows Visual Studio 2022 x64' } + - { os: windows-2022, shell: cmd, arch: x86, bitness: 32, runtime: vc17, cmake_params: '-G "Visual Studio 17" -T v143 -A Win32', cmake_flags: ' /MP4 /wd4267 /wd4244 /wd4305 /wd4996', test_config: 'Release/', choco_flags: '--x86', desc: 'Windows Visual Studio 2022 x86' } + - { os: windows-2022, shell: cmd, arch: x64, bitness: 64, runtime: vc16, cmake_params: '-G "Visual Studio 17" -T v142 -A x64', cmake_flags: ' /MP4 /wd4267 /wd4244 /wd4305 /wd4996', test_config: 'Release/', desc: 'Windows Visual Studio 2019 x64' } + - { os: windows-2022, shell: cmd, arch: x86, bitness: 32, runtime: vc16, cmake_params: '-G "Visual Studio 17" -T v142 -A Win32', cmake_flags: ' /MP4 /wd4267 /wd4244 /wd4305 /wd4996', test_config: 'Release/', choco_flags: '--x86', desc: 'Windows Visual Studio 2019 x86' } + - { os: windows-2019, shell: cmd, arch: x64, bitness: 64, runtime: vc15, cmake_params: '-G "Visual Studio 16" -T v141 -A x64', cmake_flags: ' /MP4 /wd4267 /wd4244 /wd4305 /wd4996', test_config: 'Release/', desc: 'Windows Visual Studio 2017 x64' } + - { os: windows-2019, shell: cmd, arch: x86, bitness: 32, runtime: vc15, cmake_params: '-G "Visual Studio 16" -T v141 -A Win32', cmake_flags: ' /MP4 /wd4267 /wd4244 /wd4305 /wd4996', test_config: 'Release/', choco_flags: '--x86', desc: 'Windows Visual Studio 2017 x86' } - { os: windows-2022, shell: cmd, arch: x64, bitness: 64, runtime: mingw13, cmake_params: '-G "MinGW Makefiles"', cmake_flags: '-fPIC', desc: 'Windows MinGW 13.2.0 x64' } - { os: windows-2022, shell: cmd, arch: x86, bitness: 32, runtime: mingw13, cmake_params: '-G "MinGW Makefiles"', cmake_flags: '-fPIC', choco_flags: '--x86', desc: 'Windows MinGW 13.2.0 x86' } - { os: windows-2022, shell: cmd, arch: x64, bitness: 64, runtime: mingw12, cmake_params: '-G "MinGW Makefiles"', cmake_flags: '-fPIC', desc: 'Windows MinGW 12.2.0 x64' } diff --git a/.github/workflows/vcmatrix.yml b/.github/workflows/vcmatrix.yml index 91516af96..ae9ee2de1 100644 --- a/.github/workflows/vcmatrix.yml +++ b/.github/workflows/vcmatrix.yml @@ -68,7 +68,7 @@ jobs: del /f /q ibex.2.8.9.20241117.nupkg - run: | mkdir build ; cd build - cmake -E env CXXFLAGS=" /wd4267 /wd4244 /wd4305 /wd4996" CFLAGS=" /wd4267 /wd4244 /wd4305 /wd4996" cmake ${{ matrix.cfg.cmake_params }} -D CMAKE_INSTALL_PREFIX="../codac" -D WITH_PYTHON=ON .. + cmake -E env CXXFLAGS=" /MP4 /wd4267 /wd4244 /wd4305 /wd4996" CFLAGS=" /MP4 /wd4267 /wd4244 /wd4305 /wd4996" cmake ${{ matrix.cfg.cmake_params }} -D CMAKE_INSTALL_PREFIX="../codac" -D WITH_PYTHON=ON .. cmake --build . -j 4 --config Release --target install cmake --build . --config Release --target pip_package ; cp `ls *.whl` ../`ls *.whl | sed "s/py3-none-any/cp${{ matrix.cfg.py_v_maj }}${{ matrix.cfg.py_v_min }}-cp${{ matrix.cfg.py_v_maj }}${{ matrix.cfg.py_v_min }}${{ matrix.cfg.cpcfg }}/"` cd .. From 8cbeadad4128744d35c7e901fc6459afd39955aa Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Sun, 19 Jan 2025 16:14:56 +0100 Subject: [PATCH 246/256] Add "ctest -C Debug -V --output-on-failure" to unixmatrix.yml --- .github/workflows/unixmatrix.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/unixmatrix.yml b/.github/workflows/unixmatrix.yml index a7c76922c..c1190be22 100644 --- a/.github/workflows/unixmatrix.yml +++ b/.github/workflows/unixmatrix.yml @@ -143,7 +143,7 @@ jobs: - run: | if [ ${{ runner.os }} = Windows ]; then source ~/refreshenv.bashrc ; refreshenv ; export PATH=$BASHMINGWPATH:$PATH ; fi mkdir build ; cd build - cmake -E env CXXFLAGS="${{ matrix.cfg.cmake_flags }}" CFLAGS="${{ matrix.cfg.cmake_flags }}" cmake ${{ matrix.cfg.cmake_params }} -D CMAKE_INSTALL_PREFIX="../codac" .. + cmake -E env CXXFLAGS="${{ matrix.cfg.cmake_flags }}" CFLAGS="${{ matrix.cfg.cmake_flags }}" cmake ${{ matrix.cfg.cmake_params }} -D BUILD_TESTS=ON -D CMAKE_INSTALL_PREFIX="../codac" .. cmake --build . -j 4 --config Debug --target install cd .. sed_param=s/PATH_SUFFIXES\ /PATHS\ \$\{CMAKE_CURRENT_LIST_FILE\}\\/..\\/..\\/..\\/..\\/\ PATH_SUFFIXES\ / @@ -211,6 +211,12 @@ jobs: overwrite: true tag_name: autotagname-${{ github.sha }} if: (github.event_name!='pull_request')&&((github.ref_name=='codac1')||(github.ref_name=='codac2')||(github.ref_name=='codac2_codac4matlab')) + - run: | + if [ ${{ runner.os }} = Windows ]; then source ~/refreshenv.bashrc ; refreshenv ; export PATH=$BASHMINGWPATH:$PATH ; fi + cd build && ctest -C Debug -V --output-on-failure + cd .. + shell: bash + if: (matrix.cfg.cross!=true) - run: | if [ ${{ runner.os }} = Windows ]; then source ~/refreshenv.bashrc ; refreshenv ; export PATH=$BASHMINGWPATH:$PATH ; fi rm -Rf codac From 705da3d733237179cecc63516bf5802b4c4c4c4d Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Mon, 20 Jan 2025 22:09:32 +0100 Subject: [PATCH 247/256] Adjust the minimum required CMake version to be consistent with the C++17 requirement and to solve some compatibility issues with future versions of certain dependencies --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ec8717dbc..8f1cb31a4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,7 @@ # Codac - cmake configuration file # ================================================================== - cmake_minimum_required(VERSION 3.0.2) + cmake_minimum_required(VERSION 3.8) list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/scripts/CMakeModules/) include(version_from_git) From 2296c1abf48282a4a581168bc836d42fa6252807 Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Mon, 20 Jan 2025 22:00:41 +0100 Subject: [PATCH 248/256] Corrections for the tests to pass in all configurations --- src/core/2/domains/interval/codac2_IntervalMatrix.h | 4 ++-- src/core/contractors/dyn/codac_CtcPicard.h | 3 ++- src/core/geometry/codac_GrahamScan.h | 12 ++++++------ 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/core/2/domains/interval/codac2_IntervalMatrix.h b/src/core/2/domains/interval/codac2_IntervalMatrix.h index 19a5038fd..865fc1ef6 100644 --- a/src/core/2/domains/interval/codac2_IntervalMatrix.h +++ b/src/core/2/domains/interval/codac2_IntervalMatrix.h @@ -383,9 +383,9 @@ namespace codac2 { if((this->data()+i)->is_unbounded()) return POS_INFINITY; if((this->data()+i)->is_degenerated()) return 0.; - v += log((this->data()+i)->diam()); + v += std::log((this->data()+i)->diam()); } - return exp(v); + return std::exp(v); } Matrix_ lb() const diff --git a/src/core/contractors/dyn/codac_CtcPicard.h b/src/core/contractors/dyn/codac_CtcPicard.h index 4e2b07688..073f18c2a 100644 --- a/src/core/contractors/dyn/codac_CtcPicard.h +++ b/src/core/contractors/dyn/codac_CtcPicard.h @@ -36,10 +36,11 @@ namespace codac int picard_iterations() const; + void guess_kth_slices_envelope(TubeVector& x, int k, TimePropag t_propa); + protected: void contract_kth_slices(TubeVector& x, int k, TimePropag t_propa); - void guess_kth_slices_envelope(TubeVector& x, int k, TimePropag t_propa); const TFunction* m_f_ptr = nullptr; const TFnc& m_f; diff --git a/src/core/geometry/codac_GrahamScan.h b/src/core/geometry/codac_GrahamScan.h index cc3a5ba66..2f76c01e3 100644 --- a/src/core/geometry/codac_GrahamScan.h +++ b/src/core/geometry/codac_GrahamScan.h @@ -27,11 +27,14 @@ namespace codac // Prints convex hull of a set of n points. static const std::vector convex_hull(const std::vector& v_points); - - protected: - // A utility function to find next to top in a stack static const Vector next_to_top(const std::stack& s); + + // To find orientation of ordered triplet (p, q, r). + static OrientationInterval orientation(const IntervalVector& p0, const IntervalVector& p1, const IntervalVector& p2); + + + protected: // A utility function to swap two points static void swap(Vector& p1, Vector& p2); @@ -39,9 +42,6 @@ namespace codac // A utility function to return square of distance between p1 and p2 static const Interval dist(const IntervalVector& p1, const IntervalVector& p2); - // To find orientation of ordered triplet (p, q, r). - static OrientationInterval orientation(const IntervalVector& p0, const IntervalVector& p1, const IntervalVector& p2); - friend class ThickPointsSorter; friend class ConvexPolygon; }; From c48f5a5bd091f15b15db283880ef226f2fb4128f Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Mon, 20 Jan 2025 22:40:31 +0100 Subject: [PATCH 249/256] Known issue in older versions of Visual Studio 2017 --- CMakeLists.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8f1cb31a4..27cc9f9f6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -69,6 +69,10 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) #endif() + if(MSVC) + add_definitions(-D_ENABLE_EXTENDED_ALIGNED_STORAGE) # Due to a known issue in older versions of Visual Studio 2017... + endif() + ################################################################################ # Optional binary tree activation for all tubes (for tests purposes mainly) From 6cc925dc38fa26e07c03263575cdb24ebfded9cc Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Mon, 20 Jan 2025 23:02:01 +0100 Subject: [PATCH 250/256] Update catch.hpp --- tests/catch/catch.hpp | 3323 +++++++++++++++++++++++++++-------------- 1 file changed, 2240 insertions(+), 1083 deletions(-) diff --git a/tests/catch/catch.hpp b/tests/catch/catch.hpp index 6bcdd0450..ad8e1cd44 100644 --- a/tests/catch/catch.hpp +++ b/tests/catch/catch.hpp @@ -1,6 +1,6 @@ /* - * Catch v1.6.1 - * Generated: 2017-01-20 12:33:53.497767 + * Catch v1.12.2 + * Generated: 2025-01-20 23:39:35.565000 * ---------------------------------------------------------- * This file has been merged from multiple headers. Please don't edit it directly * Copyright (c) 2012 Two Blue Cubes Ltd. All rights reserved. @@ -40,6 +40,8 @@ #elif defined __GNUC__ # pragma GCC diagnostic ignored "-Wvariadic-macros" # pragma GCC diagnostic ignored "-Wunused-variable" +# pragma GCC diagnostic ignored "-Wparentheses" + # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wpadded" #endif @@ -74,11 +76,15 @@ // CATCH_CONFIG_CPP11_LONG_LONG : is long long supported? // CATCH_CONFIG_CPP11_OVERRIDE : is override supported? // CATCH_CONFIG_CPP11_UNIQUE_PTR : is unique_ptr supported (otherwise use auto_ptr) +// CATCH_CONFIG_CPP11_SHUFFLE : is std::shuffle supported? +// CATCH_CONFIG_CPP11_TYPE_TRAITS : are type_traits and enable_if supported? // CATCH_CONFIG_CPP11_OR_GREATER : Is C++11 supported? // CATCH_CONFIG_VARIADIC_MACROS : are variadic macros supported? // CATCH_CONFIG_COUNTER : is the __COUNTER__ macro supported? +// CATCH_CONFIG_WINDOWS_SEH : is Windows SEH supported? +// CATCH_CONFIG_POSIX_SIGNALS : are POSIX signals supported? // **************** // Note to maintainers: if new toggles are added please document them // in configuration.md, too @@ -114,11 +120,46 @@ # endif # if defined(CATCH_CPP11_OR_GREATER) -# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS _Pragma( "clang diagnostic ignored \"-Wparentheses\"" ) +# define CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \ + _Pragma( "clang diagnostic push" ) \ + _Pragma( "clang diagnostic ignored \"-Wexit-time-destructors\"" ) +# define CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS \ + _Pragma( "clang diagnostic pop" ) + +# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ + _Pragma( "clang diagnostic push" ) \ + _Pragma( "clang diagnostic ignored \"-Wparentheses\"" ) +# define CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \ + _Pragma( "clang diagnostic pop" ) # endif #endif // __clang__ +//////////////////////////////////////////////////////////////////////////////// +// We know some environments not to support full POSIX signals +#if defined(__CYGWIN__) || defined(__QNX__) + +# if !defined(CATCH_CONFIG_POSIX_SIGNALS) +# define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS +# endif + +#endif + +#ifdef __OS400__ +# define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS +# define CATCH_CONFIG_COLOUR_NONE +#endif + +//////////////////////////////////////////////////////////////////////////////// +// Cygwin +#ifdef __CYGWIN__ + +// Required for some versions of Cygwin to declare gettimeofday +// see: http://stackoverflow.com/questions/36901803/gettimeofday-not-declared-in-this-scope-cygwin +# define _BSD_SOURCE + +#endif // __CYGWIN__ + //////////////////////////////////////////////////////////////////////////////// // Borland #ifdef __BORLANDC__ @@ -145,10 +186,6 @@ # define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR # endif -# if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS) && defined(CATCH_CPP11_OR_GREATER) -# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS _Pragma( "GCC diagnostic ignored \"-Wparentheses\"" ) -# endif - // - otherwise more recent versions define __cplusplus >= 201103L // and will get picked up below @@ -158,6 +195,8 @@ // Visual C++ #ifdef _MSC_VER +#define CATCH_INTERNAL_CONFIG_WINDOWS_SEH + #if (_MSC_VER >= 1600) # define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR # define CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR @@ -167,6 +206,7 @@ #define CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT #define CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS #define CATCH_INTERNAL_CONFIG_CPP11_SHUFFLE +#define CATCH_INTERNAL_CONFIG_CPP11_TYPE_TRAITS #endif #endif // _MSC_VER @@ -174,7 +214,7 @@ //////////////////////////////////////////////////////////////////////////////// // Use variadic macros if the compiler supports them -#if ( defined _MSC_VER && _MSC_VER > 1400 && !defined __EDGE__) || \ +#if ( defined _MSC_VER && _MSC_VER >= 1400 && !defined __EDGE__) || \ ( defined __WAVE__ && __WAVE_HAS_VARIADICS ) || \ ( defined __GNUC__ && __GNUC__ >= 3 ) || \ ( !defined __cplusplus && __STDC_VERSION__ >= 199901L || __cplusplus >= 201103L ) @@ -185,10 +225,15 @@ // Use __COUNTER__ if the compiler supports it #if ( defined _MSC_VER && _MSC_VER >= 1300 ) || \ - ( defined __GNUC__ && __GNUC__ >= 4 && __GNUC_MINOR__ >= 3 ) || \ + ( defined __GNUC__ && ( __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3 )) ) || \ ( defined __clang__ && __clang_major__ >= 3 ) -#define CATCH_INTERNAL_CONFIG_COUNTER +// Use of __COUNTER__ is suppressed during code analysis in CLion/AppCode 2017.2.x and former, +// because __COUNTER__ is not properly handled by it. +// This does not affect compilation +#if ( !defined __JETBRAINS_IDE__ || __JETBRAINS_IDE__ >= 20170300L ) + #define CATCH_INTERNAL_CONFIG_COUNTER +#endif #endif @@ -235,6 +280,9 @@ # if !defined(CATCH_INTERNAL_CONFIG_CPP11_SHUFFLE) # define CATCH_INTERNAL_CONFIG_CPP11_SHUFFLE # endif +# if !defined(CATCH_INTERNAL_CONFIG_CPP11_TYPE_TRAITS) +# define CATCH_INTERNAL_CONFIG_CPP11_TYPE_TRAITS +# endif #endif // __cplusplus >= 201103L @@ -266,18 +314,30 @@ #if defined(CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR) && !defined(CATCH_CONFIG_CPP11_NO_UNIQUE_PTR) && !defined(CATCH_CONFIG_CPP11_UNIQUE_PTR) && !defined(CATCH_CONFIG_NO_CPP11) # define CATCH_CONFIG_CPP11_UNIQUE_PTR #endif -// Use of __COUNTER__ is suppressed if __JETBRAINS_IDE__ is #defined (meaning we're being parsed by a JetBrains IDE for -// analytics) because, at time of writing, __COUNTER__ is not properly handled by it. -// This does not affect compilation -#if defined(CATCH_INTERNAL_CONFIG_COUNTER) && !defined(CATCH_CONFIG_NO_COUNTER) && !defined(CATCH_CONFIG_COUNTER) && !defined(__JETBRAINS_IDE__) +#if defined(CATCH_INTERNAL_CONFIG_COUNTER) && !defined(CATCH_CONFIG_NO_COUNTER) && !defined(CATCH_CONFIG_COUNTER) # define CATCH_CONFIG_COUNTER #endif #if defined(CATCH_INTERNAL_CONFIG_CPP11_SHUFFLE) && !defined(CATCH_CONFIG_CPP11_NO_SHUFFLE) && !defined(CATCH_CONFIG_CPP11_SHUFFLE) && !defined(CATCH_CONFIG_NO_CPP11) # define CATCH_CONFIG_CPP11_SHUFFLE #endif +# if defined(CATCH_INTERNAL_CONFIG_CPP11_TYPE_TRAITS) && !defined(CATCH_CONFIG_CPP11_NO_TYPE_TRAITS) && !defined(CATCH_CONFIG_CPP11_TYPE_TRAITS) && !defined(CATCH_CONFIG_NO_CPP11) +# define CATCH_CONFIG_CPP11_TYPE_TRAITS +# endif +#if defined(CATCH_INTERNAL_CONFIG_WINDOWS_SEH) && !defined(CATCH_CONFIG_NO_WINDOWS_SEH) && !defined(CATCH_CONFIG_WINDOWS_SEH) +# define CATCH_CONFIG_WINDOWS_SEH +#endif +// This is set by default, because we assume that unix compilers are posix-signal-compatible by default. +#if !defined(CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_POSIX_SIGNALS) +# define CATCH_CONFIG_POSIX_SIGNALS +#endif #if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS) # define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS +# define CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS +#endif +#if !defined(CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS +# define CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS #endif // noexcept support: @@ -322,7 +382,6 @@ #define INTERNAL_CATCH_STRINGIFY( expr ) INTERNAL_CATCH_STRINGIFY2( expr ) #include -#include #include namespace Catch { @@ -362,14 +421,14 @@ namespace Catch { }; template - inline void deleteAll( ContainerT& container ) { + void deleteAll( ContainerT& container ) { typename ContainerT::const_iterator it = container.begin(); typename ContainerT::const_iterator itEnd = container.end(); for(; it != itEnd; ++it ) delete *it; } template - inline void deleteAllValues( AssociativeContainerT& container ) { + void deleteAllValues( AssociativeContainerT& container ) { typename AssociativeContainerT::const_iterator it = container.begin(); typename AssociativeContainerT::const_iterator itEnd = container.end(); for(; it != itEnd; ++it ) @@ -377,7 +436,9 @@ namespace Catch { } bool startsWith( std::string const& s, std::string const& prefix ); + bool startsWith( std::string const& s, char prefix ); bool endsWith( std::string const& s, std::string const& suffix ); + bool endsWith( std::string const& s, char suffix ); bool contains( std::string const& s, std::string const& infix ); void toLowerInPlace( std::string& s ); std::string toLower( std::string const& s ); @@ -397,8 +458,8 @@ namespace Catch { SourceLineInfo(); SourceLineInfo( char const* _file, std::size_t _line ); - SourceLineInfo( SourceLineInfo const& other ); # ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS + SourceLineInfo(SourceLineInfo const& other) = default; SourceLineInfo( SourceLineInfo && ) = default; SourceLineInfo& operator = ( SourceLineInfo const& ) = default; SourceLineInfo& operator = ( SourceLineInfo && ) = default; @@ -407,15 +468,16 @@ namespace Catch { bool operator == ( SourceLineInfo const& other ) const; bool operator < ( SourceLineInfo const& other ) const; - std::string file; + char const* file; std::size_t line; }; std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ); // This is just here to avoid compiler warnings with macro constants and boolean literals - inline bool alwaysTrue( std::size_t = 0 ) { return true; } - inline bool alwaysFalse( std::size_t = 0 ) { return false; } + inline bool isTrue( bool value ){ return value; } + inline bool alwaysTrue() { return true; } + inline bool alwaysFalse() { return false; } void throwLogicError( std::string const& message, SourceLineInfo const& locationInfo ); @@ -440,15 +502,12 @@ namespace Catch { #define CATCH_INTERNAL_LINEINFO ::Catch::SourceLineInfo( __FILE__, static_cast( __LINE__ ) ) #define CATCH_INTERNAL_ERROR( msg ) ::Catch::throwLogicError( msg, CATCH_INTERNAL_LINEINFO ); -#include - namespace Catch { class NotImplementedException : public std::exception { public: NotImplementedException( SourceLineInfo const& lineInfo ); - NotImplementedException( NotImplementedException const& ) {} virtual ~NotImplementedException() CATCH_NOEXCEPT {} @@ -574,10 +633,6 @@ namespace Catch { #pragma clang diagnostic pop #endif -#include -#include -#include - namespace Catch { class TestCase; @@ -721,59 +776,76 @@ void registerTestCaseFunction /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_TESTCASE2( TestName, ... ) \ static void TestName(); \ - namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &TestName, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( __VA_ARGS__ ) ); }\ + CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \ + namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &TestName, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( __VA_ARGS__ ) ); } /* NOLINT */ \ + CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS \ static void TestName() #define INTERNAL_CATCH_TESTCASE( ... ) \ INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), __VA_ARGS__ ) /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, ... ) \ - namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &QualifiedMethod, "&" #QualifiedMethod, Catch::NameAndDesc( __VA_ARGS__ ), CATCH_INTERNAL_LINEINFO ); } + CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \ + namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &QualifiedMethod, "&" #QualifiedMethod, Catch::NameAndDesc( __VA_ARGS__ ), CATCH_INTERNAL_LINEINFO ); } /* NOLINT */ \ + CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_TEST_CASE_METHOD2( TestName, ClassName, ... )\ + CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \ namespace{ \ struct TestName : ClassName{ \ void test(); \ }; \ - Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( &TestName::test, #ClassName, Catch::NameAndDesc( __VA_ARGS__ ), CATCH_INTERNAL_LINEINFO ); \ + Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( &TestName::test, #ClassName, Catch::NameAndDesc( __VA_ARGS__ ), CATCH_INTERNAL_LINEINFO ); /* NOLINT */ \ } \ + CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS \ void TestName::test() #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, ... ) \ INTERNAL_CATCH_TEST_CASE_METHOD2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), ClassName, __VA_ARGS__ ) /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_REGISTER_TESTCASE( Function, ... ) \ - Catch::AutoReg( Function, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( __VA_ARGS__ ) ); + CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \ + Catch::AutoReg( Function, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( __VA_ARGS__ ) ); /* NOLINT */ \ + CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS #else /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_TESTCASE2( TestName, Name, Desc ) \ static void TestName(); \ - namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &TestName, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( Name, Desc ) ); }\ + CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \ + namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &TestName, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( Name, Desc ) ); } /* NOLINT */ \ + CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS \ static void TestName() #define INTERNAL_CATCH_TESTCASE( Name, Desc ) \ INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), Name, Desc ) /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, Name, Desc ) \ - namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &QualifiedMethod, "&" #QualifiedMethod, Catch::NameAndDesc( Name, Desc ), CATCH_INTERNAL_LINEINFO ); } + CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \ + namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &QualifiedMethod, "&" #QualifiedMethod, Catch::NameAndDesc( Name, Desc ), CATCH_INTERNAL_LINEINFO ); } /* NOLINT */ \ + CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_TEST_CASE_METHOD2( TestCaseName, ClassName, TestName, Desc )\ + CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \ namespace{ \ struct TestCaseName : ClassName{ \ void test(); \ }; \ - Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( &TestCaseName::test, #ClassName, Catch::NameAndDesc( TestName, Desc ), CATCH_INTERNAL_LINEINFO ); \ + Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( &TestCaseName::test, #ClassName, Catch::NameAndDesc( TestName, Desc ), CATCH_INTERNAL_LINEINFO ); /* NOLINT */ \ } \ + CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS \ void TestCaseName::test() #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, TestName, Desc )\ INTERNAL_CATCH_TEST_CASE_METHOD2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), ClassName, TestName, Desc ) /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_REGISTER_TESTCASE( Function, Name, Desc ) \ - Catch::AutoReg( Function, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( Name, Desc ) ); + CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \ + Catch::AutoReg( Function, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( Name, Desc ) ); /* NOLINT */ \ + CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS + #endif // #included from: internal/catch_capture.hpp @@ -841,27 +913,83 @@ namespace Catch { namespace Catch { + struct STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison; + + struct DecomposedExpression + { + virtual ~DecomposedExpression() {} + virtual bool isBinaryExpression() const { + return false; + } + virtual void reconstructExpression( std::string& dest ) const = 0; + + // Only simple binary comparisons can be decomposed. + // If more complex check is required then wrap sub-expressions in parentheses. + template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator + ( T const& ); + template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator - ( T const& ); + template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator * ( T const& ); + template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator / ( T const& ); + template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator % ( T const& ); + template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator && ( T const& ); + template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator || ( T const& ); + + private: + DecomposedExpression& operator = (DecomposedExpression const&); + }; + struct AssertionInfo { - AssertionInfo() {} - AssertionInfo( std::string const& _macroName, + AssertionInfo(); + AssertionInfo( char const * _macroName, SourceLineInfo const& _lineInfo, - std::string const& _capturedExpression, - ResultDisposition::Flags _resultDisposition ); + char const * _capturedExpression, + ResultDisposition::Flags _resultDisposition, + char const * _secondArg = ""); - std::string macroName; + char const * macroName; SourceLineInfo lineInfo; - std::string capturedExpression; + char const * capturedExpression; ResultDisposition::Flags resultDisposition; + char const * secondArg; }; struct AssertionResultData { - AssertionResultData() : resultType( ResultWas::Unknown ) {} + AssertionResultData() : decomposedExpression( CATCH_NULL ) + , resultType( ResultWas::Unknown ) + , negated( false ) + , parenthesized( false ) {} + + void negate( bool parenthesize ) { + negated = !negated; + parenthesized = parenthesize; + if( resultType == ResultWas::Ok ) + resultType = ResultWas::ExpressionFailed; + else if( resultType == ResultWas::ExpressionFailed ) + resultType = ResultWas::Ok; + } + + std::string const& reconstructExpression() const { + if( decomposedExpression != CATCH_NULL ) { + decomposedExpression->reconstructExpression( reconstructedExpression ); + if( parenthesized ) { + reconstructedExpression.insert( 0, 1, '(' ); + reconstructedExpression.append( 1, ')' ); + } + if( negated ) { + reconstructedExpression.insert( 0, 1, '!' ); + } + decomposedExpression = CATCH_NULL; + } + return reconstructedExpression; + } - std::string reconstructedExpression; + mutable DecomposedExpression const* decomposedExpression; + mutable std::string reconstructedExpression; std::string message; ResultWas::OfType resultType; + bool negated; + bool parenthesized; }; class AssertionResult { @@ -888,6 +1016,8 @@ namespace Catch { std::string getMessage() const; SourceLineInfo getSourceInfo() const; std::string getTestMacroName() const; + void discardDecomposedExpression() const; + void expandDecomposedExpression() const; protected: AssertionInfo m_info; @@ -903,313 +1033,161 @@ namespace Catch { namespace Matchers { namespace Impl { - namespace Generic { - template class AllOf; - template class AnyOf; - template class Not; - } - - template - struct Matcher : SharedImpl - { - typedef ExpressionT ExpressionType; - - virtual ~Matcher() {} - virtual Ptr clone() const = 0; - virtual bool match( ExpressionT const& expr ) const = 0; - virtual std::string toString() const = 0; - - Generic::AllOf operator && ( Matcher const& other ) const; - Generic::AnyOf operator || ( Matcher const& other ) const; - Generic::Not operator ! () const; - }; - - template - struct MatcherImpl : Matcher { - - virtual Ptr > clone() const { - return Ptr >( new DerivedT( static_cast( *this ) ) ); - } - }; + template struct MatchAllOf; + template struct MatchAnyOf; + template struct MatchNotOf; - namespace Generic { - template - class Not : public MatcherImpl, ExpressionT> { + class MatcherUntypedBase { public: - explicit Not( Matcher const& matcher ) : m_matcher(matcher.clone()) {} - Not( Not const& other ) : m_matcher( other.m_matcher ) {} - - virtual bool match( ExpressionT const& expr ) const CATCH_OVERRIDE { - return !m_matcher->match( expr ); + std::string toString() const { + if( m_cachedToString.empty() ) + m_cachedToString = describe(); + return m_cachedToString; } - virtual std::string toString() const CATCH_OVERRIDE { - return "not " + m_matcher->toString(); - } + protected: + virtual ~MatcherUntypedBase(); + virtual std::string describe() const = 0; + mutable std::string m_cachedToString; private: - Ptr< Matcher > m_matcher; + MatcherUntypedBase& operator = ( MatcherUntypedBase const& ); }; - template - class AllOf : public MatcherImpl, ExpressionT> { - public: + template + struct MatcherMethod { + virtual bool match( ObjectT const& arg ) const = 0; + }; + template + struct MatcherMethod { + virtual bool match( PtrT* arg ) const = 0; + }; - AllOf() {} - AllOf( AllOf const& other ) : m_matchers( other.m_matchers ) {} + template + struct MatcherBase : MatcherUntypedBase, MatcherMethod { - AllOf& add( Matcher const& matcher ) { - m_matchers.push_back( matcher.clone() ); - return *this; - } - virtual bool match( ExpressionT const& expr ) const - { - for( std::size_t i = 0; i < m_matchers.size(); ++i ) - if( !m_matchers[i]->match( expr ) ) + MatchAllOf operator && ( MatcherBase const& other ) const; + MatchAnyOf operator || ( MatcherBase const& other ) const; + MatchNotOf operator ! () const; + }; + + template + struct MatchAllOf : MatcherBase { + virtual bool match( ArgT const& arg ) const CATCH_OVERRIDE { + for( std::size_t i = 0; i < m_matchers.size(); ++i ) { + if (!m_matchers[i]->match(arg)) return false; + } return true; } - virtual std::string toString() const { - std::ostringstream oss; - oss << "( "; + virtual std::string describe() const CATCH_OVERRIDE { + std::string description; + description.reserve( 4 + m_matchers.size()*32 ); + description += "( "; for( std::size_t i = 0; i < m_matchers.size(); ++i ) { if( i != 0 ) - oss << " and "; - oss << m_matchers[i]->toString(); + description += " and "; + description += m_matchers[i]->toString(); } - oss << " )"; - return oss.str(); + description += " )"; + return description; } - AllOf operator && ( Matcher const& other ) const { - AllOf allOfExpr( *this ); - allOfExpr.add( other ); - return allOfExpr; + MatchAllOf& operator && ( MatcherBase const& other ) { + m_matchers.push_back( &other ); + return *this; } - private: - std::vector > > m_matchers; + std::vector const*> m_matchers; }; + template + struct MatchAnyOf : MatcherBase { - template - class AnyOf : public MatcherImpl, ExpressionT> { - public: - - AnyOf() {} - AnyOf( AnyOf const& other ) : m_matchers( other.m_matchers ) {} - - AnyOf& add( Matcher const& matcher ) { - m_matchers.push_back( matcher.clone() ); - return *this; - } - virtual bool match( ExpressionT const& expr ) const - { - for( std::size_t i = 0; i < m_matchers.size(); ++i ) - if( m_matchers[i]->match( expr ) ) + virtual bool match( ArgT const& arg ) const CATCH_OVERRIDE { + for( std::size_t i = 0; i < m_matchers.size(); ++i ) { + if (m_matchers[i]->match(arg)) return true; + } return false; } - virtual std::string toString() const { - std::ostringstream oss; - oss << "( "; + virtual std::string describe() const CATCH_OVERRIDE { + std::string description; + description.reserve( 4 + m_matchers.size()*32 ); + description += "( "; for( std::size_t i = 0; i < m_matchers.size(); ++i ) { if( i != 0 ) - oss << " or "; - oss << m_matchers[i]->toString(); + description += " or "; + description += m_matchers[i]->toString(); } - oss << " )"; - return oss.str(); - } - - AnyOf operator || ( Matcher const& other ) const { - AnyOf anyOfExpr( *this ); - anyOfExpr.add( other ); - return anyOfExpr; - } - - private: - std::vector > > m_matchers; - }; - - } // namespace Generic - - template - Generic::AllOf Matcher::operator && ( Matcher const& other ) const { - Generic::AllOf allOfExpr; - allOfExpr.add( *this ); - allOfExpr.add( other ); - return allOfExpr; - } - - template - Generic::AnyOf Matcher::operator || ( Matcher const& other ) const { - Generic::AnyOf anyOfExpr; - anyOfExpr.add( *this ); - anyOfExpr.add( other ); - return anyOfExpr; - } - - template - Generic::Not Matcher::operator ! () const { - return Generic::Not( *this ); - } - - namespace StdString { - - inline std::string makeString( std::string const& str ) { return str; } - inline std::string makeString( const char* str ) { return str ? std::string( str ) : std::string(); } - - struct CasedString - { - CasedString( std::string const& str, CaseSensitive::Choice caseSensitivity ) - : m_caseSensitivity( caseSensitivity ), - m_str( adjustString( str ) ) - {} - std::string adjustString( std::string const& str ) const { - return m_caseSensitivity == CaseSensitive::No - ? toLower( str ) - : str; - - } - std::string toStringSuffix() const - { - return m_caseSensitivity == CaseSensitive::No - ? " (case insensitive)" - : ""; + description += " )"; + return description; } - CaseSensitive::Choice m_caseSensitivity; - std::string m_str; - }; - - struct Equals : MatcherImpl { - Equals( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) - : m_data( str, caseSensitivity ) - {} - Equals( Equals const& other ) : m_data( other.m_data ){} - virtual ~Equals(); - - virtual bool match( std::string const& expr ) const { - return m_data.m_str == m_data.adjustString( expr );; - } - virtual std::string toString() const { - return "equals: \"" + m_data.m_str + "\"" + m_data.toStringSuffix(); + MatchAnyOf& operator || ( MatcherBase const& other ) { + m_matchers.push_back( &other ); + return *this; } - CasedString m_data; + std::vector const*> m_matchers; }; - struct Contains : MatcherImpl { - Contains( std::string const& substr, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) - : m_data( substr, caseSensitivity ){} - Contains( Contains const& other ) : m_data( other.m_data ){} + template + struct MatchNotOf : MatcherBase { - virtual ~Contains(); + MatchNotOf( MatcherBase const& underlyingMatcher ) : m_underlyingMatcher( underlyingMatcher ) {} - virtual bool match( std::string const& expr ) const { - return m_data.adjustString( expr ).find( m_data.m_str ) != std::string::npos; - } - virtual std::string toString() const { - return "contains: \"" + m_data.m_str + "\"" + m_data.toStringSuffix(); + virtual bool match( ArgT const& arg ) const CATCH_OVERRIDE { + return !m_underlyingMatcher.match( arg ); } - CasedString m_data; - }; - - struct StartsWith : MatcherImpl { - StartsWith( std::string const& substr, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) - : m_data( substr, caseSensitivity ){} - - StartsWith( StartsWith const& other ) : m_data( other.m_data ){} - - virtual ~StartsWith(); - - virtual bool match( std::string const& expr ) const { - return startsWith( m_data.adjustString( expr ), m_data.m_str ); - } - virtual std::string toString() const { - return "starts with: \"" + m_data.m_str + "\"" + m_data.toStringSuffix(); + virtual std::string describe() const CATCH_OVERRIDE { + return "not " + m_underlyingMatcher.toString(); } - - CasedString m_data; + MatcherBase const& m_underlyingMatcher; }; - struct EndsWith : MatcherImpl { - EndsWith( std::string const& substr, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) - : m_data( substr, caseSensitivity ){} - EndsWith( EndsWith const& other ) : m_data( other.m_data ){} - - virtual ~EndsWith(); - - virtual bool match( std::string const& expr ) const { - return endsWith( m_data.adjustString( expr ), m_data.m_str ); - } - virtual std::string toString() const { - return "ends with: \"" + m_data.m_str + "\"" + m_data.toStringSuffix(); - } + template + MatchAllOf MatcherBase::operator && ( MatcherBase const& other ) const { + return MatchAllOf() && *this && other; + } + template + MatchAnyOf MatcherBase::operator || ( MatcherBase const& other ) const { + return MatchAnyOf() || *this || other; + } + template + MatchNotOf MatcherBase::operator ! () const { + return MatchNotOf( *this ); + } - CasedString m_data; - }; - } // namespace StdString } // namespace Impl // The following functions create the actual matcher objects. // This allows the types to be inferred - template - inline Impl::Generic::Not Not( Impl::Matcher const& m ) { - return Impl::Generic::Not( m ); - } - - template - inline Impl::Generic::AllOf AllOf( Impl::Matcher const& m1, - Impl::Matcher const& m2 ) { - return Impl::Generic::AllOf().add( m1 ).add( m2 ); - } - template - inline Impl::Generic::AllOf AllOf( Impl::Matcher const& m1, - Impl::Matcher const& m2, - Impl::Matcher const& m3 ) { - return Impl::Generic::AllOf().add( m1 ).add( m2 ).add( m3 ); - } - template - inline Impl::Generic::AnyOf AnyOf( Impl::Matcher const& m1, - Impl::Matcher const& m2 ) { - return Impl::Generic::AnyOf().add( m1 ).add( m2 ); - } - template - inline Impl::Generic::AnyOf AnyOf( Impl::Matcher const& m1, - Impl::Matcher const& m2, - Impl::Matcher const& m3 ) { - return Impl::Generic::AnyOf().add( m1 ).add( m2 ).add( m3 ); - } - - inline Impl::StdString::Equals Equals( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) { - return Impl::StdString::Equals( str, caseSensitivity ); - } - inline Impl::StdString::Equals Equals( const char* str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) { - return Impl::StdString::Equals( Impl::StdString::makeString( str ), caseSensitivity ); - } - inline Impl::StdString::Contains Contains( std::string const& substr, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) { - return Impl::StdString::Contains( substr, caseSensitivity ); - } - inline Impl::StdString::Contains Contains( const char* substr, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) { - return Impl::StdString::Contains( Impl::StdString::makeString( substr ), caseSensitivity ); + // - deprecated: prefer ||, && and ! + template + Impl::MatchNotOf Not( Impl::MatcherBase const& underlyingMatcher ) { + return Impl::MatchNotOf( underlyingMatcher ); } - inline Impl::StdString::StartsWith StartsWith( std::string const& substr ) { - return Impl::StdString::StartsWith( substr ); + template + Impl::MatchAllOf AllOf( Impl::MatcherBase const& m1, Impl::MatcherBase const& m2 ) { + return Impl::MatchAllOf() && m1 && m2; } - inline Impl::StdString::StartsWith StartsWith( const char* substr ) { - return Impl::StdString::StartsWith( Impl::StdString::makeString( substr ) ); + template + Impl::MatchAllOf AllOf( Impl::MatcherBase const& m1, Impl::MatcherBase const& m2, Impl::MatcherBase const& m3 ) { + return Impl::MatchAllOf() && m1 && m2 && m3; } - inline Impl::StdString::EndsWith EndsWith( std::string const& substr ) { - return Impl::StdString::EndsWith( substr ); + template + Impl::MatchAnyOf AnyOf( Impl::MatcherBase const& m1, Impl::MatcherBase const& m2 ) { + return Impl::MatchAnyOf() || m1 || m2; } - inline Impl::StdString::EndsWith EndsWith( const char* substr ) { - return Impl::StdString::EndsWith( Impl::StdString::makeString( substr ) ); + template + Impl::MatchAnyOf AnyOf( Impl::MatcherBase const& m1, Impl::MatcherBase const& m2, Impl::MatcherBase const& m3 ) { + return Impl::MatchAnyOf() || m1 || m2 || m3; } } // namespace Matchers using namespace Matchers; +using Matchers::Impl::MatcherBase; } // namespace Catch @@ -1219,28 +1197,27 @@ namespace Catch { template class ExpressionLhs; - struct STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison; - struct CopyableStream { CopyableStream() {} CopyableStream( CopyableStream const& other ) { oss << other.oss.str(); } CopyableStream& operator=( CopyableStream const& other ) { - oss.str(""); + oss.str(std::string()); oss << other.oss.str(); return *this; } std::ostringstream oss; }; - class ResultBuilder { + class ResultBuilder : public DecomposedExpression { public: ResultBuilder( char const* macroName, SourceLineInfo const& lineInfo, char const* capturedExpression, ResultDisposition::Flags resultDisposition, char const* secondArg = "" ); + ~ResultBuilder(); template ExpressionLhs operator <= ( T const& operand ); @@ -1248,46 +1225,60 @@ namespace Catch { template ResultBuilder& operator << ( T const& value ) { - m_stream.oss << value; + stream().oss << value; return *this; } - template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator && ( RhsT const& ); - template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator || ( RhsT const& ); - ResultBuilder& setResultType( ResultWas::OfType result ); ResultBuilder& setResultType( bool result ); - ResultBuilder& setLhs( std::string const& lhs ); - ResultBuilder& setRhs( std::string const& rhs ); - ResultBuilder& setOp( std::string const& op ); - void endExpression(); + void endExpression( DecomposedExpression const& expr ); + + virtual void reconstructExpression( std::string& dest ) const CATCH_OVERRIDE; - std::string reconstructExpression() const; AssertionResult build() const; + AssertionResult build( DecomposedExpression const& expr ) const; void useActiveException( ResultDisposition::Flags resultDisposition = ResultDisposition::Normal ); void captureResult( ResultWas::OfType resultType ); void captureExpression(); void captureExpectedException( std::string const& expectedMessage ); - void captureExpectedException( Matchers::Impl::Matcher const& matcher ); + void captureExpectedException( Matchers::Impl::MatcherBase const& matcher ); void handleResult( AssertionResult const& result ); void react(); bool shouldDebugBreak() const; bool allowThrows() const; + template + void captureMatch( ArgT const& arg, MatcherT const& matcher, char const* matcherString ); + + void setExceptionGuard(); + void unsetExceptionGuard(); + private: AssertionInfo m_assertionInfo; AssertionResultData m_data; - struct ExprComponents { - ExprComponents() : testFalse( false ) {} - bool testFalse; - std::string lhs, rhs, op; - } m_exprComponents; - CopyableStream m_stream; + + CopyableStream &stream() + { + if(!m_usedStream) + { + m_usedStream = true; + m_stream().oss.str(""); + } + return m_stream(); + } + + static CopyableStream &m_stream() + { + static CopyableStream s; + return s; + } bool m_shouldDebugBreak; bool m_shouldThrow; + bool m_guardException; + bool m_usedStream; }; } // namespace Catch @@ -1302,6 +1293,8 @@ namespace Catch { #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable:4389) // '==' : signed/unsigned mismatch +#pragma warning(disable:4018) // more "signed/unsigned mismatch" +#pragma warning(disable:4312) // Converting int to T* using reinterpret_cast (issue on x64 platform) #endif #include @@ -1327,7 +1320,7 @@ namespace Internal { template<> struct OperatorTraits{ static const char* getName(){ return ">="; } }; template - inline T& opCast(T const& t) { return const_cast(t); } + T& opCast(T const& t) { return const_cast(t); } // nullptr_t support based on pull request #154 from Konstantin Baumann #ifdef CATCH_CONFIG_CPP11_NULLPTR @@ -1337,7 +1330,7 @@ namespace Internal { // So the compare overloads can be operator agnostic we convey the operator as a template // enum, which is used to specialise an Evaluator for doing the comparison. template - class Evaluator{}; + struct Evaluator{}; template struct Evaluator { @@ -1603,7 +1596,7 @@ std::string toString( std::nullptr_t ); #ifdef __OBJC__ std::string toString( NSString const * const& nsstring ); - std::string toString( NSString * CATCH_ARC_STRONG const& nsstring ); + std::string toString( NSString * CATCH_ARC_STRONG & nsstring ); std::string toString( NSObject* const& nsObject ); #endif @@ -1611,6 +1604,7 @@ namespace Detail { extern const std::string unprintableString; + #if !defined(CATCH_CONFIG_CPP11_STREAM_INSERTABLE_CHECK) struct BorgType { template BorgType( T const& ); }; @@ -1629,6 +1623,20 @@ namespace Detail { static T const&t; enum { value = sizeof( testStreamable(s << t) ) == sizeof( TrueType ) }; }; +#else + template + class IsStreamInsertable { + template + static auto test(int) + -> decltype( std::declval() << std::declval(), std::true_type() ); + + template + static auto test(...) -> std::false_type; + + public: + static const bool value = decltype(test(0))::value; + }; +#endif #if defined(CATCH_CONFIG_CPP11_IS_ENUM) template - inline std::string rawMemoryToString( const T& object ) { + std::string rawMemoryToString( const T& object ) { return rawMemoryToString( &object, sizeof(object) ); } @@ -1805,90 +1813,159 @@ std::string toString( T const& value ) { namespace Catch { -// Wraps the LHS of an expression and captures the operator and RHS (if any) - -// wrapping them all in a ResultBuilder object -template -class ExpressionLhs { - ExpressionLhs& operator = ( ExpressionLhs const& ); -# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS - ExpressionLhs& operator = ( ExpressionLhs && ) = delete; -# endif +template +class BinaryExpression; + +template +class MatchExpression; +// Wraps the LHS of an expression and overloads comparison operators +// for also capturing those and RHS (if any) +template +class ExpressionLhs : public DecomposedExpression { public: - ExpressionLhs( ResultBuilder& rb, T lhs ) : m_rb( rb ), m_lhs( lhs ) {} -# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS - ExpressionLhs( ExpressionLhs const& ) = default; - ExpressionLhs( ExpressionLhs && ) = default; -# endif + ExpressionLhs( ResultBuilder& rb, T lhs ) : m_rb( rb ), m_lhs( lhs ), m_truthy(false) {} + + ExpressionLhs& operator = ( const ExpressionLhs& ); template - ResultBuilder& operator == ( RhsT const& rhs ) { + BinaryExpression + operator == ( RhsT const& rhs ) { return captureExpression( rhs ); } template - ResultBuilder& operator != ( RhsT const& rhs ) { + BinaryExpression + operator != ( RhsT const& rhs ) { return captureExpression( rhs ); } template - ResultBuilder& operator < ( RhsT const& rhs ) { + BinaryExpression + operator < ( RhsT const& rhs ) { return captureExpression( rhs ); } template - ResultBuilder& operator > ( RhsT const& rhs ) { + BinaryExpression + operator > ( RhsT const& rhs ) { return captureExpression( rhs ); } template - ResultBuilder& operator <= ( RhsT const& rhs ) { + BinaryExpression + operator <= ( RhsT const& rhs ) { return captureExpression( rhs ); } template - ResultBuilder& operator >= ( RhsT const& rhs ) { + BinaryExpression + operator >= ( RhsT const& rhs ) { return captureExpression( rhs ); } - ResultBuilder& operator == ( bool rhs ) { + BinaryExpression operator == ( bool rhs ) { return captureExpression( rhs ); } - ResultBuilder& operator != ( bool rhs ) { + BinaryExpression operator != ( bool rhs ) { return captureExpression( rhs ); } void endExpression() { - bool value = m_lhs ? true : false; + m_truthy = m_lhs ? true : false; m_rb - .setLhs( Catch::toString( value ) ) - .setResultType( value ) - .endExpression(); + .setResultType( m_truthy ) + .endExpression( *this ); } - // Only simple binary expressions are allowed on the LHS. - // If more complex compositions are required then place the sub expression in parentheses - template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator + ( RhsT const& ); - template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator - ( RhsT const& ); - template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator / ( RhsT const& ); - template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator * ( RhsT const& ); - template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator && ( RhsT const& ); - template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator || ( RhsT const& ); + virtual void reconstructExpression( std::string& dest ) const CATCH_OVERRIDE { + dest = Catch::toString( m_lhs ); + } private: template - ResultBuilder& captureExpression( RhsT const& rhs ) { - return m_rb - .setResultType( Internal::compare( m_lhs, rhs ) ) - .setLhs( Catch::toString( m_lhs ) ) - .setRhs( Catch::toString( rhs ) ) - .setOp( Internal::OperatorTraits::getName() ); + BinaryExpression captureExpression( RhsT& rhs ) const { + return BinaryExpression( m_rb, m_lhs, rhs ); + } + + template + BinaryExpression captureExpression( bool rhs ) const { + return BinaryExpression( m_rb, m_lhs, rhs ); } private: ResultBuilder& m_rb; T m_lhs; + bool m_truthy; +}; + +template +class BinaryExpression : public DecomposedExpression { +public: + BinaryExpression( ResultBuilder& rb, LhsT lhs, RhsT rhs ) + : m_rb( rb ), m_lhs( lhs ), m_rhs( rhs ) {} + + BinaryExpression& operator = ( BinaryExpression& ); + + void endExpression() const { + m_rb + .setResultType( Internal::compare( m_lhs, m_rhs ) ) + .endExpression( *this ); + } + + virtual bool isBinaryExpression() const CATCH_OVERRIDE { + return true; + } + + virtual void reconstructExpression( std::string& dest ) const CATCH_OVERRIDE { + std::string lhs = Catch::toString( m_lhs ); + std::string rhs = Catch::toString( m_rhs ); + char delim = lhs.size() + rhs.size() < 40 && + lhs.find('\n') == std::string::npos && + rhs.find('\n') == std::string::npos ? ' ' : '\n'; + dest.reserve( 7 + lhs.size() + rhs.size() ); + // 2 for spaces around operator + // 2 for operator + // 2 for parentheses (conditionally added later) + // 1 for negation (conditionally added later) + dest = lhs; + dest += delim; + dest += Internal::OperatorTraits::getName(); + dest += delim; + dest += rhs; + } + +private: + ResultBuilder& m_rb; + LhsT m_lhs; + RhsT m_rhs; +}; + +template +class MatchExpression : public DecomposedExpression { +public: + MatchExpression( ArgT arg, MatcherT matcher, char const* matcherString ) + : m_arg( arg ), m_matcher( matcher ), m_matcherString( matcherString ) {} + + virtual bool isBinaryExpression() const CATCH_OVERRIDE { + return true; + } + + virtual void reconstructExpression( std::string& dest ) const CATCH_OVERRIDE { + std::string matcherAsString = m_matcher.toString(); + dest = Catch::toString( m_arg ); + dest += ' '; + if( matcherAsString == Detail::unprintableString ) + dest += m_matcherString; + else + dest += matcherAsString; + } + +private: + ArgT m_arg; + MatcherT m_matcher; + char const* m_matcherString; }; } // end namespace Catch @@ -1897,7 +1974,7 @@ class ExpressionLhs { namespace Catch { template - inline ExpressionLhs ResultBuilder::operator <= ( T const& operand ) { + ExpressionLhs ResultBuilder::operator <= ( T const& operand ) { return ExpressionLhs( *this, operand ); } @@ -1905,6 +1982,14 @@ namespace Catch { return ExpressionLhs( *this, value ); } + template + void ResultBuilder::captureMatch( ArgT const& arg, MatcherT const& matcher, + char const* matcherString ) { + MatchExpression expr( arg, matcher, matcherString ); + setResultType( matcher.match( arg ) ); + endExpression( expr ); + } + } // namespace Catch // #included from: catch_message.h @@ -1994,7 +2079,13 @@ namespace Catch { virtual std::string getCurrentTestName() const = 0; virtual const AssertionResult* getLastResult() const = 0; + virtual void exceptionEarlyReported() = 0; + virtual void handleFatalErrorCondition( std::string const& message ) = 0; + + virtual bool lastAssertionPassed() = 0; + virtual void assertionPassed() = 0; + virtual void assertionRun() = 0; }; IResultCapture& getResultCapture(); @@ -2034,14 +2125,15 @@ namespace Catch{ // The following code snippet based on: // http://cocoawithlove.com/2008/03/break-into-debugger.html - #ifdef DEBUG - #if defined(__ppc64__) || defined(__ppc__) - #define CATCH_TRAP() \ - __asm__("li r0, 20\nsc\nnop\nli r0, 37\nli r4, 2\nsc\nnop\n" \ - : : : "memory","r0","r3","r4" ) - #else - #define CATCH_TRAP() _asm__("int $3\n" : : ) - #endif + #if defined(__ppc64__) || defined(__ppc__) + #define CATCH_TRAP() \ + __asm__("li r0, 20\nsc\nnop\nli r0, 37\nli r4, 2\nsc\nnop\n" \ + : : : "memory","r0","r3","r4" ) /* NOLINT */ + #elif defined(__aarch64__) + // Backport of https://github.com/catchorg/Catch2/commit/a25c1a24af8bffd35727a888a307ff0280cf9387 + #define CATCH_TRAP() __asm__(".inst 0xd4200000") + #else + #define CATCH_TRAP() __asm__("int $3\n" : : /* NOLINT */ ) #endif #elif defined(CATCH_PLATFORM_LINUX) @@ -2049,7 +2141,7 @@ namespace Catch{ // directly at the location of the failing check instead of breaking inside // raise() called from it, i.e. one stack frame below. #if defined(__GNUC__) && (defined(__i386) || defined(__x86_64)) - #define CATCH_TRAP() asm volatile ("int $3") + #define CATCH_TRAP() asm volatile ("int $3") /* NOLINT */ #else // Fall back to the generic way. #include @@ -2080,6 +2172,47 @@ namespace Catch { }; } +#if !defined(CATCH_CONFIG_DISABLE_STRINGIFICATION) +# define CATCH_INTERNAL_STRINGIFY(expr) #expr +#else +# define CATCH_INTERNAL_STRINGIFY(expr) "Disabled by CATCH_CONFIG_DISABLE_STRINGIFICATION" +#endif + +#if defined(CATCH_CONFIG_FAST_COMPILE) +/////////////////////////////////////////////////////////////////////////////// +// We can speedup compilation significantly by breaking into debugger lower in +// the callstack, because then we don't have to expand CATCH_BREAK_INTO_DEBUGGER +// macro in each assertion +#define INTERNAL_CATCH_REACT( resultBuilder ) \ + resultBuilder.react(); + +/////////////////////////////////////////////////////////////////////////////// +// Another way to speed-up compilation is to omit local try-catch for REQUIRE* +// macros. +// This can potentially cause false negative, if the test code catches +// the exception before it propagates back up to the runner. +#define INTERNAL_CATCH_TEST_NO_TRY( macroName, resultDisposition, expr ) \ + do { \ + Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(expr), resultDisposition ); \ + __catchResult.setExceptionGuard(); \ + CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ + ( __catchResult <= expr ).endExpression(); \ + CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \ + __catchResult.unsetExceptionGuard(); \ + INTERNAL_CATCH_REACT( __catchResult ) \ + } while( Catch::isTrue( false && static_cast( !!(expr) ) ) ) // expr here is never evaluated at runtime but it forces the compiler to give it a look +// The double negation silences MSVC's C4800 warning, the static_cast forces short-circuit evaluation if the type has overloaded &&. + +#define INTERNAL_CHECK_THAT_NO_TRY( macroName, matcher, resultDisposition, arg ) \ + do { \ + Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(arg) ", " CATCH_INTERNAL_STRINGIFY(matcher), resultDisposition ); \ + __catchResult.setExceptionGuard(); \ + __catchResult.captureMatch( arg, matcher, CATCH_INTERNAL_STRINGIFY(matcher) ); \ + __catchResult.unsetExceptionGuard(); \ + INTERNAL_CATCH_REACT( __catchResult ) \ + } while( Catch::alwaysFalse() ) + +#else /////////////////////////////////////////////////////////////////////////////// // In the event of a failure works out if the debugger needs to be invoked // and/or an exception thrown and takes appropriate action. @@ -2088,37 +2221,40 @@ namespace Catch { #define INTERNAL_CATCH_REACT( resultBuilder ) \ if( resultBuilder.shouldDebugBreak() ) CATCH_BREAK_INTO_DEBUGGER(); \ resultBuilder.react(); +#endif /////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_TEST( expr, resultDisposition, macroName ) \ +#define INTERNAL_CATCH_TEST( macroName, resultDisposition, expr ) \ do { \ - Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \ + Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(expr), resultDisposition ); \ try { \ CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ ( __catchResult <= expr ).endExpression(); \ + CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \ } \ catch( ... ) { \ - __catchResult.useActiveException( Catch::ResultDisposition::Normal ); \ + __catchResult.useActiveException( resultDisposition ); \ } \ INTERNAL_CATCH_REACT( __catchResult ) \ - } while( Catch::alwaysFalse( sizeof(expr) ) ) // expr here is never evaluated at runtime but it forces the compiler to give it a look + } while( Catch::isTrue( false && static_cast( !!(expr) ) ) ) // expr here is never evaluated at runtime but it forces the compiler to give it a look + // The double negation silences MSVC's C4800 warning, the static_cast forces short-circuit evaluation if the type has overloaded &&. /////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_IF( expr, resultDisposition, macroName ) \ - INTERNAL_CATCH_TEST( expr, resultDisposition, macroName ); \ - if( Catch::getResultCapture().getLastResult()->succeeded() ) +#define INTERNAL_CATCH_IF( macroName, resultDisposition, expr ) \ + INTERNAL_CATCH_TEST( macroName, resultDisposition, expr ); \ + if( Catch::getResultCapture().lastAssertionPassed() ) /////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_ELSE( expr, resultDisposition, macroName ) \ - INTERNAL_CATCH_TEST( expr, resultDisposition, macroName ); \ - if( !Catch::getResultCapture().getLastResult()->succeeded() ) +#define INTERNAL_CATCH_ELSE( macroName, resultDisposition, expr ) \ + INTERNAL_CATCH_TEST( macroName, resultDisposition, expr ); \ + if( !Catch::getResultCapture().lastAssertionPassed() ) /////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_NO_THROW( expr, resultDisposition, macroName ) \ +#define INTERNAL_CATCH_NO_THROW( macroName, resultDisposition, expr ) \ do { \ - Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \ + Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(expr), resultDisposition ); \ try { \ - expr; \ + static_cast(expr); \ __catchResult.captureResult( Catch::ResultWas::Ok ); \ } \ catch( ... ) { \ @@ -2128,12 +2264,12 @@ namespace Catch { } while( Catch::alwaysFalse() ) /////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_THROWS( expr, resultDisposition, matcher, macroName ) \ +#define INTERNAL_CATCH_THROWS( macroName, resultDisposition, matcher, expr ) \ do { \ - Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition, #matcher ); \ + Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(expr), resultDisposition, CATCH_INTERNAL_STRINGIFY(matcher) ); \ if( __catchResult.allowThrows() ) \ try { \ - expr; \ + static_cast(expr); \ __catchResult.captureResult( Catch::ResultWas::DidntThrowException ); \ } \ catch( ... ) { \ @@ -2145,12 +2281,12 @@ namespace Catch { } while( Catch::alwaysFalse() ) /////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_THROWS_AS( expr, exceptionType, resultDisposition, macroName ) \ +#define INTERNAL_CATCH_THROWS_AS( macroName, exceptionType, resultDisposition, expr ) \ do { \ - Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \ + Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(expr) ", " CATCH_INTERNAL_STRINGIFY(exceptionType), resultDisposition ); \ if( __catchResult.allowThrows() ) \ try { \ - expr; \ + static_cast(expr); \ __catchResult.captureResult( Catch::ResultWas::DidntThrowException ); \ } \ catch( exceptionType ) { \ @@ -2166,7 +2302,7 @@ namespace Catch { /////////////////////////////////////////////////////////////////////////////// #ifdef CATCH_CONFIG_VARIADIC_MACROS - #define INTERNAL_CATCH_MSG( messageType, resultDisposition, macroName, ... ) \ + #define INTERNAL_CATCH_MSG( macroName, messageType, resultDisposition, ... ) \ do { \ Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, "", resultDisposition ); \ __catchResult << __VA_ARGS__ + ::Catch::StreamEndStop(); \ @@ -2174,7 +2310,7 @@ namespace Catch { INTERNAL_CATCH_REACT( __catchResult ) \ } while( Catch::alwaysFalse() ) #else - #define INTERNAL_CATCH_MSG( messageType, resultDisposition, macroName, log ) \ + #define INTERNAL_CATCH_MSG( macroName, messageType, resultDisposition, log ) \ do { \ Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, "", resultDisposition ); \ __catchResult << log + ::Catch::StreamEndStop(); \ @@ -2184,21 +2320,15 @@ namespace Catch { #endif /////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_INFO( log, macroName ) \ +#define INTERNAL_CATCH_INFO( macroName, log ) \ Catch::ScopedMessage INTERNAL_CATCH_UNIQUE_NAME( scopedMessage ) = Catch::MessageBuilder( macroName, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log; /////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CHECK_THAT( arg, matcher, resultDisposition, macroName ) \ +#define INTERNAL_CHECK_THAT( macroName, matcher, resultDisposition, arg ) \ do { \ - Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #arg ", " #matcher, resultDisposition ); \ + Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(arg) ", " CATCH_INTERNAL_STRINGIFY(matcher), resultDisposition ); \ try { \ - std::string matcherAsString = (matcher).toString(); \ - __catchResult \ - .setLhs( Catch::toString( arg ) ) \ - .setRhs( matcherAsString == Catch::Detail::unprintableString ? #matcher : matcherAsString ) \ - .setOp( "matches" ) \ - .setResultType( (matcher).match( arg ) ); \ - __catchResult.captureExpression(); \ + __catchResult.captureMatch( arg, matcher, CATCH_INTERNAL_STRINGIFY(matcher) ); \ } catch( ... ) { \ __catchResult.useActiveException( resultDisposition | Catch::ResultDisposition::ContinueOnFailure ); \ } \ @@ -2281,6 +2411,8 @@ namespace Catch { }; } +#include + namespace Catch { struct SectionInfo { @@ -2309,14 +2441,19 @@ namespace Catch { // #included from: catch_timer.h #define TWOBLUECUBES_CATCH_TIMER_H_INCLUDED -#ifdef CATCH_PLATFORM_WINDOWS -typedef unsigned long long uint64_t; +#ifdef _MSC_VER + +namespace Catch { + typedef unsigned long long UInt64; +} #else #include +namespace Catch { + typedef uint64_t UInt64; +} #endif namespace Catch { - class Timer { public: Timer() : m_ticks( 0 ) {} @@ -2326,7 +2463,7 @@ namespace Catch { double getElapsedSeconds() const; private: - uint64_t m_ticks; + UInt64 m_ticks; }; } // namespace Catch @@ -2365,7 +2502,6 @@ namespace Catch { // #included from: internal/catch_generators.hpp #define TWOBLUECUBES_CATCH_GENERATORS_HPP_INCLUDED -#include #include #include #include @@ -2479,7 +2615,7 @@ class CompositeGenerator { private: void move( CompositeGenerator& other ) { - std::copy( other.m_composed.begin(), other.m_composed.end(), std::back_inserter( m_composed ) ); + m_composed.insert( m_composed.end(), other.m_composed.begin(), other.m_composed.end() ); m_totalSize += other.m_totalSize; other.m_composed.clear(); } @@ -2561,12 +2697,15 @@ namespace Catch { struct IExceptionTranslator; struct IReporterRegistry; struct IReporterFactory; + struct ITagAliasRegistry; struct IRegistryHub { virtual ~IRegistryHub(); virtual IReporterRegistry const& getReporterRegistry() const = 0; virtual ITestCaseRegistry const& getTestCaseRegistry() const = 0; + virtual ITagAliasRegistry const& getTagAliasRegistry() const = 0; + virtual IExceptionTranslatorRegistry& getExceptionTranslatorRegistry() = 0; }; @@ -2576,6 +2715,7 @@ namespace Catch { virtual void registerListener( Ptr const& factory ) = 0; virtual void registerTest( TestCase const& testInfo ) = 0; virtual void registerTranslator( const IExceptionTranslator* translator ) = 0; + virtual void registerTagAlias( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) = 0; }; IRegistryHub& getRegistryHub(); @@ -2651,6 +2791,10 @@ namespace Catch { #include #include +#if defined(CATCH_CONFIG_CPP11_TYPE_TRAITS) +#include +#endif + namespace Catch { namespace Detail { @@ -2658,62 +2802,140 @@ namespace Detail { public: explicit Approx ( double value ) : m_epsilon( std::numeric_limits::epsilon()*100 ), + m_margin( 0.0 ), m_scale( 1.0 ), m_value( value ) {} - Approx( Approx const& other ) - : m_epsilon( other.m_epsilon ), - m_scale( other.m_scale ), - m_value( other.m_value ) - {} - static Approx custom() { return Approx( 0 ); } - Approx operator()( double value ) { - Approx approx( value ); +#if defined(CATCH_CONFIG_CPP11_TYPE_TRAITS) + + template ::value>::type> + Approx operator()( T value ) { + Approx approx( static_cast(value) ); approx.epsilon( m_epsilon ); + approx.margin( m_margin ); approx.scale( m_scale ); return approx; } - friend bool operator == ( double lhs, Approx const& rhs ) { + template ::value>::type> + explicit Approx( T value ): Approx(static_cast(value)) + {} + + template ::value>::type> + friend bool operator == ( const T& lhs, Approx const& rhs ) { // Thanks to Richard Harris for his help refining this formula - return fabs( lhs - rhs.m_value ) < rhs.m_epsilon * (rhs.m_scale + (std::max)( fabs(lhs), fabs(rhs.m_value) ) ); + auto lhs_v = double(lhs); + bool relativeOK = std::fabs(lhs_v - rhs.m_value) < rhs.m_epsilon * (rhs.m_scale + (std::max)(std::fabs(lhs_v), std::fabs(rhs.m_value))); + if (relativeOK) { + return true; + } + + return std::fabs(lhs_v - rhs.m_value) <= rhs.m_margin; } - friend bool operator == ( Approx const& lhs, double rhs ) { + template ::value>::type> + friend bool operator == ( Approx const& lhs, const T& rhs ) { return operator==( rhs, lhs ); } - friend bool operator != ( double lhs, Approx const& rhs ) { + template ::value>::type> + friend bool operator != ( T lhs, Approx const& rhs ) { return !operator==( lhs, rhs ); } - friend bool operator != ( Approx const& lhs, double rhs ) { + template ::value>::type> + friend bool operator != ( Approx const& lhs, T rhs ) { return !operator==( rhs, lhs ); } - friend bool operator <= ( double lhs, Approx const& rhs ) - { - return lhs < rhs.m_value || lhs == rhs; + template ::value>::type> + friend bool operator <= ( T lhs, Approx const& rhs ) { + return double(lhs) < rhs.m_value || lhs == rhs; } - friend bool operator <= ( Approx const& lhs, double rhs ) - { - return lhs.m_value < rhs || lhs == rhs; + template ::value>::type> + friend bool operator <= ( Approx const& lhs, T rhs ) { + return lhs.m_value < double(rhs) || lhs == rhs; } - friend bool operator >= ( double lhs, Approx const& rhs ) - { - return lhs > rhs.m_value || lhs == rhs; + template ::value>::type> + friend bool operator >= ( T lhs, Approx const& rhs ) { + return double(lhs) > rhs.m_value || lhs == rhs; } - friend bool operator >= ( Approx const& lhs, double rhs ) - { - return lhs.m_value > rhs || lhs == rhs; + template ::value>::type> + friend bool operator >= ( Approx const& lhs, T rhs ) { + return lhs.m_value > double(rhs) || lhs == rhs; + } + + template ::value>::type> + Approx& epsilon( T newEpsilon ) { + m_epsilon = double(newEpsilon); + return *this; + } + + template ::value>::type> + Approx& margin( T newMargin ) { + m_margin = double(newMargin); + return *this; + } + + template ::value>::type> + Approx& scale( T newScale ) { + m_scale = double(newScale); + return *this; + } + +#else + + Approx operator()( double value ) { + Approx approx( value ); + approx.epsilon( m_epsilon ); + approx.margin( m_margin ); + approx.scale( m_scale ); + return approx; + } + + friend bool operator == ( double lhs, Approx const& rhs ) { + // Thanks to Richard Harris for his help refining this formula + bool relativeOK = std::fabs( lhs - rhs.m_value ) < rhs.m_epsilon * (rhs.m_scale + (std::max)( std::fabs(lhs), std::fabs(rhs.m_value) ) ); + if (relativeOK) { + return true; + } + return std::fabs(lhs - rhs.m_value) <= rhs.m_margin; + } + + friend bool operator == ( Approx const& lhs, double rhs ) { + return operator==( rhs, lhs ); + } + + friend bool operator != ( double lhs, Approx const& rhs ) { + return !operator==( lhs, rhs ); + } + + friend bool operator != ( Approx const& lhs, double rhs ) { + return !operator==( rhs, lhs ); + } + + friend bool operator <= ( double lhs, Approx const& rhs ) { + return lhs < rhs.m_value || lhs == rhs; + } + + friend bool operator <= ( Approx const& lhs, double rhs ) { + return lhs.m_value < rhs || lhs == rhs; + } + + friend bool operator >= ( double lhs, Approx const& rhs ) { + return lhs > rhs.m_value || lhs == rhs; + } + + friend bool operator >= ( Approx const& lhs, double rhs ) { + return lhs.m_value > rhs || lhs == rhs; } Approx& epsilon( double newEpsilon ) { @@ -2721,10 +2943,16 @@ namespace Detail { return *this; } + Approx& margin( double newMargin ) { + m_margin = newMargin; + return *this; + } + Approx& scale( double newScale ) { m_scale = newScale; return *this; } +#endif std::string toString() const { std::ostringstream oss; @@ -2734,6 +2962,7 @@ namespace Detail { private: double m_epsilon; + double m_margin; double m_scale; double m_value; }; @@ -2746,6 +2975,153 @@ inline std::string toString( Detail::Approx const& value ) { } // end namespace Catch +// #included from: internal/catch_matchers_string.h +#define TWOBLUECUBES_CATCH_MATCHERS_STRING_H_INCLUDED + +namespace Catch { +namespace Matchers { + + namespace StdString { + + struct CasedString + { + CasedString( std::string const& str, CaseSensitive::Choice caseSensitivity ); + std::string adjustString( std::string const& str ) const; + std::string caseSensitivitySuffix() const; + + CaseSensitive::Choice m_caseSensitivity; + std::string m_str; + }; + + struct StringMatcherBase : MatcherBase { + StringMatcherBase( std::string const& operation, CasedString const& comparator ); + virtual std::string describe() const CATCH_OVERRIDE; + + CasedString m_comparator; + std::string m_operation; + }; + + struct EqualsMatcher : StringMatcherBase { + EqualsMatcher( CasedString const& comparator ); + virtual bool match( std::string const& source ) const CATCH_OVERRIDE; + }; + struct ContainsMatcher : StringMatcherBase { + ContainsMatcher( CasedString const& comparator ); + virtual bool match( std::string const& source ) const CATCH_OVERRIDE; + }; + struct StartsWithMatcher : StringMatcherBase { + StartsWithMatcher( CasedString const& comparator ); + virtual bool match( std::string const& source ) const CATCH_OVERRIDE; + }; + struct EndsWithMatcher : StringMatcherBase { + EndsWithMatcher( CasedString const& comparator ); + virtual bool match( std::string const& source ) const CATCH_OVERRIDE; + }; + + } // namespace StdString + + // The following functions create the actual matcher objects. + // This allows the types to be inferred + + StdString::EqualsMatcher Equals( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ); + StdString::ContainsMatcher Contains( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ); + StdString::EndsWithMatcher EndsWith( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ); + StdString::StartsWithMatcher StartsWith( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ); + +} // namespace Matchers +} // namespace Catch + +// #included from: internal/catch_matchers_vector.h +#define TWOBLUECUBES_CATCH_MATCHERS_VECTOR_H_INCLUDED + +namespace Catch { +namespace Matchers { + + namespace Vector { + + template + struct ContainsElementMatcher : MatcherBase, T> { + + ContainsElementMatcher(T const &comparator) : m_comparator( comparator) {} + + bool match(std::vector const &v) const CATCH_OVERRIDE { + return std::find(v.begin(), v.end(), m_comparator) != v.end(); + } + + virtual std::string describe() const CATCH_OVERRIDE { + return "Contains: " + Catch::toString( m_comparator ); + } + + T const& m_comparator; + }; + + template + struct ContainsMatcher : MatcherBase, std::vector > { + + ContainsMatcher(std::vector const &comparator) : m_comparator( comparator ) {} + + bool match(std::vector const &v) const CATCH_OVERRIDE { + // !TBD: see note in EqualsMatcher + if (m_comparator.size() > v.size()) + return false; + for (size_t i = 0; i < m_comparator.size(); ++i) + if (std::find(v.begin(), v.end(), m_comparator[i]) == v.end()) + return false; + return true; + } + virtual std::string describe() const CATCH_OVERRIDE { + return "Contains: " + Catch::toString( m_comparator ); + } + + std::vector const& m_comparator; + }; + + template + struct EqualsMatcher : MatcherBase, std::vector > { + + EqualsMatcher(std::vector const &comparator) : m_comparator( comparator ) {} + + bool match(std::vector const &v) const CATCH_OVERRIDE { + // !TBD: This currently works if all elements can be compared using != + // - a more general approach would be via a compare template that defaults + // to using !=. but could be specialised for, e.g. std::vector etc + // - then just call that directly + if (m_comparator.size() != v.size()) + return false; + for (size_t i = 0; i < v.size(); ++i) + if (m_comparator[i] != v[i]) + return false; + return true; + } + virtual std::string describe() const CATCH_OVERRIDE { + return "Equals: " + Catch::toString( m_comparator ); + } + std::vector const& m_comparator; + }; + + } // namespace Vector + + // The following functions create the actual matcher objects. + // This allows the types to be inferred + + template + Vector::ContainsMatcher Contains( std::vector const& comparator ) { + return Vector::ContainsMatcher( comparator ); + } + + template + Vector::ContainsElementMatcher VectorContains( T const& comparator ) { + return Vector::ContainsElementMatcher( comparator ); + } + + template + Vector::EqualsMatcher Equals( std::vector const& comparator ) { + return Vector::EqualsMatcher( comparator ); + } + +} // namespace Matchers +} // namespace Catch + // #included from: internal/catch_interfaces_tag_alias_registry.h #define TWOBLUECUBES_CATCH_INTERFACES_TAG_ALIAS_REGISTRY_H_INCLUDED @@ -2757,7 +3133,7 @@ inline std::string toString( Detail::Approx const& value ) { namespace Catch { struct TagAlias { - TagAlias( std::string _tag, SourceLineInfo _lineInfo ) : tag( _tag ), lineInfo( _lineInfo ) {} + TagAlias( std::string const& _tag, SourceLineInfo _lineInfo ) : tag( _tag ), lineInfo( _lineInfo ) {} std::string tag; SourceLineInfo lineInfo; @@ -2829,8 +3205,18 @@ namespace Catch { } private: - T* nullableValue; - char storage[sizeof(T)]; + T *nullableValue; + union { + char storage[sizeof(T)]; + + // These are here to force alignment for the storage + long double dummy1; + void (*dummy2)(); + long double dummy3; +#ifdef CATCH_CONFIG_CPP11_LONG_LONG + long long dummy4; +#endif + }; }; } // end namespace Catch @@ -2870,7 +3256,8 @@ namespace Catch { IsHidden = 1 << 1, ShouldFail = 1 << 2, MayFail = 1 << 3, - Throws = 1 << 4 + Throws = 1 << 4, + NonPortable = 1 << 5 }; TestCaseInfo( std::string const& _name, @@ -3028,64 +3415,67 @@ namespace Catch { namespace Impl { namespace NSStringMatchers { - template - struct StringHolder : MatcherImpl{ + struct StringHolder : MatcherBase{ StringHolder( NSString* substr ) : m_substr( [substr copy] ){} StringHolder( StringHolder const& other ) : m_substr( [other.m_substr copy] ){} StringHolder() { arcSafeRelease( m_substr ); } + virtual bool match( NSString* arg ) const CATCH_OVERRIDE { + return false; + } + NSString* m_substr; }; - struct Equals : StringHolder { + struct Equals : StringHolder { Equals( NSString* substr ) : StringHolder( substr ){} - virtual bool match( ExpressionType const& str ) const { + virtual bool match( NSString* str ) const CATCH_OVERRIDE { return (str != nil || m_substr == nil ) && [str isEqualToString:m_substr]; } - virtual std::string toString() const { + virtual std::string describe() const CATCH_OVERRIDE { return "equals string: " + Catch::toString( m_substr ); } }; - struct Contains : StringHolder { + struct Contains : StringHolder { Contains( NSString* substr ) : StringHolder( substr ){} - virtual bool match( ExpressionType const& str ) const { + virtual bool match( NSString* str ) const { return (str != nil || m_substr == nil ) && [str rangeOfString:m_substr].location != NSNotFound; } - virtual std::string toString() const { + virtual std::string describe() const CATCH_OVERRIDE { return "contains string: " + Catch::toString( m_substr ); } }; - struct StartsWith : StringHolder { + struct StartsWith : StringHolder { StartsWith( NSString* substr ) : StringHolder( substr ){} - virtual bool match( ExpressionType const& str ) const { + virtual bool match( NSString* str ) const { return (str != nil || m_substr == nil ) && [str rangeOfString:m_substr].location == 0; } - virtual std::string toString() const { + virtual std::string describe() const CATCH_OVERRIDE { return "starts with: " + Catch::toString( m_substr ); } }; - struct EndsWith : StringHolder { + struct EndsWith : StringHolder { EndsWith( NSString* substr ) : StringHolder( substr ){} - virtual bool match( ExpressionType const& str ) const { + virtual bool match( NSString* str ) const { return (str != nil || m_substr == nil ) && [str rangeOfString:m_substr].location == [str length] - [m_substr length]; } - virtual std::string toString() const { + virtual std::string describe() const CATCH_OVERRIDE { return "ends with: " + Catch::toString( m_substr ); } }; @@ -3126,6 +3516,29 @@ return @ desc; \ #endif #ifdef CATCH_IMPL + +// !TBD: Move the leak detector code into a separate header +#ifdef CATCH_CONFIG_WINDOWS_CRTDBG +#include +class LeakDetector { +public: + LeakDetector() { + int flag = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG); + flag |= _CRTDBG_LEAK_CHECK_DF; + flag |= _CRTDBG_ALLOC_MEM_DF; + _CrtSetDbgFlag(flag); + _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG); + _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR); + // Change this to leaking allocation's number to break there + _CrtSetBreakAlloc(-1); + } +}; +#else +class LeakDetector {}; +#endif + +LeakDetector leakDetector; + // #included from: internal/catch_impl.hpp #define TWOBLUECUBES_CATCH_IMPL_HPP_INCLUDED @@ -3165,6 +3578,8 @@ return @ desc; \ // #included from: catch_wildcard_pattern.hpp #define TWOBLUECUBES_CATCH_WILDCARD_PATTERN_HPP_INCLUDED +#include + namespace Catch { class WildcardPattern { @@ -3182,11 +3597,11 @@ namespace Catch m_wildcard( NoWildcard ), m_pattern( adjustCase( pattern ) ) { - if( startsWith( m_pattern, "*" ) ) { + if( startsWith( m_pattern, '*' ) ) { m_pattern = m_pattern.substr( 1 ); m_wildcard = WildcardAtStart; } - if( endsWith( m_pattern, "*" ) ) { + if( endsWith( m_pattern, '*' ) ) { m_pattern = m_pattern.substr( 0, m_pattern.size()-1 ); m_wildcard = static_cast( m_wildcard | WildcardAtEnd ); } @@ -3316,7 +3731,7 @@ namespace Catch { ITagAliasRegistry const* m_tagAliases; public: - TestSpecParser( ITagAliasRegistry const& tagAliases ) : m_tagAliases( &tagAliases ) {} + TestSpecParser( ITagAliasRegistry const& tagAliases ) :m_mode(None), m_exclusion(false), m_start(0), m_pos(0), m_tagAliases( &tagAliases ) {} TestSpecParser& parse( std::string const& arg ) { m_mode = None; @@ -3373,6 +3788,8 @@ namespace Catch { m_start = start; } void escape() { + if( m_mode == None ) + m_start = m_pos; m_mode = EscapedName; m_escapeChars.push_back( m_pos ); } @@ -3381,7 +3798,7 @@ namespace Catch { void addPattern() { std::string token = subString(); for( size_t i = 0; i < m_escapeChars.size(); ++i ) - token = token.substr( 0, m_escapeChars[i] ) + token.substr( m_escapeChars[i]+1 ); + token = token.substr( 0, m_escapeChars[i]-m_start-i ) + token.substr( m_escapeChars[i]-m_start-i+1 ); m_escapeChars.clear(); if( startsWith( token, "exclude:" ) ) { m_exclusion = true; @@ -3416,7 +3833,7 @@ namespace Catch { // #included from: catch_interfaces_config.h #define TWOBLUECUBES_CATCH_INTERFACES_CONFIG_H_INCLUDED -#include +#include #include #include @@ -3448,6 +3865,12 @@ namespace Catch { Yes, No }; }; + struct WaitForKeypress { enum When { + Never, + BeforeStart = 1, + BeforeExit = 2, + BeforeStartAndExit = BeforeStart | BeforeExit + }; }; class TestSpec; @@ -3468,6 +3891,8 @@ namespace Catch { virtual RunTests::InWhatOrder runOrder() const = 0; virtual unsigned int rngSeed() const = 0; virtual UseColour::YesOrNo useColour() const = 0; + virtual std::vector const& getSectionsToRun() const = 0; + }; } @@ -3496,6 +3921,7 @@ namespace Catch { std::ostream& cout(); std::ostream& cerr(); + std::ostream& clog(); struct IStream { virtual ~IStream() CATCH_NOEXCEPT; @@ -3536,8 +3962,7 @@ namespace Catch { #include #include #include -#include -#include +#include #ifndef CATCH_CONFIG_CONSOLE_WIDTH #define CATCH_CONFIG_CONSOLE_WIDTH 80 @@ -3552,25 +3977,29 @@ namespace Catch { listTags( false ), listReporters( false ), listTestNamesOnly( false ), + listExtraInfo( false ), showSuccessfulTests( false ), shouldDebugBreak( false ), noThrow( false ), showHelp( false ), showInvisibles( false ), filenamesAsTags( false ), + libIdentify( false ), abortAfter( -1 ), rngSeed( 0 ), verbosity( Verbosity::Normal ), warnings( WarnAbout::Nothing ), showDurations( ShowDurations::DefaultForReporter ), runOrder( RunTests::InDeclarationOrder ), - useColour( UseColour::Auto ) + useColour( UseColour::Auto ), + waitForKeypress( WaitForKeypress::Never ) {} bool listTests; bool listTags; bool listReporters; bool listTestNamesOnly; + bool listExtraInfo; bool showSuccessfulTests; bool shouldDebugBreak; @@ -3578,6 +4007,7 @@ namespace Catch { bool showHelp; bool showInvisibles; bool filenamesAsTags; + bool libIdentify; int abortAfter; unsigned int rngSeed; @@ -3587,6 +4017,7 @@ namespace Catch { ShowDurations::OrNot showDurations; RunTests::InWhatOrder runOrder; UseColour::YesOrNo useColour; + WaitForKeypress::When waitForKeypress; std::string outputFilename; std::string name; @@ -3594,6 +4025,7 @@ namespace Catch { std::vector reporterNames; std::vector testsOrTags; + std::vector sectionsToRun; }; class Config : public SharedImpl { @@ -3618,8 +4050,7 @@ namespace Catch { } } - virtual ~Config() { - } + virtual ~Config() {} std::string const& getFilename() const { return m_data.outputFilename ; @@ -3629,30 +4060,30 @@ namespace Catch { bool listTestNamesOnly() const { return m_data.listTestNamesOnly; } bool listTags() const { return m_data.listTags; } bool listReporters() const { return m_data.listReporters; } + bool listExtraInfo() const { return m_data.listExtraInfo; } std::string getProcessName() const { return m_data.processName; } - bool shouldDebugBreak() const { return m_data.shouldDebugBreak; } - - std::vector getReporterNames() const { return m_data.reporterNames; } + std::vector const& getReporterNames() const { return m_data.reporterNames; } + std::vector const& getSectionsToRun() const CATCH_OVERRIDE { return m_data.sectionsToRun; } - int abortAfter() const { return m_data.abortAfter; } - - TestSpec const& testSpec() const { return m_testSpec; } + virtual TestSpec const& testSpec() const CATCH_OVERRIDE { return m_testSpec; } bool showHelp() const { return m_data.showHelp; } - bool showInvisibles() const { return m_data.showInvisibles; } // IConfig interface - virtual bool allowThrows() const { return !m_data.noThrow; } - virtual std::ostream& stream() const { return m_stream->stream(); } - virtual std::string name() const { return m_data.name.empty() ? m_data.processName : m_data.name; } - virtual bool includeSuccessfulResults() const { return m_data.showSuccessfulTests; } - virtual bool warnAboutMissingAssertions() const { return m_data.warnings & WarnAbout::NoAssertions; } - virtual ShowDurations::OrNot showDurations() const { return m_data.showDurations; } - virtual RunTests::InWhatOrder runOrder() const { return m_data.runOrder; } - virtual unsigned int rngSeed() const { return m_data.rngSeed; } - virtual UseColour::YesOrNo useColour() const { return m_data.useColour; } + virtual bool allowThrows() const CATCH_OVERRIDE { return !m_data.noThrow; } + virtual std::ostream& stream() const CATCH_OVERRIDE { return m_stream->stream(); } + virtual std::string name() const CATCH_OVERRIDE { return m_data.name.empty() ? m_data.processName : m_data.name; } + virtual bool includeSuccessfulResults() const CATCH_OVERRIDE { return m_data.showSuccessfulTests; } + virtual bool warnAboutMissingAssertions() const CATCH_OVERRIDE { return m_data.warnings & WarnAbout::NoAssertions; } + virtual ShowDurations::OrNot showDurations() const CATCH_OVERRIDE { return m_data.showDurations; } + virtual RunTests::InWhatOrder runOrder() const CATCH_OVERRIDE { return m_data.runOrder; } + virtual unsigned int rngSeed() const CATCH_OVERRIDE { return m_data.rngSeed; } + virtual UseColour::YesOrNo useColour() const CATCH_OVERRIDE { return m_data.useColour; } + virtual bool shouldDebugBreak() const CATCH_OVERRIDE { return m_data.shouldDebugBreak; } + virtual int abortAfter() const CATCH_OVERRIDE { return m_data.abortAfter; } + virtual bool showInvisibles() const CATCH_OVERRIDE { return m_data.showInvisibles; } private: @@ -3717,6 +4148,7 @@ namespace Catch { #include #include #include +#include // Use optional outer namespace #ifdef STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE @@ -3826,7 +4258,7 @@ namespace Tbc { return oss.str(); } - inline friend std::ostream& operator << ( std::ostream& _stream, Text const& _text ) { + friend std::ostream& operator << ( std::ostream& _stream, Text const& _text ) { for( Text::const_iterator it = _text.begin(), itEnd = _text.end(); it != itEnd; ++it ) { if( it != _text.begin() ) @@ -4059,7 +4491,7 @@ namespace Clara { _dest = _source; } char toLowerCh(char c) { - return static_cast( ::tolower( c ) ); + return static_cast( std::tolower( c ) ); } inline void convertInto( std::string const& _source, bool& _dest ) { std::string sourceLC = _source; @@ -4213,12 +4645,13 @@ namespace Clara { } void parseIntoTokens( std::string const& arg, std::vector& tokens ) { - for( std::size_t i = 0; i <= arg.size(); ++i ) { + for( std::size_t i = 0; i < arg.size(); ++i ) { char c = arg[i]; if( c == '"' ) inQuotes = !inQuotes; mode = handleMode( i, c, arg, tokens ); } + mode = handleMode( arg.size(), '\0', arg, tokens ); } Mode handleMode( std::size_t i, char c, std::string const& arg, std::vector& tokens ) { switch( mode ) { @@ -4251,6 +4684,7 @@ namespace Clara { default: from = i; return ShortOpt; } } + Mode handleOpt( std::size_t i, char c, std::string const& arg, std::vector& tokens ) { if( std::string( ":=\0", 3 ).find( c ) == std::string::npos ) return mode; @@ -4582,7 +5016,7 @@ namespace Clara { } std::vector parseInto( std::vector const& args, ConfigT& config ) const { - std::string processName = args[0]; + std::string processName = args.empty() ? std::string() : args[0]; std::size_t lastSlash = processName.find_last_of( "/\\" ); if( lastSlash != std::string::npos ) processName = processName.substr( lastSlash+1 ); @@ -4714,6 +5148,7 @@ STITCH_CLARA_CLOSE_NAMESPACE #endif #include +#include namespace Catch { @@ -4724,13 +5159,14 @@ namespace Catch { config.abortAfter = x; } inline void addTestOrTags( ConfigData& config, std::string const& _testSpec ) { config.testsOrTags.push_back( _testSpec ); } + inline void addSectionToRun( ConfigData& config, std::string const& sectionName ) { config.sectionsToRun.push_back( sectionName ); } inline void addReporterName( ConfigData& config, std::string const& _reporterName ) { config.reporterNames.push_back( _reporterName ); } inline void addWarning( ConfigData& config, std::string const& _warning ) { if( _warning == "NoAssertions" ) config.warnings = static_cast( config.warnings | WarnAbout::NoAssertions ); else - throw std::runtime_error( "Unrecognised warning: '" + _warning + "'" ); + throw std::runtime_error( "Unrecognised warning: '" + _warning + '\'' ); } inline void setOrder( ConfigData& config, std::string const& order ) { if( startsWith( "declared", order ) ) @@ -4740,7 +5176,7 @@ namespace Catch { else if( startsWith( "random", order ) ) config.runOrder = RunTests::InRandomOrder; else - throw std::runtime_error( "Unrecognised ordering: '" + order + "'" ); + throw std::runtime_error( "Unrecognised ordering: '" + order + '\'' ); } inline void setRngSeed( ConfigData& config, std::string const& seed ) { if( seed == "time" ) { @@ -4751,7 +5187,7 @@ namespace Catch { ss << seed; ss >> config.rngSeed; if( ss.fail() ) - throw std::runtime_error( "Argment to --rng-seed should be the word 'time' or a number" ); + throw std::runtime_error( "Argument to --rng-seed should be the word 'time' or a number" ); } } inline void setVerbosity( ConfigData& config, int level ) { @@ -4775,6 +5211,18 @@ namespace Catch { else throw std::runtime_error( "colour mode must be one of: auto, yes or no" ); } + inline void setWaitForKeypress( ConfigData& config, std::string const& keypress ) { + std::string keypressLc = toLower( keypress ); + if( keypressLc == "start" ) + config.waitForKeypress = WaitForKeypress::BeforeStart; + else if( keypressLc == "exit" ) + config.waitForKeypress = WaitForKeypress::BeforeExit; + else if( keypressLc == "both" ) + config.waitForKeypress = WaitForKeypress::BeforeStartAndExit; + else + throw std::runtime_error( "keypress argument must be one of: start, exit or both. '" + keypress + "' not recognised" ); + }; + inline void forceColour( ConfigData& config ) { config.useColour = UseColour::Yes; } @@ -4786,10 +5234,10 @@ namespace Catch { std::string line; while( std::getline( f, line ) ) { line = trim(line); - if( !line.empty() && !startsWith( line, "#" ) ) { - if( !startsWith( line, "\"" ) ) - line = "\"" + line + "\""; - addTestOrTags( config, line + "," ); + if( !line.empty() && !startsWith( line, '#' ) ) { + if( !startsWith( line, '"' ) ) + line = '"' + line + '"'; + addTestOrTags( config, line + ',' ); } } } @@ -4877,11 +5325,19 @@ namespace Catch { .describe( "adds a tag for the filename" ) .bind( &ConfigData::filenamesAsTags ); + cli["-c"]["--section"] + .describe( "specify section to run" ) + .bind( &addSectionToRun, "section name" ); + // Less common commands which don't have a short form cli["--list-test-names-only"] .describe( "list all/matching test cases names only" ) .bind( &ConfigData::listTestNamesOnly ); + cli["--list-extra-info"] + .describe( "list all/matching test cases with more info" ) + .bind( &ConfigData::listExtraInfo ); + cli["--list-reporters"] .describe( "list all reporters" ) .bind( &ConfigData::listReporters ); @@ -4902,6 +5358,14 @@ namespace Catch { .describe( "should output be colourised" ) .bind( &setUseColour, "yes|no" ); + cli["--libidentify"] + .describe( "report name and version according to libidentify standard" ) + .bind( &ConfigData::libIdentify ); + + cli["--wait-for-keypress"] + .describe( "waits for a keypress before exiting" ) + .bind( &setWaitForKeypress, "start|exit|both" ); + return cli; } @@ -4949,19 +5413,16 @@ namespace Tbc { TextAttributes() : initialIndent( std::string::npos ), indent( 0 ), - width( consoleWidth-1 ), - tabChar( '\t' ) + width( consoleWidth-1 ) {} TextAttributes& setInitialIndent( std::size_t _value ) { initialIndent = _value; return *this; } TextAttributes& setIndent( std::size_t _value ) { indent = _value; return *this; } TextAttributes& setWidth( std::size_t _value ) { width = _value; return *this; } - TextAttributes& setTabChar( char _value ) { tabChar = _value; return *this; } std::size_t initialIndent; // indent of first line, or npos std::size_t indent; // indent of subsequent lines, or all if initialIndent is npos std::size_t width; // maximum width of text, including indent. Longer text will wrap - char tabChar; // If this char is seen the indent is changed to current pos }; class Text { @@ -4969,62 +5430,76 @@ namespace Tbc { Text( std::string const& _str, TextAttributes const& _attr = TextAttributes() ) : attr( _attr ) { - std::string wrappableChars = " [({.,/|\\-"; - std::size_t indent = _attr.initialIndent != std::string::npos - ? _attr.initialIndent - : _attr.indent; - std::string remainder = _str; + const std::string wrappableBeforeChars = "[({<\t"; + const std::string wrappableAfterChars = "])}>-,./|\\"; + const std::string wrappableInsteadOfChars = " \n\r"; + std::string indent = _attr.initialIndent != std::string::npos + ? std::string( _attr.initialIndent, ' ' ) + : std::string( _attr.indent, ' ' ); + + typedef std::string::const_iterator iterator; + iterator it = _str.begin(); + const iterator strEnd = _str.end(); + + while( it != strEnd ) { - while( !remainder.empty() ) { if( lines.size() >= 1000 ) { lines.push_back( "... message truncated due to excessive size" ); return; } - std::size_t tabPos = std::string::npos; - std::size_t width = (std::min)( remainder.size(), _attr.width - indent ); - std::size_t pos = remainder.find_first_of( '\n' ); - if( pos <= width ) { - width = pos; - } - pos = remainder.find_last_of( _attr.tabChar, width ); - if( pos != std::string::npos ) { - tabPos = pos; - if( remainder[width] == '\n' ) - width--; - remainder = remainder.substr( 0, tabPos ) + remainder.substr( tabPos+1 ); - } - if( width == remainder.size() ) { - spliceLine( indent, remainder, width ); - } - else if( remainder[width] == '\n' ) { - spliceLine( indent, remainder, width ); - if( width <= 1 || remainder.size() != 1 ) - remainder = remainder.substr( 1 ); - indent = _attr.indent; - } - else { - pos = remainder.find_last_of( wrappableChars, width ); - if( pos != std::string::npos && pos > 0 ) { - spliceLine( indent, remainder, pos ); - if( remainder[0] == ' ' ) - remainder = remainder.substr( 1 ); + std::string suffix; + std::size_t width = (std::min)( static_cast( strEnd-it ), _attr.width-static_cast( indent.size() ) ); + iterator itEnd = it+width; + iterator itNext = _str.end(); + + iterator itNewLine = std::find( it, itEnd, '\n' ); + if( itNewLine != itEnd ) + itEnd = itNewLine; + + if( itEnd != strEnd ) { + bool foundWrapPoint = false; + iterator findIt = itEnd; + do { + if( wrappableAfterChars.find( *findIt ) != std::string::npos && findIt != itEnd ) { + itEnd = findIt+1; + itNext = findIt+1; + foundWrapPoint = true; + } + else if( findIt > it && wrappableBeforeChars.find( *findIt ) != std::string::npos ) { + itEnd = findIt; + itNext = findIt; + foundWrapPoint = true; + } + else if( wrappableInsteadOfChars.find( *findIt ) != std::string::npos ) { + itNext = findIt+1; + itEnd = findIt; + foundWrapPoint = true; + } + if( findIt == it ) + break; + else + --findIt; + } + while( !foundWrapPoint ); + + if( !foundWrapPoint ) { + // No good wrap char, so we'll break mid word and add a hyphen + --itEnd; + itNext = itEnd; + suffix = "-"; } else { - spliceLine( indent, remainder, width-1 ); - lines.back() += "-"; + while( itEnd > it && wrappableInsteadOfChars.find( *(itEnd-1) ) != std::string::npos ) + --itEnd; } - if( lines.size() == 1 ) - indent = _attr.indent; - if( tabPos != std::string::npos ) - indent += tabPos; } - } - } + lines.push_back( indent + std::string( it, itEnd ) + suffix ); - void spliceLine( std::size_t _indent, std::string& _remainder, std::size_t _pos ) { - lines.push_back( std::string( _indent, ' ' ) + _remainder.substr( 0, _pos ) ); - _remainder = _remainder.substr( _pos ); + if( indent.size() != _attr.indent ) + indent = std::string( _attr.indent, ' ' ); + it = itNext; + } } typedef std::vector::const_iterator const_iterator; @@ -5133,7 +5608,6 @@ namespace Catch { #include #include #include -#include namespace Catch { @@ -5400,8 +5874,9 @@ namespace Catch { } std::size_t matchedTests = 0; - TextAttributes nameAttr, tagsAttr; + TextAttributes nameAttr, descAttr, tagsAttr; nameAttr.setInitialIndent( 2 ).setIndent( 4 ); + descAttr.setIndent( 4 ); tagsAttr.setIndent( 6 ); std::vector matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config ); @@ -5416,14 +5891,21 @@ namespace Catch { Colour colourGuard( colour ); Catch::cout() << Text( testCaseInfo.name, nameAttr ) << std::endl; + if( config.listExtraInfo() ) { + Catch::cout() << " " << testCaseInfo.lineInfo << std::endl; + std::string description = testCaseInfo.description; + if( description.empty() ) + description = "(NO DESCRIPTION)"; + Catch::cout() << Text( description, descAttr ) << std::endl; + } if( !testCaseInfo.tags.empty() ) Catch::cout() << Text( testCaseInfo.tagsAsString, tagsAttr ) << std::endl; } if( !config.testSpec().hasFilters() ) - Catch::cout() << pluralise( matchedTests, "test case" ) << "\n" << std::endl; + Catch::cout() << pluralise( matchedTests, "test case" ) << '\n' << std::endl; else - Catch::cout() << pluralise( matchedTests, "matching test case" ) << "\n" << std::endl; + Catch::cout() << pluralise( matchedTests, "matching test case" ) << '\n' << std::endl; return matchedTests; } @@ -5438,10 +5920,13 @@ namespace Catch { ++it ) { matchedTests++; TestCaseInfo const& testCaseInfo = it->getTestCaseInfo(); - if( startsWith( testCaseInfo.name, "#" ) ) - Catch::cout() << "\"" << testCaseInfo.name << "\"" << std::endl; + if( startsWith( testCaseInfo.name, '#' ) ) + Catch::cout() << '"' << testCaseInfo.name << '"'; else - Catch::cout() << testCaseInfo.name << std::endl; + Catch::cout() << testCaseInfo.name; + if ( config.listExtraInfo() ) + Catch::cout() << "\t@" << testCaseInfo.lineInfo; + Catch::cout() << std::endl; } return matchedTests; } @@ -5502,9 +5987,9 @@ namespace Catch { .setInitialIndent( 0 ) .setIndent( oss.str().size() ) .setWidth( CATCH_CONFIG_CONSOLE_WIDTH-10 ) ); - Catch::cout() << oss.str() << wrapper << "\n"; + Catch::cout() << oss.str() << wrapper << '\n'; } - Catch::cout() << pluralise( tagCounts.size(), "tag" ) << "\n" << std::endl; + Catch::cout() << pluralise( tagCounts.size(), "tag" ) << '\n' << std::endl; return tagCounts.size(); } @@ -5523,9 +6008,9 @@ namespace Catch { .setWidth( CATCH_CONFIG_CONSOLE_WIDTH - maxNameLen-8 ) ); Catch::cout() << " " << it->first - << ":" + << ':' << std::string( maxNameLen - it->first.size() + 2, ' ' ) - << wrapper << "\n"; + << wrapper << '\n'; } Catch::cout() << std::endl; return factories.size(); @@ -5533,7 +6018,7 @@ namespace Catch { inline Option list( Config const& config ) { Option listedCount; - if( config.listTests() ) + if( config.listTests() || ( config.listExtraInfo() && !config.listTestNamesOnly() ) ) listedCount = listedCount.valueOr(0) + listTests( config ); if( config.listTestNamesOnly() ) listedCount = listedCount.valueOr(0) + listTestsNamesOnly( config ); @@ -5552,19 +6037,32 @@ namespace Catch { // #included from: catch_test_case_tracker.hpp #define TWOBLUECUBES_CATCH_TEST_CASE_TRACKER_HPP_INCLUDED -#include +#include #include #include #include +#include + +CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS namespace Catch { namespace TestCaseTracking { + struct NameAndLocation { + std::string name; + SourceLineInfo location; + + NameAndLocation( std::string const& _name, SourceLineInfo const& _location ) + : name( _name ), + location( _location ) + {} + }; + struct ITracker : SharedImpl<> { virtual ~ITracker(); // static queries - virtual std::string name() const = 0; + virtual NameAndLocation const& nameAndLocation() const = 0; // dynamic queries virtual bool isComplete() const = 0; // Successfully completed or failed @@ -5580,7 +6078,7 @@ namespace TestCaseTracking { virtual void markAsNeedingAnotherRun() = 0; virtual void addChild( Ptr const& child ) = 0; - virtual ITracker* findChild( std::string const& name ) = 0; + virtual ITracker* findChild( NameAndLocation const& nameAndLocation ) = 0; virtual void openChild() = 0; // Debug/ checking @@ -5588,7 +6086,7 @@ namespace TestCaseTracking { virtual bool isIndexTracker() const = 0; }; - class TrackerContext { + class TrackerContext { enum RunState { NotStarted, @@ -5650,30 +6148,32 @@ namespace TestCaseTracking { Failed }; class TrackerHasName { - std::string m_name; + NameAndLocation m_nameAndLocation; public: - TrackerHasName( std::string const& name ) : m_name( name ) {} + TrackerHasName( NameAndLocation const& nameAndLocation ) : m_nameAndLocation( nameAndLocation ) {} bool operator ()( Ptr const& tracker ) { - return tracker->name() == m_name; + return + tracker->nameAndLocation().name == m_nameAndLocation.name && + tracker->nameAndLocation().location == m_nameAndLocation.location; } }; typedef std::vector > Children; - std::string m_name; + NameAndLocation m_nameAndLocation; TrackerContext& m_ctx; ITracker* m_parent; Children m_children; CycleState m_runState; public: - TrackerBase( std::string const& name, TrackerContext& ctx, ITracker* parent ) - : m_name( name ), + TrackerBase( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ) + : m_nameAndLocation( nameAndLocation ), m_ctx( ctx ), m_parent( parent ), m_runState( NotStarted ) {} virtual ~TrackerBase(); - virtual std::string name() const CATCH_OVERRIDE { - return m_name; + virtual NameAndLocation const& nameAndLocation() const CATCH_OVERRIDE { + return m_nameAndLocation; } virtual bool isComplete() const CATCH_OVERRIDE { return m_runState == CompletedSuccessfully || m_runState == Failed; @@ -5692,8 +6192,8 @@ namespace TestCaseTracking { m_children.push_back( child ); } - virtual ITracker* findChild( std::string const& name ) CATCH_OVERRIDE { - Children::const_iterator it = std::find_if( m_children.begin(), m_children.end(), TrackerHasName( name ) ); + virtual ITracker* findChild( NameAndLocation const& nameAndLocation ) CATCH_OVERRIDE { + Children::const_iterator it = std::find_if( m_children.begin(), m_children.end(), TrackerHasName( nameAndLocation ) ); return( it != m_children.end() ) ? it->get() : CATCH_NULL; @@ -5771,32 +6271,56 @@ namespace TestCaseTracking { }; class SectionTracker : public TrackerBase { + std::vector m_filters; public: - SectionTracker( std::string const& name, TrackerContext& ctx, ITracker* parent ) - : TrackerBase( name, ctx, parent ) - {} + SectionTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ) + : TrackerBase( nameAndLocation, ctx, parent ) + { + if( parent ) { + while( !parent->isSectionTracker() ) + parent = &parent->parent(); + + SectionTracker& parentSection = static_cast( *parent ); + addNextFilters( parentSection.m_filters ); + } + } virtual ~SectionTracker(); virtual bool isSectionTracker() const CATCH_OVERRIDE { return true; } - static SectionTracker& acquire( TrackerContext& ctx, std::string const& name ) { + static SectionTracker& acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation ) { SectionTracker* section = CATCH_NULL; ITracker& currentTracker = ctx.currentTracker(); - if( ITracker* childTracker = currentTracker.findChild( name ) ) { + if( ITracker* childTracker = currentTracker.findChild( nameAndLocation ) ) { assert( childTracker ); assert( childTracker->isSectionTracker() ); section = static_cast( childTracker ); } else { - section = new SectionTracker( name, ctx, ¤tTracker ); + section = new SectionTracker( nameAndLocation, ctx, ¤tTracker ); currentTracker.addChild( section ); } - if( !ctx.completedCycle() && !section->isComplete() ) { + if( !ctx.completedCycle() ) + section->tryOpen(); + return *section; + } + + void tryOpen() { + if( !isComplete() && (m_filters.empty() || m_filters[0].empty() || m_filters[0] == m_nameAndLocation.name ) ) + open(); + } - section->open(); + void addInitialFilters( std::vector const& filters ) { + if( !filters.empty() ) { + m_filters.push_back(""); // Root - should never be consulted + m_filters.push_back(""); // Test Case - not a section filter + m_filters.insert( m_filters.end(), filters.begin(), filters.end() ); } - return *section; + } + void addNextFilters( std::vector const& filters ) { + if( filters.size() > 1 ) + m_filters.insert( m_filters.end(), ++filters.begin(), filters.end() ); } }; @@ -5804,8 +6328,8 @@ namespace TestCaseTracking { int m_size; int m_index; public: - IndexTracker( std::string const& name, TrackerContext& ctx, ITracker* parent, int size ) - : TrackerBase( name, ctx, parent ), + IndexTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent, int size ) + : TrackerBase( nameAndLocation, ctx, parent ), m_size( size ), m_index( -1 ) {} @@ -5813,17 +6337,17 @@ namespace TestCaseTracking { virtual bool isIndexTracker() const CATCH_OVERRIDE { return true; } - static IndexTracker& acquire( TrackerContext& ctx, std::string const& name, int size ) { + static IndexTracker& acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation, int size ) { IndexTracker* tracker = CATCH_NULL; ITracker& currentTracker = ctx.currentTracker(); - if( ITracker* childTracker = currentTracker.findChild( name ) ) { + if( ITracker* childTracker = currentTracker.findChild( nameAndLocation ) ) { assert( childTracker ); assert( childTracker->isIndexTracker() ); tracker = static_cast( childTracker ); } else { - tracker = new IndexTracker( name, ctx, ¤tTracker, size ); + tracker = new IndexTracker( nameAndLocation, ctx, ¤tTracker, size ); currentTracker.addChild( tracker ); } @@ -5851,7 +6375,7 @@ namespace TestCaseTracking { }; inline ITracker& TrackerContext::startRun() { - m_rootTracker = new SectionTracker( "{root}", *this, CATCH_NULL ); + m_rootTracker = new SectionTracker( NameAndLocation( "{root}", CATCH_INTERNAL_LINEINFO ), *this, CATCH_NULL ); m_currentTracker = CATCH_NULL; m_runState = Executing; return *m_rootTracker; @@ -5866,40 +6390,212 @@ using TestCaseTracking::IndexTracker; } // namespace Catch +CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS + // #included from: catch_fatal_condition.hpp #define TWOBLUECUBES_CATCH_FATAL_CONDITION_H_INCLUDED +#include +#include + namespace Catch { - // Report the error condition then exit the process - inline void fatal( std::string const& message, int exitCode ) { + //! Signals fatal error message to the run context + inline void reportFatal( std::string const& message ) { IContext& context = Catch::getCurrentContext(); IResultCapture* resultCapture = context.getResultCapture(); resultCapture->handleFatalErrorCondition( message ); - - if( Catch::alwaysTrue() ) // avoids "no return" warnings - exit( exitCode ); } } // namespace Catch #if defined ( CATCH_PLATFORM_WINDOWS ) ///////////////////////////////////////// +// #included from: catch_windows_h_proxy.h + +#define TWOBLUECUBES_CATCH_WINDOWS_H_PROXY_H_INCLUDED + +#ifdef CATCH_DEFINES_NOMINMAX +# define NOMINMAX +#endif +#ifdef CATCH_DEFINES_WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif + +#ifdef __AFXDLL +#include +#else +#include +#endif + +#ifdef CATCH_DEFINES_NOMINMAX +# undef NOMINMAX +#endif +#ifdef CATCH_DEFINES_WIN32_LEAN_AND_MEAN +# undef WIN32_LEAN_AND_MEAN +#endif + + +# if !defined ( CATCH_CONFIG_WINDOWS_SEH ) namespace Catch { + class FatalConditionHandler { + bool m_started; + + // Install/disengage implementation for specific platform. + // Should be if-defed to work on current platform, can assume + // engage-disengage 1:1 pairing. + void engage_platform() {} + void disengage_platform() {} + + public: + // Should also have platform-specific implementations as needed + FatalConditionHandler() : m_started(false) {} + ~FatalConditionHandler() {} + + void engage() { + assert(!m_started && "Handler cannot be installed twice."); + m_started = true; + engage_platform(); + } + + void disengage() { + assert(m_started && "Handler cannot be uninstalled without being installed first"); + m_started = false; + disengage_platform(); + } + }; +} - struct FatalConditionHandler { - void reset() {} - }; +# else // CATCH_CONFIG_WINDOWS_SEH is defined + +namespace Catch { + + struct SignalDefs { DWORD id; const char* name; }; + extern SignalDefs signalDefs[]; + // There is no 1-1 mapping between signals and windows exceptions. + // Windows can easily distinguish between SO and SigSegV, + // but SigInt, SigTerm, etc are handled differently. + SignalDefs signalDefs[] = { + { EXCEPTION_ILLEGAL_INSTRUCTION, "SIGILL - Illegal instruction signal" }, + { EXCEPTION_STACK_OVERFLOW, "SIGSEGV - Stack overflow" }, + { EXCEPTION_ACCESS_VIOLATION, "SIGSEGV - Segmentation violation signal" }, + { EXCEPTION_INT_DIVIDE_BY_ZERO, "Divide by zero error" }, + }; + + static LONG CALLBACK handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo) { + for (int i = 0; i < sizeof(signalDefs) / sizeof(SignalDefs); ++i) { + if (ExceptionInfo->ExceptionRecord->ExceptionCode == signalDefs[i].id) { + reportFatal(signalDefs[i].name); + } + } + // If its not an exception we care about, pass it along. + // This stops us from eating debugger breaks etc. + return EXCEPTION_CONTINUE_SEARCH; + } + + // Since we do not support multiple instantiations, we put these + // into global variables and rely on cleaning them up in outlined + // constructors/destructors + static PVOID exceptionHandlerHandle = CATCH_NULL; + + class FatalConditionHandler { + bool m_started; + + // Install/disengage implementation for specific platform. + // Should be if-defed to work on current platform, can assume + // engage-disengage 1:1 pairing. + + void engage_platform() { + // Register as first handler in current chain + exceptionHandlerHandle = AddVectoredExceptionHandler(1, handleVectoredException); + if (!exceptionHandlerHandle) { + throw std::runtime_error("Could not register vectored exception handler"); + } + } + + void disengage_platform() { + if (!RemoveVectoredExceptionHandler(exceptionHandlerHandle)) { + throw std::runtime_error("Could not unregister vectored exception handler"); + } + exceptionHandlerHandle = CATCH_NULL; + } + + public: + FatalConditionHandler() : m_started(false) { + ULONG guaranteeSize = static_cast(32 * 1024); + if (!SetThreadStackGuarantee(&guaranteeSize)) { + // We do not want to fully error out, because needing + // the stack reserve should be rare enough anyway. + Catch::cerr() + << "Failed to reserve piece of stack." + << " Stack overflows will not be reported successfully."; + } + } + + // We do not attempt to unset the stack guarantee, because + // Windows does not support lowering the stack size guarantee. + ~FatalConditionHandler() {} + + void engage() { + assert(!m_started && "Handler cannot be installed twice."); + m_started = true; + engage_platform(); + } + + void disengage() { + assert(m_started && "Handler cannot be uninstalled without being installed first"); + m_started = false; + disengage_platform(); + } + }; } // namespace Catch +# endif // CATCH_CONFIG_WINDOWS_SEH + #else // Not Windows - assumed to be POSIX compatible ////////////////////////// +# if !defined(CATCH_CONFIG_POSIX_SIGNALS) + +namespace Catch { + class FatalConditionHandler { + bool m_started; + + // Install/disengage implementation for specific platform. + // Should be if-defed to work on current platform, can assume + // engage-disengage 1:1 pairing. + void engage_platform() {} + void disengage_platform() {} + + public: + // Should also have platform-specific implementations as needed + FatalConditionHandler() : m_started(false) {} + ~FatalConditionHandler() {} + + void engage() { + assert(!m_started && "Handler cannot be installed twice."); + m_started = true; + engage_platform(); + } + + void disengage() { + assert(m_started && "Handler cannot be uninstalled without being installed first"); + m_started = false; + disengage_platform(); + } + }; +} + +# else // CATCH_CONFIG_POSIX_SIGNALS is defined + #include namespace Catch { - struct SignalDefs { int id; const char* name; }; + struct SignalDefs { + int id; + const char* name; + }; extern SignalDefs signalDefs[]; SignalDefs signalDefs[] = { { SIGINT, "SIGINT - Terminal interrupt signal" }, @@ -5908,39 +6604,133 @@ namespace Catch { { SIGSEGV, "SIGSEGV - Segmentation violation signal" }, { SIGTERM, "SIGTERM - Termination request signal" }, { SIGABRT, "SIGABRT - Abort (abnormal termination) signal" } - }; + }; + +// Older GCCs trigger -Wmissing-field-initializers for T foo = {} +// which is zero initialization, but not explicit. We want to avoid +// that. +#if defined(__GNUC__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wmissing-field-initializers" +#endif - struct FatalConditionHandler { + static char* altStackMem = CATCH_NULL; + static std::size_t altStackSize = 0; + static stack_t oldSigStack; + static struct sigaction oldSigActions[sizeof(signalDefs) / sizeof(SignalDefs)]; - static void handleSignal( int sig ) { - for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i ) - if( sig == signalDefs[i].id ) - fatal( signalDefs[i].name, -sig ); - fatal( "", -sig ); + static void restorePreviousSignalHandlers() { + // We set signal handlers back to the previous ones. Hopefully + // nobody overwrote them in the meantime, and doesn't expect + // their signal handlers to live past ours given that they + // installed them after ours.. + for (std::size_t i = 0; i < sizeof(signalDefs) / sizeof(SignalDefs); ++i) { + sigaction(signalDefs[i].id, &oldSigActions[i], CATCH_NULL); } + // Return the old stack + sigaltstack(&oldSigStack, CATCH_NULL); + } - FatalConditionHandler() : m_isSet( true ) { - for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i ) - signal( signalDefs[i].id, handleSignal ); + static void handleSignal( int sig ) { + char const * name = ""; + for (std::size_t i = 0; i < sizeof(signalDefs) / sizeof(SignalDefs); ++i) { + SignalDefs &def = signalDefs[i]; + if (sig == def.id) { + name = def.name; + break; + } } - ~FatalConditionHandler() { - reset(); + // We need to restore previous signal handlers and let them do + // their thing, so that the users can have the debugger break + // when a signal is raised, and so on. + restorePreviousSignalHandlers(); + reportFatal( name ); + raise( sig ); + } + + class FatalConditionHandler { + bool m_started; + + // Install/disengage implementation for specific platform. + // Should be if-defed to work on current platform, can assume + // engage-disengage 1:1 pairing. + + void engage_platform() { + stack_t sigStack; + sigStack.ss_sp = altStackMem; + sigStack.ss_size = SIGSTKSZ; + sigStack.ss_flags = 0; + sigaltstack(&sigStack, &oldSigStack); + struct sigaction sa = { 0 }; + + sa.sa_handler = handleSignal; + sa.sa_flags = SA_ONSTACK; + for (std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i) { + sigaction(signalDefs[i].id, &sa, &oldSigActions[i]); + } } - void reset() { - if( m_isSet ) { - for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i ) - signal( signalDefs[i].id, SIG_DFL ); - m_isSet = false; + + void disengage_platform() { + restorePreviousSignalHandlers(); + } + + public: + FatalConditionHandler() : m_started(false) { + assert(!altStackMem && "Cannot initialize POSIX signal handler when one already exists"); + if (altStackSize == 0) { + altStackSize = SIGSTKSZ; } + altStackMem = new char[altStackSize](); + } + + ~FatalConditionHandler() { + delete[] altStackMem; + // We signal that another instance can be constructed by zeroing + // out the pointer. + altStackMem = CATCH_NULL; + } + + void engage() { + assert(!m_started && "Handler cannot be installed twice."); + m_started = true; + engage_platform(); } - bool m_isSet; + void disengage() { + assert(m_started && "Handler cannot be uninstalled without being installed first"); + m_started = false; + disengage_platform(); + } }; +#if defined(__GNUC__) +# pragma GCC diagnostic pop +#endif + } // namespace Catch +# endif // CATCH_CONFIG_POSIX_SIGNALS + #endif // not Windows +namespace Catch { + + //! Simple RAII guard for (dis)engaging the FatalConditionHandler + class FatalConditionHandlerGuard { + FatalConditionHandler* m_handler; + public: + FatalConditionHandlerGuard(FatalConditionHandler* handler): + m_handler(handler) { + m_handler->engage(); + } + ~FatalConditionHandlerGuard() { + m_handler->disengage(); + } + }; + +} // end namespace Catch + +#include #include #include @@ -5969,6 +6759,29 @@ namespace Catch { std::string& m_targetString; }; + // StdErr has two constituent streams in C++, std::cerr and std::clog + // This means that we need to redirect 2 streams into 1 to keep proper + // order of writes and cannot use StreamRedirect on its own + class StdErrRedirect { + public: + StdErrRedirect(std::string& targetString) + :m_cerrBuf( cerr().rdbuf() ), m_clogBuf(clog().rdbuf()), + m_targetString(targetString){ + cerr().rdbuf(m_oss.rdbuf()); + clog().rdbuf(m_oss.rdbuf()); + } + ~StdErrRedirect() { + m_targetString += m_oss.str(); + cerr().rdbuf(m_cerrBuf); + clog().rdbuf(m_clogBuf); + } + private: + std::streambuf* m_cerrBuf; + std::streambuf* m_clogBuf; + std::ostringstream m_oss; + std::string& m_targetString; + }; + /////////////////////////////////////////////////////////////////////////// class RunContext : public IResultCapture, public IRunner { @@ -5983,7 +6796,8 @@ namespace Catch { m_context( getCurrentMutableContext() ), m_activeTestCase( CATCH_NULL ), m_config( _config ), - m_reporter( reporter ) + m_reporter( reporter ), + m_shouldReportUnexpected ( true ) { m_context.setRunner( this ); m_context.setConfig( m_config ); @@ -6015,10 +6829,12 @@ namespace Catch { m_activeTestCase = &testCase; do { - m_trackerContext.startRun(); + ITracker& rootTracker = m_trackerContext.startRun(); + assert( rootTracker.isSectionTracker() ); + static_cast( rootTracker ).addInitialFilters( m_config->getSectionsToRun() ); do { m_trackerContext.startCycle(); - m_testCaseTracker = &SectionTracker::acquire( m_trackerContext, testInfo.name ); + m_testCaseTracker = &SectionTracker::acquire( m_trackerContext, TestCaseTracking::NameAndLocation( testInfo.name, testInfo.lineInfo ) ); runCurrentTest( redirectedCout, redirectedCerr ); } while( !m_testCaseTracker->isSuccessfullyCompleted() && !aborting() ); @@ -6056,26 +6872,44 @@ namespace Catch { m_totals.assertions.passed++; } else if( !result.isOk() ) { - m_totals.assertions.failed++; + if( m_activeTestCase->getTestCaseInfo().okToFail() ) + m_totals.assertions.failedButOk++; + else + m_totals.assertions.failed++; } - if( m_reporter->assertionEnded( AssertionStats( result, m_messages, m_totals ) ) ) - m_messages.clear(); + // We have no use for the return value (whether messages should be cleared), because messages were made scoped + // and should be let to clear themselves out. + static_cast(m_reporter->assertionEnded(AssertionStats(result, m_messages, m_totals))); // Reset working state m_lastAssertionInfo = AssertionInfo( "", m_lastAssertionInfo.lineInfo, "{Unknown expression after the reported line}" , m_lastAssertionInfo.resultDisposition ); m_lastResult = result; } + virtual bool lastAssertionPassed() + { + return m_totals.assertions.passed == (m_prevPassed + 1); + } + + virtual void assertionPassed() + { + m_totals.assertions.passed++; + m_lastAssertionInfo.capturedExpression = "{Unknown expression after the reported line}"; + m_lastAssertionInfo.macroName = ""; + } + + virtual void assertionRun() + { + m_prevPassed = m_totals.assertions.passed; + } + virtual bool sectionStarted ( SectionInfo const& sectionInfo, Counts& assertions ) { - std::ostringstream oss; - oss << sectionInfo.name << "@" << sectionInfo.lineInfo; - - ITracker& sectionTracker = SectionTracker::acquire( m_trackerContext, oss.str() ); + ITracker& sectionTracker = SectionTracker::acquire( m_trackerContext, TestCaseTracking::NameAndLocation( sectionInfo.name, sectionInfo.lineInfo ) ); if( !sectionTracker.isOpen() ) return false; m_activeSections.push_back( §ionTracker ); @@ -6134,18 +6968,26 @@ namespace Catch { virtual std::string getCurrentTestName() const { return m_activeTestCase ? m_activeTestCase->getTestCaseInfo().name - : ""; + : std::string(); } virtual const AssertionResult* getLastResult() const { return &m_lastResult; } + virtual void exceptionEarlyReported() { + m_shouldReportUnexpected = false; + } + virtual void handleFatalErrorCondition( std::string const& message ) { - ResultBuilder resultBuilder = makeUnexpectedResultBuilder(); - resultBuilder.setResultType( ResultWas::FatalErrorCondition ); - resultBuilder << message; - resultBuilder.captureExpression(); + // Don't rebuild the result -- the stringification itself can cause more fatal errors + // Instead, fake a result data. + AssertionResultData tempResult; + tempResult.resultType = ResultWas::FatalErrorCondition; + tempResult.message = message; + AssertionResult result(m_lastAssertionInfo, tempResult); + + getResultCapture().assertionEnded(result); handleUnfinishedSections(); @@ -6162,13 +7004,14 @@ namespace Catch { Totals deltaTotals; deltaTotals.testCases.failed = 1; + deltaTotals.assertions.failed = 1; m_reporter->testCaseEnded( TestCaseStats( testInfo, deltaTotals, - "", - "", + std::string(), + std::string(), false ) ); m_totals.testCases.failed++; - testGroupEnded( "", m_totals, 1, 1 ); + testGroupEnded( std::string(), m_totals, 1, 1 ); m_reporter->testRunEnded( TestRunStats( m_runInfo, m_totals, false ) ); } @@ -6186,6 +7029,7 @@ namespace Catch { m_reporter->sectionStarting( testCaseSection ); Counts prevAssertions = m_totals.assertions; double duration = 0; + m_shouldReportUnexpected = true; try { m_lastAssertionInfo = AssertionInfo( "TEST_CASE", testCaseInfo.lineInfo, "", ResultDisposition::Normal ); @@ -6195,7 +7039,7 @@ namespace Catch { timer.start(); if( m_reporter->getPreferences().shouldRedirectStdOut ) { StreamRedirect coutRedir( Catch::cout(), redirectedCout ); - StreamRedirect cerrRedir( Catch::cerr(), redirectedCerr ); + StdErrRedirect errRedir( redirectedCerr ); invokeActiveTestCase(); } else { @@ -6207,7 +7051,11 @@ namespace Catch { // This just means the test was aborted due to failure } catch(...) { - makeUnexpectedResultBuilder().useActiveException(); + // Under CATCH_CONFIG_FAST_COMPILE, unexpected exceptions under REQUIRE assertions + // are reported without translation at the point of origin. + if (m_shouldReportUnexpected) { + makeUnexpectedResultBuilder().useActiveException(); + } } m_testCaseTracker->close(); handleUnfinishedSections(); @@ -6216,28 +7064,21 @@ namespace Catch { Counts assertions = m_totals.assertions - prevAssertions; bool missingAssertions = testForMissingAssertions( assertions ); - if( testCaseInfo.okToFail() ) { - std::swap( assertions.failedButOk, assertions.failed ); - m_totals.assertions.failed -= assertions.failedButOk; - m_totals.assertions.failedButOk += assertions.failedButOk; - } - SectionStats testCaseSectionStats( testCaseSection, assertions, duration, missingAssertions ); m_reporter->sectionEnded( testCaseSectionStats ); } void invokeActiveTestCase() { - FatalConditionHandler fatalConditionHandler; // Handle signals + FatalConditionHandlerGuard _(&m_fatalConditionhandler); m_activeTestCase->invoke(); - fatalConditionHandler.reset(); } private: ResultBuilder makeUnexpectedResultBuilder() const { - return ResultBuilder( m_lastAssertionInfo.macroName.c_str(), + return ResultBuilder( m_lastAssertionInfo.macroName, m_lastAssertionInfo.lineInfo, - m_lastAssertionInfo.capturedExpression.c_str(), + m_lastAssertionInfo.capturedExpression, m_lastAssertionInfo.resultDisposition ); } @@ -6267,6 +7108,9 @@ namespace Catch { std::vector m_unfinishedSections; std::vector m_activeSections; TrackerContext m_trackerContext; + FatalConditionHandler m_fatalConditionhandler; + size_t m_prevPassed; + bool m_shouldReportUnexpected; }; IResultCapture& getResultCapture() { @@ -6288,7 +7132,7 @@ namespace Catch { Version( unsigned int _majorVersion, unsigned int _minorVersion, unsigned int _patchNumber, - std::string const& _branchName, + char const * const _branchName, unsigned int _buildNumber ); unsigned int const majorVersion; @@ -6296,7 +7140,7 @@ namespace Catch { unsigned int const patchNumber; // buildNumber is only used if branchName is not null - std::string const branchName; + char const * const branchName; unsigned int const buildNumber; friend std::ostream& operator << ( std::ostream& os, Version const& version ); @@ -6305,7 +7149,7 @@ namespace Catch { void operator=( Version const& ); }; - extern Version libraryVersion; + inline Version libraryVersion(); } #include @@ -6324,10 +7168,14 @@ namespace Catch { return reporter; } +#if !defined(CATCH_CONFIG_DEFAULT_REPORTER) +#define CATCH_CONFIG_DEFAULT_REPORTER "console" +#endif + Ptr makeReporter( Ptr const& config ) { std::vector reporters = config->getReporterNames(); if( reporters.empty() ) - reporters.push_back( "console" ); + reporters.push_back( CATCH_CONFIG_DEFAULT_REPORTER ); Ptr reporter; for( std::vector::const_iterator it = reporters.begin(), itEnd = reporters.end(); @@ -6387,11 +7235,11 @@ namespace Catch { if( lastSlash != std::string::npos ) filename = filename.substr( lastSlash+1 ); - std::string::size_type lastDot = filename.find_last_of( "." ); + std::string::size_type lastDot = filename.find_last_of( '.' ); if( lastDot != std::string::npos ) filename = filename.substr( 0, lastDot ); - tags.insert( "#" + filename ); + tags.insert( '#' + filename ); setTags( test, tags ); } } @@ -6417,11 +7265,18 @@ namespace Catch { } void showHelp( std::string const& processName ) { - Catch::cout() << "\nCatch v" << libraryVersion << "\n"; + Catch::cout() << "\nCatch v" << libraryVersion() << "\n"; m_cli.usage( Catch::cout(), processName ); Catch::cout() << "For more detail usage please see the project docs\n" << std::endl; } + void libIdentify() { + Catch::cout() + << std::left << std::setw(16) << "description: " << "A Catch test executable\n" + << std::left << std::setw(16) << "category: " << "testframework\n" + << std::left << std::setw(16) << "framework: " << "Catch Test\n" + << std::left << std::setw(16) << "version: " << libraryVersion() << std::endl; + } int applyCommandLine( int argc, char const* const* const argv, OnUnusedOptions::DoWhat unusedOptionBehaviour = OnUnusedOptions::Fail ) { try { @@ -6429,6 +7284,8 @@ namespace Catch { m_unusedTokens = m_cli.parseInto( Clara::argsToVector( argc, argv ), m_configData ); if( m_configData.showHelp ) showHelp( m_configData.processName ); + if( m_configData.libIdentify ) + libIdentify(); m_config.reset(); } catch( std::exception& ex ) { @@ -6458,29 +7315,43 @@ namespace Catch { return returnCode; } - int run() { - if( m_configData.showHelp ) - return 0; + #if defined(WIN32) && defined(UNICODE) + int run( int argc, wchar_t const* const* const argv ) { - try - { - config(); // Force config to be constructed + char **utf8Argv = new char *[ argc ]; - seedRng( *m_config ); + for ( int i = 0; i < argc; ++i ) { + int bufSize = WideCharToMultiByte( CP_UTF8, 0, argv[i], -1, NULL, 0, NULL, NULL ); - if( m_configData.filenamesAsTags ) - applyFilenamesAsTags( *m_config ); + utf8Argv[ i ] = new char[ bufSize ]; - // Handle list request - if( Option listed = list( config() ) ) - return static_cast( *listed ); + WideCharToMultiByte( CP_UTF8, 0, argv[i], -1, utf8Argv[i], bufSize, NULL, NULL ); + } - return static_cast( runTests( m_config ).assertions.failed ); + int returnCode = applyCommandLine( argc, utf8Argv ); + if( returnCode == 0 ) + returnCode = run(); + + for ( int i = 0; i < argc; ++i ) + delete [] utf8Argv[ i ]; + + delete [] utf8Argv; + + return returnCode; + } + #endif + + int run() { + if( ( m_configData.waitForKeypress & WaitForKeypress::BeforeStart ) != 0 ) { + Catch::cout() << "...waiting for enter/ return before starting" << std::endl; + static_cast(std::getchar()); } - catch( std::exception& ex ) { - Catch::cerr() << ex.what() << std::endl; - return (std::numeric_limits::max)(); + int exitCode = runInternal(); + if( ( m_configData.waitForKeypress & WaitForKeypress::BeforeExit ) != 0 ) { + Catch::cout() << "...waiting for enter/ return before exiting, with code: " << exitCode << std::endl; + static_cast(std::getchar()); } + return exitCode; } Clara::CommandLine const& cli() const { @@ -6498,6 +7369,32 @@ namespace Catch { return *m_config; } private: + + int runInternal() { + if( m_configData.showHelp || m_configData.libIdentify ) + return 0; + + try + { + config(); // Force config to be constructed + + seedRng( *m_config ); + + if( m_configData.filenamesAsTags ) + applyFilenamesAsTags( *m_config ); + + // Handle list request + if( Option listed = list( config() ) ) + return static_cast( *listed ); + + return static_cast( runTests( m_config ).assertions.failed ); + } + catch( std::exception& ex ) { + Catch::cerr() << ex.what() << std::endl; + return (std::numeric_limits::max)(); + } + } + Clara::CommandLine m_cli; std::vector m_unusedTokens; ConfigData m_configData; @@ -6517,20 +7414,19 @@ namespace Catch { #include #include #include -#include #include namespace Catch { struct RandomNumberGenerator { - typedef std::ptrdiff_t result_type; + typedef unsigned int result_type; result_type operator()( result_type n ) const { return std::rand() % n; } #ifdef CATCH_CONFIG_CPP11_SHUFFLE - static constexpr result_type min() { return 0; } - static constexpr result_type max() { return 1000000; } - result_type operator()() const { return std::rand() % max(); } + static constexpr result_type (min)() { return 0; } + static constexpr result_type (max)() { return 1000000; } + result_type operator()() const { return std::rand() % (max)(); } #endif template static void shuffle( V& vector ) { @@ -6578,7 +7474,7 @@ namespace Catch { ss << Colour( Colour::Red ) << "error: TEST_CASE( \"" << it->name << "\" ) already defined.\n" - << "\tFirst seen at " << prev.first->getTestCaseInfo().lineInfo << "\n" + << "\tFirst seen at " << prev.first->getTestCaseInfo().lineInfo << '\n' << "\tRedefined at " << it->getTestCaseInfo().lineInfo << std::endl; throw std::runtime_error(ss.str()); @@ -6610,7 +7506,7 @@ namespace Catch { virtual void registerTest( TestCase const& testCase ) { std::string name = testCase.getTestCaseInfo().name; - if( name == "" ) { + if( name.empty() ) { std::ostringstream oss; oss << "Anonymous test case " << ++m_unnamedCount; return registerTest( testCase.withName( oss.str() ) ); @@ -6659,7 +7555,7 @@ namespace Catch { inline std::string extractClassName( std::string const& classOrQualifiedMethodName ) { std::string className = classOrQualifiedMethodName; - if( startsWith( className, "&" ) ) + if( startsWith( className, '&' ) ) { std::size_t lastColons = className.rfind( "::" ); std::size_t penultimateColons = className.rfind( "::", lastColons-1 ); @@ -6806,6 +7702,26 @@ namespace Catch { }; } +// #included from: catch_tag_alias_registry.h +#define TWOBLUECUBES_CATCH_TAG_ALIAS_REGISTRY_H_INCLUDED + +#include + +namespace Catch { + + class TagAliasRegistry : public ITagAliasRegistry { + public: + virtual ~TagAliasRegistry(); + virtual Option find( std::string const& alias ) const; + virtual std::string expandAliases( std::string const& unexpandedTestSpec ) const; + void add( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ); + + private: + std::map m_registry; + }; + +} // end namespace Catch + namespace Catch { namespace { @@ -6827,6 +7743,9 @@ namespace Catch { virtual IExceptionTranslatorRegistry& getExceptionTranslatorRegistry() CATCH_OVERRIDE { return m_exceptionTranslatorRegistry; } + virtual ITagAliasRegistry const& getTagAliasRegistry() const CATCH_OVERRIDE { + return m_tagAliasRegistry; + } public: // IMutableRegistryHub virtual void registerReporter( std::string const& name, Ptr const& factory ) CATCH_OVERRIDE { @@ -6841,11 +7760,15 @@ namespace Catch { virtual void registerTranslator( const IExceptionTranslator* translator ) CATCH_OVERRIDE { m_exceptionTranslatorRegistry.registerTranslator( translator ); } + virtual void registerTagAlias( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) CATCH_OVERRIDE { + m_tagAliasRegistry.add( alias, tag, lineInfo ); + } private: TestRegistry m_testCaseRegistry; ReporterRegistry m_reporterRegistry; ExceptionTranslatorRegistry m_exceptionTranslatorRegistry; + TagAliasRegistry m_tagAliasRegistry; }; // Single, global, instance @@ -6877,7 +7800,7 @@ namespace Catch { // #included from: catch_notimplemented_exception.hpp #define TWOBLUECUBES_CATCH_NOTIMPLEMENTED_EXCEPTION_HPP_INCLUDED -#include +#include namespace Catch { @@ -6949,7 +7872,7 @@ namespace Catch { m_ofs.open( filename.c_str() ); if( m_ofs.fail() ) { std::ostringstream oss; - oss << "Unable to open file: '" << filename << "'"; + oss << "Unable to open file: '" << filename << '\''; throw std::domain_error( oss.str() ); } } @@ -6991,6 +7914,9 @@ namespace Catch { std::ostream& cerr() { return std::cerr; } + std::ostream& clog() { + return std::clog; + } #endif } @@ -7090,6 +8016,23 @@ namespace Catch { // #included from: catch_console_colour_impl.hpp #define TWOBLUECUBES_CATCH_CONSOLE_COLOUR_IMPL_HPP_INCLUDED +// #included from: catch_errno_guard.hpp +#define TWOBLUECUBES_CATCH_ERRNO_GUARD_HPP_INCLUDED + +#include + +namespace Catch { + + class ErrnoGuard { + public: + ErrnoGuard():m_oldErrno(errno){} + ~ErrnoGuard() { errno = m_oldErrno; } + private: + int m_oldErrno; + }; + +} + namespace Catch { namespace { @@ -7111,38 +8054,14 @@ namespace Catch { } // namespace Catch #if !defined( CATCH_CONFIG_COLOUR_NONE ) && !defined( CATCH_CONFIG_COLOUR_WINDOWS ) && !defined( CATCH_CONFIG_COLOUR_ANSI ) -# ifdef CATCH_PLATFORM_WINDOWS -# define CATCH_CONFIG_COLOUR_WINDOWS -# else -# define CATCH_CONFIG_COLOUR_ANSI -# endif -#endif - -#if defined ( CATCH_CONFIG_COLOUR_WINDOWS ) ///////////////////////////////////////// - -// #included from: catch_windows_h_proxy.h - -#define TWOBLUECUBES_CATCH_WINDOWS_H_PROXY_H_INCLUDED - -#ifdef CATCH_DEFINES_NOMINMAX -# define NOMINMAX -#endif -#ifdef CATCH_DEFINES_WIN32_LEAN_AND_MEAN -# define WIN32_LEAN_AND_MEAN -#endif - -#ifdef __AFXDLL -#include -#else -#include +# ifdef CATCH_PLATFORM_WINDOWS +# define CATCH_CONFIG_COLOUR_WINDOWS +# else +# define CATCH_CONFIG_COLOUR_ANSI +# endif #endif -#ifdef CATCH_DEFINES_NOMINMAX -# undef NOMINMAX -#endif -#ifdef CATCH_DEFINES_WIN32_LEAN_AND_MEAN -# undef WIN32_LEAN_AND_MEAN -#endif +#if defined ( CATCH_CONFIG_COLOUR_WINDOWS ) ///////////////////////////////////////// namespace Catch { namespace { @@ -7249,6 +8168,7 @@ namespace { }; IColourImpl* platformColourInstance() { + ErrnoGuard guard; Ptr config = getCurrentContext().getConfig(); UseColour::YesOrNo colourMode = config ? config->useColour() @@ -7367,14 +8287,18 @@ namespace Catch { namespace Catch { - AssertionInfo::AssertionInfo( std::string const& _macroName, + AssertionInfo::AssertionInfo():macroName(""), capturedExpression(""), resultDisposition(ResultDisposition::Normal), secondArg(""){} + + AssertionInfo::AssertionInfo( char const * _macroName, SourceLineInfo const& _lineInfo, - std::string const& _capturedExpression, - ResultDisposition::Flags _resultDisposition ) + char const * _capturedExpression, + ResultDisposition::Flags _resultDisposition, + char const * _secondArg) : macroName( _macroName ), lineInfo( _lineInfo ), capturedExpression( _capturedExpression ), - resultDisposition( _resultDisposition ) + resultDisposition( _resultDisposition ), + secondArg( _secondArg ) {} AssertionResult::AssertionResult() {} @@ -7401,24 +8325,30 @@ namespace Catch { } bool AssertionResult::hasExpression() const { - return !m_info.capturedExpression.empty(); + return m_info.capturedExpression[0] != 0; } bool AssertionResult::hasMessage() const { return !m_resultData.message.empty(); } + std::string capturedExpressionWithSecondArgument( char const * capturedExpression, char const * secondArg ) { + return (secondArg[0] == 0 || secondArg[0] == '"' && secondArg[1] == '"') + ? capturedExpression + : std::string(capturedExpression) + ", " + secondArg; + } + std::string AssertionResult::getExpression() const { if( isFalseTest( m_info.resultDisposition ) ) - return "!" + m_info.capturedExpression; + return "!(" + capturedExpressionWithSecondArgument(m_info.capturedExpression, m_info.secondArg) + ")"; else - return m_info.capturedExpression; + return capturedExpressionWithSecondArgument(m_info.capturedExpression, m_info.secondArg); } std::string AssertionResult::getExpressionInMacro() const { - if( m_info.macroName.empty() ) - return m_info.capturedExpression; + if( m_info.macroName[0] == 0 ) + return capturedExpressionWithSecondArgument(m_info.capturedExpression, m_info.secondArg); else - return m_info.macroName + "( " + m_info.capturedExpression + " )"; + return std::string(m_info.macroName) + "( " + capturedExpressionWithSecondArgument(m_info.capturedExpression, m_info.secondArg) + " )"; } bool AssertionResult::hasExpandedExpression() const { @@ -7426,7 +8356,7 @@ namespace Catch { } std::string AssertionResult::getExpandedExpression() const { - return m_resultData.reconstructedExpression; + return m_resultData.reconstructExpression(); } std::string AssertionResult::getMessage() const { @@ -7440,15 +8370,25 @@ namespace Catch { return m_info.macroName; } + void AssertionResult::discardDecomposedExpression() const { + m_resultData.decomposedExpression = CATCH_NULL; + } + + void AssertionResult::expandDecomposedExpression() const { + m_resultData.reconstructExpression(); + } + } // end namespace Catch // #included from: catch_test_case_info.hpp #define TWOBLUECUBES_CATCH_TEST_CASE_INFO_HPP_INCLUDED +#include + namespace Catch { inline TestCaseInfo::SpecialProperties parseSpecialTag( std::string const& tag ) { - if( startsWith( tag, "." ) || + if( startsWith( tag, '.' ) || tag == "hide" || tag == "!hide" ) return TestCaseInfo::IsHidden; @@ -7458,25 +8398,23 @@ namespace Catch { return TestCaseInfo::ShouldFail; else if( tag == "!mayfail" ) return TestCaseInfo::MayFail; + else if( tag == "!nonportable" ) + return TestCaseInfo::NonPortable; else return TestCaseInfo::None; } inline bool isReservedTag( std::string const& tag ) { - return parseSpecialTag( tag ) == TestCaseInfo::None && tag.size() > 0 && !isalnum( tag[0] ); + return parseSpecialTag( tag ) == TestCaseInfo::None && tag.size() > 0 && !std::isalnum( tag[0] ); } inline void enforceNotReservedTag( std::string const& tag, SourceLineInfo const& _lineInfo ) { if( isReservedTag( tag ) ) { - { - Colour colourGuard( Colour::Red ); - Catch::cerr() - << "Tag name [" << tag << "] not allowed.\n" - << "Tag names starting with non alpha-numeric characters are reserved\n"; - } - { - Colour colourGuard( Colour::FileName ); - Catch::cerr() << _lineInfo << std::endl; - } - exit(1); + std::ostringstream ss; + ss << Colour(Colour::Red) + << "Tag name [" << tag << "] not allowed.\n" + << "Tag names starting with non alpha-numeric characters are reserved\n" + << Colour(Colour::FileName) + << _lineInfo << '\n'; + throw std::runtime_error(ss.str()); } } @@ -7532,7 +8470,7 @@ namespace Catch { std::ostringstream oss; for( std::set::const_iterator it = tags.begin(), itEnd = tags.end(); it != itEnd; ++it ) { - oss << "[" << *it << "]"; + oss << '[' << *it << ']'; std::string lcaseTag = toLower( *it ); testCaseInfo.properties = static_cast( testCaseInfo.properties | parseSpecialTag( lcaseTag ) ); testCaseInfo.lcaseTags.insert( lcaseTag ); @@ -7638,7 +8576,7 @@ namespace Catch { ( unsigned int _majorVersion, unsigned int _minorVersion, unsigned int _patchNumber, - std::string const& _branchName, + char const * const _branchName, unsigned int _buildNumber ) : majorVersion( _majorVersion ), minorVersion( _minorVersion ), @@ -7648,18 +8586,21 @@ namespace Catch { {} std::ostream& operator << ( std::ostream& os, Version const& version ) { - os << version.majorVersion << "." - << version.minorVersion << "." + os << version.majorVersion << '.' + << version.minorVersion << '.' << version.patchNumber; - - if( !version.branchName.empty() ) { - os << "-" << version.branchName - << "." << version.buildNumber; + // branchName is never null -> 0th char is \0 if it is empty + if (version.branchName[0]) { + os << '-' << version.branchName + << '.' << version.buildNumber; } return os; } - Version libraryVersion( 1, 6, 1, "", 0 ); + inline Version libraryVersion() { + static Version version( 1, 12, 2, "", 0 ); + return version; + } } @@ -7692,9 +8633,18 @@ namespace Catch { : m_info( other.m_info ) {} +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable:4996) // std::uncaught_exception is deprecated in C++17 +#endif ScopedMessage::~ScopedMessage() { - getResultCapture().popScopedMessage( m_info ); + if ( !std::uncaught_exception() ){ + getResultCapture().popScopedMessage(m_info); + } } +#if defined(_MSC_VER) +#pragma warning(pop) +#endif } // end namespace Catch @@ -7830,29 +8780,32 @@ namespace Catch #endif #ifdef CATCH_PLATFORM_WINDOWS + #else + #include + #endif namespace Catch { namespace { #ifdef CATCH_PLATFORM_WINDOWS - uint64_t getCurrentTicks() { - static uint64_t hz=0, hzo=0; + UInt64 getCurrentTicks() { + static UInt64 hz=0, hzo=0; if (!hz) { QueryPerformanceFrequency( reinterpret_cast( &hz ) ); QueryPerformanceCounter( reinterpret_cast( &hzo ) ); } - uint64_t t; + UInt64 t; QueryPerformanceCounter( reinterpret_cast( &t ) ); return ((t-hzo)*1000000)/hz; } #else - uint64_t getCurrentTicks() { + UInt64 getCurrentTicks() { timeval t; gettimeofday(&t,CATCH_NULL); - return static_cast( t.tv_sec ) * 1000000ull + static_cast( t.tv_usec ); + return static_cast( t.tv_sec ) * 1000000ull + static_cast( t.tv_usec ); } #endif } @@ -7878,19 +8831,28 @@ namespace Catch { // #included from: catch_common.hpp #define TWOBLUECUBES_CATCH_COMMON_HPP_INCLUDED +#include +#include + namespace Catch { bool startsWith( std::string const& s, std::string const& prefix ) { - return s.size() >= prefix.size() && s.substr( 0, prefix.size() ) == prefix; + return s.size() >= prefix.size() && std::equal(prefix.begin(), prefix.end(), s.begin()); + } + bool startsWith( std::string const& s, char prefix ) { + return !s.empty() && s[0] == prefix; } bool endsWith( std::string const& s, std::string const& suffix ) { - return s.size() >= suffix.size() && s.substr( s.size()-suffix.size(), suffix.size() ) == suffix; + return s.size() >= suffix.size() && std::equal(suffix.rbegin(), suffix.rend(), s.rbegin()); + } + bool endsWith( std::string const& s, char suffix ) { + return !s.empty() && s[s.size()-1] == suffix; } bool contains( std::string const& s, std::string const& infix ) { return s.find( infix ) != std::string::npos; } char toLowerCh(char c) { - return static_cast( ::tolower( c ) ); + return static_cast( std::tolower( c ) ); } void toLowerInPlace( std::string& s ) { std::transform( s.begin(), s.end(), s.begin(), toLowerCh ); @@ -7905,7 +8867,7 @@ namespace Catch { std::string::size_type start = str.find_first_not_of( whitespaceChars ); std::string::size_type end = str.find_last_not_of( whitespaceChars ); - return start != std::string::npos ? str.substr( start, 1+end-start ) : ""; + return start != std::string::npos ? str.substr( start, 1+end-start ) : std::string(); } bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis ) { @@ -7928,29 +8890,25 @@ namespace Catch { {} std::ostream& operator << ( std::ostream& os, pluralise const& pluraliser ) { - os << pluraliser.m_count << " " << pluraliser.m_label; + os << pluraliser.m_count << ' ' << pluraliser.m_label; if( pluraliser.m_count != 1 ) - os << "s"; + os << 's'; return os; } - SourceLineInfo::SourceLineInfo() : line( 0 ){} + SourceLineInfo::SourceLineInfo() : file(""), line( 0 ){} SourceLineInfo::SourceLineInfo( char const* _file, std::size_t _line ) : file( _file ), line( _line ) {} - SourceLineInfo::SourceLineInfo( SourceLineInfo const& other ) - : file( other.file ), - line( other.line ) - {} bool SourceLineInfo::empty() const { - return file.empty(); + return file[0] == '\0'; } bool SourceLineInfo::operator == ( SourceLineInfo const& other ) const { - return line == other.line && file == other.file; + return line == other.line && (file == other.file || std::strcmp(file, other.file) == 0); } bool SourceLineInfo::operator < ( SourceLineInfo const& other ) const { - return line < other.line || ( line == other.line && file < other.file ); + return line < other.line || ( line == other.line && (std::strcmp(file, other.file) < 0)); } void seedRng( IConfig const& config ) { @@ -7963,16 +8921,16 @@ namespace Catch { std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ) { #ifndef __GNUG__ - os << info.file << "(" << info.line << ")"; + os << info.file << '(' << info.line << ')'; #else - os << info.file << ":" << info.line; + os << info.file << ':' << info.line; #endif return os; } void throwLogicError( std::string const& message, SourceLineInfo const& locationInfo ) { std::ostringstream oss; - oss << locationInfo << ": Internal Catch error: '" << message << "'"; + oss << locationInfo << ": Internal Catch error: '" << message << '\''; if( alwaysTrue() ) throw std::logic_error( oss.str() ); } @@ -7999,6 +8957,10 @@ namespace Catch { m_timer.start(); } +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable:4996) // std::uncaught_exception is deprecated in C++17 +#endif Section::~Section() { if( m_sectionIncluded ) { SectionEndInfo endInfo( m_info, m_assertions, m_timer.getElapsedSeconds() ); @@ -8008,6 +8970,9 @@ namespace Catch { getResultCapture().sectionEnded( endInfo ); } } +#if defined(_MSC_VER) +#pragma warning(pop) +#endif // This indicates whether the section should be executed or not Section::operator bool() const { @@ -8019,8 +8984,6 @@ namespace Catch { // #included from: catch_debugger.hpp #define TWOBLUECUBES_CATCH_DEBUGGER_HPP_INCLUDED -#include - #ifdef CATCH_PLATFORM_MAC #include @@ -8082,6 +9045,9 @@ namespace Catch { // be strace, for example) in /proc/$PID/status, so just get it from // there instead. bool isDebuggerActive(){ + // Libstdc++ has a bug, where std::ifstream sets errno to 0 + // This way our users can properly assert over errno values + ErrnoGuard guard; std::ifstream in("/proc/self/status"); for( std::string line; std::getline(in, line); ) { static const int PREFIX_LEN = 11; @@ -8117,7 +9083,7 @@ namespace Catch { #endif // Platform #ifdef CATCH_PLATFORM_WINDOWS - extern "C" __declspec(dllimport) void __stdcall OutputDebugStringA( const char* ); + namespace Catch { void writeToDebugConsole( std::string const& text ) { ::OutputDebugStringA( text.c_str() ); @@ -8193,7 +9159,7 @@ std::string toString( std::string const& value ) { } } } - return "\"" + s + "\""; + return '"' + s + '"'; } std::string toString( std::wstring const& value ) { @@ -8214,19 +9180,19 @@ std::string toString( char* const value ) { std::string toString( const wchar_t* const value ) { - return value ? Catch::toString( std::wstring(value) ) : std::string( "{null string}" ); + return value ? Catch::toString( std::wstring(value) ) : std::string( "{null string}" ); } std::string toString( wchar_t* const value ) { - return Catch::toString( static_cast( value ) ); + return Catch::toString( static_cast( value ) ); } std::string toString( int value ) { std::ostringstream oss; oss << value; if( value > Detail::hexThreshold ) - oss << " (0x" << std::hex << value << ")"; + oss << " (0x" << std::hex << value << ')'; return oss.str(); } @@ -8234,7 +9200,7 @@ std::string toString( unsigned long value ) { std::ostringstream oss; oss << value; if( value > Detail::hexThreshold ) - oss << " (0x" << std::hex << value << ")"; + oss << " (0x" << std::hex << value << ')'; return oss.str(); } @@ -8262,7 +9228,7 @@ std::string toString( const double value ) { return fpToString( value, 10 ); } std::string toString( const float value ) { - return fpToString( value, 5 ) + "f"; + return fpToString( value, 5 ) + 'f'; } std::string toString( bool value ) { @@ -8270,9 +9236,19 @@ std::string toString( bool value ) { } std::string toString( char value ) { - return value < ' ' - ? toString( static_cast( value ) ) - : Detail::makeString( value ); + if ( value == '\r' ) + return "'\\r'"; + if ( value == '\f' ) + return "'\\f'"; + if ( value == '\n' ) + return "'\\n'"; + if ( value == '\t' ) + return "'\\t'"; + if ( '\0' <= value && value < ' ' ) + return toString( static_cast( value ) ); + char chstr[] = "' '"; + chstr[1] = value; + return chstr; } std::string toString( signed char value ) { @@ -8288,14 +9264,14 @@ std::string toString( long long value ) { std::ostringstream oss; oss << value; if( value > Detail::hexThreshold ) - oss << " (0x" << std::hex << value << ")"; + oss << " (0x" << std::hex << value << ')'; return oss.str(); } std::string toString( unsigned long long value ) { std::ostringstream oss; oss << value; if( value > Detail::hexThreshold ) - oss << " (0x" << std::hex << value << ")"; + oss << " (0x" << std::hex << value << ')'; return oss.str(); } #endif @@ -8312,7 +9288,7 @@ std::string toString( std::nullptr_t ) { return "nil"; return "@" + toString([nsstring UTF8String]); } - std::string toString( NSString * CATCH_ARC_STRONG const& nsstring ) { + std::string toString( NSString * CATCH_ARC_STRONG & nsstring ) { if( !nsstring ) return "nil"; return "@" + toString([nsstring UTF8String]); @@ -8327,23 +9303,32 @@ std::string toString( std::nullptr_t ) { // #included from: catch_result_builder.hpp #define TWOBLUECUBES_CATCH_RESULT_BUILDER_HPP_INCLUDED +#include + namespace Catch { - std::string capturedExpressionWithSecondArgument( std::string const& capturedExpression, std::string const& secondArg ) { - return secondArg.empty() || secondArg == "\"\"" - ? capturedExpression - : capturedExpression + ", " + secondArg; - } ResultBuilder::ResultBuilder( char const* macroName, SourceLineInfo const& lineInfo, char const* capturedExpression, ResultDisposition::Flags resultDisposition, char const* secondArg ) - : m_assertionInfo( macroName, lineInfo, capturedExpressionWithSecondArgument( capturedExpression, secondArg ), resultDisposition ), + : m_assertionInfo( macroName, lineInfo, capturedExpression, resultDisposition, secondArg ), m_shouldDebugBreak( false ), - m_shouldThrow( false ) + m_shouldThrow( false ), + m_guardException( false ), + m_usedStream( false ) {} + ResultBuilder::~ResultBuilder() { +#if defined(CATCH_CONFIG_FAST_COMPILE) + if ( m_guardException ) { + stream().oss << "Exception translation was disabled by CATCH_CONFIG_FAST_COMPILE"; + captureResult( ResultWas::ThrewException ); + getCurrentContext().getResultCapture()->exceptionEarlyReported(); + } +#endif + } + ResultBuilder& ResultBuilder::setResultType( ResultWas::OfType result ) { m_data.resultType = result; return *this; @@ -8352,27 +9337,27 @@ namespace Catch { m_data.resultType = result ? ResultWas::Ok : ResultWas::ExpressionFailed; return *this; } - ResultBuilder& ResultBuilder::setLhs( std::string const& lhs ) { - m_exprComponents.lhs = lhs; - return *this; - } - ResultBuilder& ResultBuilder::setRhs( std::string const& rhs ) { - m_exprComponents.rhs = rhs; - return *this; - } - ResultBuilder& ResultBuilder::setOp( std::string const& op ) { - m_exprComponents.op = op; - return *this; - } - void ResultBuilder::endExpression() { - m_exprComponents.testFalse = isFalseTest( m_assertionInfo.resultDisposition ); - captureExpression(); + void ResultBuilder::endExpression( DecomposedExpression const& expr ) { + // Flip bool results if FalseTest flag is set + if( isFalseTest( m_assertionInfo.resultDisposition ) ) { + m_data.negate( expr.isBinaryExpression() ); + } + + getResultCapture().assertionRun(); + + if(getCurrentContext().getConfig()->includeSuccessfulResults() || m_data.resultType != ResultWas::Ok) + { + AssertionResult result = build( expr ); + handleResult( result ); + } + else + getResultCapture().assertionPassed(); } void ResultBuilder::useActiveException( ResultDisposition::Flags resultDisposition ) { m_assertionInfo.resultDisposition = resultDisposition; - m_stream.oss << Catch::translateActiveException(); + stream().oss << Catch::translateActiveException(); captureResult( ResultWas::ThrewException ); } @@ -8380,19 +9365,20 @@ namespace Catch { setResultType( resultType ); captureExpression(); } + void ResultBuilder::captureExpectedException( std::string const& expectedMessage ) { if( expectedMessage.empty() ) - captureExpectedException( Matchers::Impl::Generic::AllOf() ); + captureExpectedException( Matchers::Impl::MatchAllOf() ); else captureExpectedException( Matchers::Equals( expectedMessage ) ); } - void ResultBuilder::captureExpectedException( Matchers::Impl::Matcher const& matcher ) { + void ResultBuilder::captureExpectedException( Matchers::Impl::MatcherBase const& matcher ) { - assert( m_exprComponents.testFalse == false ); + assert( !isFalseTest( m_assertionInfo.resultDisposition ) ); AssertionResultData data = m_data; data.resultType = ResultWas::Ok; - data.reconstructedExpression = m_assertionInfo.capturedExpression; + data.reconstructedExpression = capturedExpressionWithSecondArgument(m_assertionInfo.capturedExpression, m_assertionInfo.secondArg); std::string actualMessage = Catch::translateActiveException(); if( !matcher.match( actualMessage ) ) { @@ -8407,6 +9393,7 @@ namespace Catch { AssertionResult result = build(); handleResult( result ); } + void ResultBuilder::handleResult( AssertionResult const& result ) { getResultCapture().assertionEnded( result ); @@ -8418,7 +9405,17 @@ namespace Catch { m_shouldThrow = true; } } + void ResultBuilder::react() { +#if defined(CATCH_CONFIG_FAST_COMPILE) + if (m_shouldDebugBreak) { + /////////////////////////////////////////////////////////////////// + // To inspect the state during test, you need to go one level up the callstack + // To go back to the test and change execution, jump over the throw statement + /////////////////////////////////////////////////////////////////// + CATCH_BREAK_INTO_DEBUGGER(); + } +#endif if( m_shouldThrow ) throw Catch::TestFailureException(); } @@ -8428,43 +9425,35 @@ namespace Catch { AssertionResult ResultBuilder::build() const { - assert( m_data.resultType != ResultWas::Unknown ); + return build( *this ); + } + // CAVEAT: The returned AssertionResult stores a pointer to the argument expr, + // a temporary DecomposedExpression, which in turn holds references to + // operands, possibly temporary as well. + // It should immediately be passed to handleResult; if the expression + // needs to be reported, its string expansion must be composed before + // the temporaries are destroyed. + AssertionResult ResultBuilder::build( DecomposedExpression const& expr ) const + { + assert( m_data.resultType != ResultWas::Unknown ); AssertionResultData data = m_data; - // Flip bool results if testFalse is set - if( m_exprComponents.testFalse ) { - if( data.resultType == ResultWas::Ok ) - data.resultType = ResultWas::ExpressionFailed; - else if( data.resultType == ResultWas::ExpressionFailed ) - data.resultType = ResultWas::Ok; - } - - data.message = m_stream.oss.str(); - data.reconstructedExpression = reconstructExpression(); - if( m_exprComponents.testFalse ) { - if( m_exprComponents.op == "" ) - data.reconstructedExpression = "!" + data.reconstructedExpression; - else - data.reconstructedExpression = "!(" + data.reconstructedExpression + ")"; - } + if(m_usedStream) + data.message = m_stream().oss.str(); + data.decomposedExpression = &expr; // for lazy reconstruction return AssertionResult( m_assertionInfo, data ); } - std::string ResultBuilder::reconstructExpression() const { - if( m_exprComponents.op == "" ) - return m_exprComponents.lhs.empty() ? m_assertionInfo.capturedExpression : m_exprComponents.lhs; - else if( m_exprComponents.op == "matches" ) - return m_exprComponents.lhs + " " + m_exprComponents.rhs; - else if( m_exprComponents.op != "!" ) { - if( m_exprComponents.lhs.size() + m_exprComponents.rhs.size() < 40 && - m_exprComponents.lhs.find("\n") == std::string::npos && - m_exprComponents.rhs.find("\n") == std::string::npos ) - return m_exprComponents.lhs + " " + m_exprComponents.op + " " + m_exprComponents.rhs; - else - return m_exprComponents.lhs + "\n" + m_exprComponents.op + "\n" + m_exprComponents.rhs; - } - else - return "{can't expand - use " + m_assertionInfo.macroName + "_FALSE( " + m_assertionInfo.capturedExpression.substr(1) + " ) instead of " + m_assertionInfo.macroName + "( " + m_assertionInfo.capturedExpression + " ) for better diagnostics}"; + + void ResultBuilder::reconstructExpression( std::string& dest ) const { + dest = capturedExpressionWithSecondArgument(m_assertionInfo.capturedExpression, m_assertionInfo.secondArg); + } + + void ResultBuilder::setExceptionGuard() { + m_guardException = true; + } + void ResultBuilder::unsetExceptionGuard() { + m_guardException = false; } } // end namespace Catch @@ -8472,30 +9461,6 @@ namespace Catch { // #included from: catch_tag_alias_registry.hpp #define TWOBLUECUBES_CATCH_TAG_ALIAS_REGISTRY_HPP_INCLUDED -// #included from: catch_tag_alias_registry.h -#define TWOBLUECUBES_CATCH_TAG_ALIAS_REGISTRY_H_INCLUDED - -#include - -namespace Catch { - - class TagAliasRegistry : public ITagAliasRegistry { - public: - virtual ~TagAliasRegistry(); - virtual Option find( std::string const& alias ) const; - virtual std::string expandAliases( std::string const& unexpandedTestSpec ) const; - void add( char const* alias, char const* tag, SourceLineInfo const& lineInfo ); - static TagAliasRegistry& get(); - - private: - std::map m_registry; - }; - -} // end namespace Catch - -#include -#include - namespace Catch { TagAliasRegistry::~TagAliasRegistry() {} @@ -8523,44 +9488,120 @@ namespace Catch { return expandedTestSpec; } - void TagAliasRegistry::add( char const* alias, char const* tag, SourceLineInfo const& lineInfo ) { + void TagAliasRegistry::add( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) { - if( !startsWith( alias, "[@" ) || !endsWith( alias, "]" ) ) { + if( !startsWith( alias, "[@" ) || !endsWith( alias, ']' ) ) { std::ostringstream oss; - oss << "error: tag alias, \"" << alias << "\" is not of the form [@alias name].\n" << lineInfo; + oss << Colour( Colour::Red ) + << "error: tag alias, \"" << alias << "\" is not of the form [@alias name].\n" + << Colour( Colour::FileName ) + << lineInfo << '\n'; throw std::domain_error( oss.str().c_str() ); } if( !m_registry.insert( std::make_pair( alias, TagAlias( tag, lineInfo ) ) ).second ) { std::ostringstream oss; - oss << "error: tag alias, \"" << alias << "\" already registered.\n" - << "\tFirst seen at " << find(alias)->lineInfo << "\n" - << "\tRedefined at " << lineInfo; + oss << Colour( Colour::Red ) + << "error: tag alias, \"" << alias << "\" already registered.\n" + << "\tFirst seen at " + << Colour( Colour::Red ) << find(alias)->lineInfo << '\n' + << Colour( Colour::Red ) << "\tRedefined at " + << Colour( Colour::FileName) << lineInfo << '\n'; throw std::domain_error( oss.str().c_str() ); } } - TagAliasRegistry& TagAliasRegistry::get() { - static TagAliasRegistry instance; - return instance; + ITagAliasRegistry::~ITagAliasRegistry() {} + + ITagAliasRegistry const& ITagAliasRegistry::get() { + return getRegistryHub().getTagAliasRegistry(); + } + RegistrarForTagAliases::RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo ) { + getMutableRegistryHub().registerTagAlias( alias, tag, lineInfo ); } - ITagAliasRegistry::~ITagAliasRegistry() {} - ITagAliasRegistry const& ITagAliasRegistry::get() { return TagAliasRegistry::get(); } +} // end namespace Catch - RegistrarForTagAliases::RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo ) { - try { - TagAliasRegistry::get().add( alias, tag, lineInfo ); +// #included from: catch_matchers_string.hpp + +namespace Catch { +namespace Matchers { + + namespace StdString { + + CasedString::CasedString( std::string const& str, CaseSensitive::Choice caseSensitivity ) + : m_caseSensitivity( caseSensitivity ), + m_str( adjustString( str ) ) + {} + std::string CasedString::adjustString( std::string const& str ) const { + return m_caseSensitivity == CaseSensitive::No + ? toLower( str ) + : str; } - catch( std::exception& ex ) { - Colour colourGuard( Colour::Red ); - Catch::cerr() << ex.what() << std::endl; - exit(1); + std::string CasedString::caseSensitivitySuffix() const { + return m_caseSensitivity == CaseSensitive::No + ? " (case insensitive)" + : std::string(); } - } -} // end namespace Catch + StringMatcherBase::StringMatcherBase( std::string const& operation, CasedString const& comparator ) + : m_comparator( comparator ), + m_operation( operation ) { + } + + std::string StringMatcherBase::describe() const { + std::string description; + description.reserve(5 + m_operation.size() + m_comparator.m_str.size() + + m_comparator.caseSensitivitySuffix().size()); + description += m_operation; + description += ": \""; + description += m_comparator.m_str; + description += "\""; + description += m_comparator.caseSensitivitySuffix(); + return description; + } + + EqualsMatcher::EqualsMatcher( CasedString const& comparator ) : StringMatcherBase( "equals", comparator ) {} + + bool EqualsMatcher::match( std::string const& source ) const { + return m_comparator.adjustString( source ) == m_comparator.m_str; + } + + ContainsMatcher::ContainsMatcher( CasedString const& comparator ) : StringMatcherBase( "contains", comparator ) {} + + bool ContainsMatcher::match( std::string const& source ) const { + return contains( m_comparator.adjustString( source ), m_comparator.m_str ); + } + + StartsWithMatcher::StartsWithMatcher( CasedString const& comparator ) : StringMatcherBase( "starts with", comparator ) {} + + bool StartsWithMatcher::match( std::string const& source ) const { + return startsWith( m_comparator.adjustString( source ), m_comparator.m_str ); + } + + EndsWithMatcher::EndsWithMatcher( CasedString const& comparator ) : StringMatcherBase( "ends with", comparator ) {} + + bool EndsWithMatcher::match( std::string const& source ) const { + return endsWith( m_comparator.adjustString( source ), m_comparator.m_str ); + } + + } // namespace StdString + + StdString::EqualsMatcher Equals( std::string const& str, CaseSensitive::Choice caseSensitivity ) { + return StdString::EqualsMatcher( StdString::CasedString( str, caseSensitivity) ); + } + StdString::ContainsMatcher Contains( std::string const& str, CaseSensitive::Choice caseSensitivity ) { + return StdString::ContainsMatcher( StdString::CasedString( str, caseSensitivity) ); + } + StdString::EndsWithMatcher EndsWith( std::string const& str, CaseSensitive::Choice caseSensitivity ) { + return StdString::EndsWithMatcher( StdString::CasedString( str, caseSensitivity) ); + } + StdString::StartsWithMatcher StartsWith( std::string const& str, CaseSensitive::Choice caseSensitivity ) { + return StdString::StartsWithMatcher( StdString::CasedString( str, caseSensitivity) ); + } +} // namespace Matchers +} // namespace Catch // #included from: ../reporters/catch_reporter_multi.hpp #define TWOBLUECUBES_CATCH_REPORTER_MULTI_HPP_INCLUDED @@ -8704,9 +9745,34 @@ Ptr addReporter( Ptr const& existingRepo #define TWOBLUECUBES_CATCH_REPORTER_BASES_HPP_INCLUDED #include +#include +#include +#include namespace Catch { + namespace { + // Because formatting using c++ streams is stateful, drop down to C is required + // Alternatively we could use stringstream, but its performance is... not good. + std::string getFormattedDuration( double duration ) { + // Max exponent + 1 is required to represent the whole part + // + 1 for decimal point + // + 3 for the 3 decimal places + // + 1 for null terminator + const size_t maxDoubleSize = DBL_MAX_10_EXP + 1 + 1 + 3 + 1; + char buffer[maxDoubleSize]; + + // Save previous errno, to prevent sprintf from overwriting it + ErrnoGuard guard; +#ifdef _MSC_VER + sprintf_s(buffer, "%.3f", duration); +#else + sprintf(buffer, "%.3f", duration); +#endif + return std::string(buffer); + } + } + struct StreamingReporterBase : SharedImpl { StreamingReporterBase( ReporterConfig const& _config ) @@ -8801,12 +9867,13 @@ namespace Catch { struct BySectionInfo { BySectionInfo( SectionInfo const& other ) : m_other( other ) {} - BySectionInfo( BySectionInfo const& other ) : m_other( other.m_other ) {} + BySectionInfo( BySectionInfo const& other ) : m_other( other.m_other ) {} bool operator() ( Ptr const& node ) const { - return node->stats.sectionInfo.lineInfo == m_other.lineInfo; + return ((node->stats.sectionInfo.name == m_other.name) && + (node->stats.sectionInfo.lineInfo == m_other.lineInfo)); } private: - void operator=( BySectionInfo const& ); + void operator=( BySectionInfo const& ); SectionInfo const& m_other; }; @@ -8862,6 +9929,12 @@ namespace Catch { assert( !m_sectionStack.empty() ); SectionNode& sectionNode = *m_sectionStack.back(); sectionNode.assertions.push_back( assertionStats ); + // AssertionResult holds a pointer to a temporary DecomposedExpression, + // which getExpandedExpression() calls to build the expression string. + // Our section stack copy of the assertionResult will likely outlive the + // temporary, so it must be expanded or discarded now to avoid calling + // a destroyed object later. + prepareExpandedExpression( sectionNode.assertions.back().assertionResult ); return true; } virtual void sectionEnded( SectionStats const& sectionStats ) CATCH_OVERRIDE { @@ -8896,6 +9969,13 @@ namespace Catch { virtual void skipTest( TestCaseInfo const& ) CATCH_OVERRIDE {} + virtual void prepareExpandedExpression( AssertionResult& result ) const { + if( result.isOk() ) + result.discardDecomposedExpression(); + else + result.expandDecomposedExpression(); + } + Ptr m_config; std::ostream& stream; std::vector m_assertions; @@ -8916,7 +9996,7 @@ namespace Catch { char const* getLineOfChars() { static char line[CATCH_CONFIG_CONSOLE_WIDTH] = {0}; if( !*line ) { - memset( line, C, CATCH_CONFIG_CONSOLE_WIDTH-1 ); + std::memset( line, C, CATCH_CONFIG_CONSOLE_WIDTH-1 ); line[CATCH_CONFIG_CONSOLE_WIDTH-1] = 0; } return line; @@ -9001,7 +10081,7 @@ namespace Catch { return new T( config ); } virtual std::string getDescription() const { - return ""; + return std::string(); } }; @@ -9019,9 +10099,13 @@ namespace Catch { #define INTERNAL_CATCH_REGISTER_REPORTER( name, reporterType ) \ namespace{ Catch::ReporterRegistrar catch_internal_RegistrarFor##reporterType( name ); } +// Deprecated - use the form without INTERNAL_ #define INTERNAL_CATCH_REGISTER_LISTENER( listenerType ) \ namespace{ Catch::ListenerRegistrar catch_internal_RegistrarFor##listenerType; } +#define CATCH_REGISTER_LISTENER( listenerType ) \ + namespace{ Catch::ListenerRegistrar catch_internal_RegistrarFor##listenerType; } + // #included from: ../internal/catch_xmlwriter.hpp #define TWOBLUECUBES_CATCH_XMLWRITER_HPP_INCLUDED @@ -9070,8 +10154,11 @@ namespace Catch { default: // Escape control chars - based on contribution by @espenalb in PR #465 and // by @mrpi PR #588 - if ( ( c >= 0 && c < '\x09' ) || ( c > '\x0D' && c < '\x20') || c=='\x7F' ) - os << "&#x" << std::uppercase << std::hex << std::setfill('0') << std::setw(2) << static_cast( c ) << ';'; + if ( ( c >= 0 && c < '\x09' ) || ( c > '\x0D' && c < '\x20') || c=='\x7F' ) { + // see http://stackoverflow.com/questions/404107/why-are-control-characters-illegal-in-xml-1-0 + os << "\\x" << std::uppercase << std::hex << std::setfill('0') << std::setw(2) + << static_cast( c ); + } else os << c; } @@ -9125,20 +10212,17 @@ namespace Catch { XmlWriter() : m_tagIsOpen( false ), m_needsNewline( false ), - m_os( &Catch::cout() ) + m_os( Catch::cout() ) { - // We encode control characters, which requires - // XML 1.1 - // see http://stackoverflow.com/questions/404107/why-are-control-characters-illegal-in-xml-1-0 - *m_os << "\n"; + writeDeclaration(); } XmlWriter( std::ostream& os ) : m_tagIsOpen( false ), m_needsNewline( false ), - m_os( &os ) + m_os( os ) { - *m_os << "\n"; + writeDeclaration(); } ~XmlWriter() { @@ -9149,7 +10233,7 @@ namespace Catch { XmlWriter& startElement( std::string const& name ) { ensureTagClosed(); newlineIfNecessary(); - stream() << m_indent << "<" << name; + m_os << m_indent << '<' << name; m_tags.push_back( name ); m_indent += " "; m_tagIsOpen = true; @@ -9166,24 +10250,25 @@ namespace Catch { newlineIfNecessary(); m_indent = m_indent.substr( 0, m_indent.size()-2 ); if( m_tagIsOpen ) { - stream() << "/>\n"; + m_os << "/>"; m_tagIsOpen = false; } else { - stream() << m_indent << "\n"; + m_os << m_indent << ""; } + m_os << std::endl; m_tags.pop_back(); return *this; } XmlWriter& writeAttribute( std::string const& name, std::string const& attribute ) { if( !name.empty() && !attribute.empty() ) - stream() << " " << name << "=\"" << XmlEncode( attribute, XmlEncode::ForAttributes ) << "\""; + m_os << ' ' << name << "=\"" << XmlEncode( attribute, XmlEncode::ForAttributes ) << '"'; return *this; } XmlWriter& writeAttribute( std::string const& name, bool attribute ) { - stream() << " " << name << "=\"" << ( attribute ? "true" : "false" ) << "\""; + m_os << ' ' << name << "=\"" << ( attribute ? "true" : "false" ) << '"'; return *this; } @@ -9199,8 +10284,8 @@ namespace Catch { bool tagWasOpen = m_tagIsOpen; ensureTagClosed(); if( tagWasOpen && indent ) - stream() << m_indent; - stream() << XmlEncode( text ); + m_os << m_indent; + m_os << XmlEncode( text ); m_needsNewline = true; } return *this; @@ -9208,39 +10293,39 @@ namespace Catch { XmlWriter& writeComment( std::string const& text ) { ensureTagClosed(); - stream() << m_indent << ""; + m_os << m_indent << ""; m_needsNewline = true; return *this; } + void writeStylesheetRef( std::string const& url ) { + m_os << "\n"; + } + XmlWriter& writeBlankLine() { ensureTagClosed(); - stream() << "\n"; + m_os << '\n'; return *this; } - void setStream( std::ostream& os ) { - m_os = &os; + void ensureTagClosed() { + if( m_tagIsOpen ) { + m_os << ">" << std::endl; + m_tagIsOpen = false; + } } private: XmlWriter( XmlWriter const& ); void operator=( XmlWriter const& ); - std::ostream& stream() { - return *m_os; - } - - void ensureTagClosed() { - if( m_tagIsOpen ) { - stream() << ">\n"; - m_tagIsOpen = false; - } + void writeDeclaration() { + m_os << "\n"; } void newlineIfNecessary() { if( m_needsNewline ) { - stream() << "\n"; + m_os << std::endl; m_needsNewline = false; } } @@ -9249,24 +10334,10 @@ namespace Catch { bool m_needsNewline; std::vector m_tags; std::string m_indent; - std::ostream* m_os; + std::ostream& m_os; }; } -// #included from: catch_reenable_warnings.h - -#define TWOBLUECUBES_CATCH_REENABLE_WARNINGS_H_INCLUDED - -#ifdef __clang__ -# ifdef __ICC // icpc defines the __clang__ macro -# pragma warning(pop) -# else -# pragma clang diagnostic pop -# endif -#elif defined __GNUC__ -# pragma GCC diagnostic pop -#endif - namespace Catch { class XmlReporter : public StreamingReporterBase { @@ -9285,6 +10356,16 @@ namespace Catch { return "Reports test results as an XML document"; } + virtual std::string getStylesheetRef() const { + return std::string(); + } + + void writeSourceInfo( SourceLineInfo const& sourceInfo ) { + m_xml + .writeAttribute( "filename", sourceInfo.file ) + .writeAttribute( "line", sourceInfo.line ); + } + public: // StreamingReporterBase virtual void noMatchingTestCases( std::string const& s ) CATCH_OVERRIDE { @@ -9293,6 +10374,9 @@ namespace Catch { virtual void testRunStarting( TestRunInfo const& testInfo ) CATCH_OVERRIDE { StreamingReporterBase::testRunStarting( testInfo ); + std::string stylesheetRef = getStylesheetRef(); + if( !stylesheetRef.empty() ) + m_xml.writeStylesheetRef( stylesheetRef ); m_xml.startElement( "Catch" ); if( !m_config->name().empty() ) m_xml.writeAttribute( "name", m_config->name() ); @@ -9306,10 +10390,16 @@ namespace Catch { virtual void testCaseStarting( TestCaseInfo const& testInfo ) CATCH_OVERRIDE { StreamingReporterBase::testCaseStarting(testInfo); - m_xml.startElement( "TestCase" ).writeAttribute( "name", testInfo.name ); + m_xml.startElement( "TestCase" ) + .writeAttribute( "name", trim( testInfo.name ) ) + .writeAttribute( "description", testInfo.description ) + .writeAttribute( "tags", testInfo.tagsAsString ); + + writeSourceInfo( testInfo.lineInfo ); if ( m_config->showDurations() == ShowDurations::Always ) m_testCaseTimer.start(); + m_xml.ensureTagClosed(); } virtual void sectionStarting( SectionInfo const& sectionInfo ) CATCH_OVERRIDE { @@ -9318,77 +10408,84 @@ namespace Catch { m_xml.startElement( "Section" ) .writeAttribute( "name", trim( sectionInfo.name ) ) .writeAttribute( "description", sectionInfo.description ); + writeSourceInfo( sectionInfo.lineInfo ); + m_xml.ensureTagClosed(); } } virtual void assertionStarting( AssertionInfo const& ) CATCH_OVERRIDE { } virtual bool assertionEnded( AssertionStats const& assertionStats ) CATCH_OVERRIDE { - const AssertionResult& assertionResult = assertionStats.assertionResult; - // Print any info messages in tags. - if( assertionStats.assertionResult.getResultType() != ResultWas::Ok ) { + AssertionResult const& result = assertionStats.assertionResult; + + bool includeResults = m_config->includeSuccessfulResults() || !result.isOk(); + + if( includeResults || result.getResultType() == ResultWas::Warning ) { + // Print any info messages in tags. for( std::vector::const_iterator it = assertionStats.infoMessages.begin(), itEnd = assertionStats.infoMessages.end(); - it != itEnd; - ++it ) { - if( it->type == ResultWas::Info ) { + it != itEnd; + ++it ) { + if( it->type == ResultWas::Info && includeResults ) { m_xml.scopedElement( "Info" ) - .writeText( it->message ); + .writeText( it->message ); } else if ( it->type == ResultWas::Warning ) { m_xml.scopedElement( "Warning" ) - .writeText( it->message ); + .writeText( it->message ); } } } // Drop out if result was successful but we're not printing them. - if( !m_config->includeSuccessfulResults() && isOk(assertionResult.getResultType()) ) + if( !includeResults && result.getResultType() != ResultWas::Warning ) return true; // Print the expression if there is one. - if( assertionResult.hasExpression() ) { + if( result.hasExpression() ) { m_xml.startElement( "Expression" ) - .writeAttribute( "success", assertionResult.succeeded() ) - .writeAttribute( "type", assertionResult.getTestMacroName() ) - .writeAttribute( "filename", assertionResult.getSourceInfo().file ) - .writeAttribute( "line", assertionResult.getSourceInfo().line ); + .writeAttribute( "success", result.succeeded() ) + .writeAttribute( "type", result.getTestMacroName() ); + + writeSourceInfo( result.getSourceInfo() ); m_xml.scopedElement( "Original" ) - .writeText( assertionResult.getExpression() ); + .writeText( result.getExpression() ); m_xml.scopedElement( "Expanded" ) - .writeText( assertionResult.getExpandedExpression() ); + .writeText( result.getExpandedExpression() ); } // And... Print a result applicable to each result type. - switch( assertionResult.getResultType() ) { + switch( result.getResultType() ) { case ResultWas::ThrewException: - m_xml.scopedElement( "Exception" ) - .writeAttribute( "filename", assertionResult.getSourceInfo().file ) - .writeAttribute( "line", assertionResult.getSourceInfo().line ) - .writeText( assertionResult.getMessage() ); + m_xml.startElement( "Exception" ); + writeSourceInfo( result.getSourceInfo() ); + m_xml.writeText( result.getMessage() ); + m_xml.endElement(); break; case ResultWas::FatalErrorCondition: - m_xml.scopedElement( "FatalErrorCondition" ) - .writeAttribute( "filename", assertionResult.getSourceInfo().file ) - .writeAttribute( "line", assertionResult.getSourceInfo().line ) - .writeText( assertionResult.getMessage() ); + m_xml.startElement( "FatalErrorCondition" ); + writeSourceInfo( result.getSourceInfo() ); + m_xml.writeText( result.getMessage() ); + m_xml.endElement(); break; case ResultWas::Info: m_xml.scopedElement( "Info" ) - .writeText( assertionResult.getMessage() ); + .writeText( result.getMessage() ); break; case ResultWas::Warning: // Warning will already have been written break; case ResultWas::ExplicitFailure: - m_xml.scopedElement( "Failure" ) - .writeText( assertionResult.getMessage() ); + m_xml.startElement( "Failure" ); + writeSourceInfo( result.getSourceInfo() ); + m_xml.writeText( result.getMessage() ); + m_xml.endElement(); break; default: break; } - if( assertionResult.hasExpression() ) + if( result.hasExpression() ) m_xml.endElement(); return true; @@ -9417,6 +10514,11 @@ namespace Catch { if ( m_config->showDurations() == ShowDurations::Always ) e.writeAttribute( "durationInSeconds", m_testCaseTimer.getElapsedSeconds() ); + if( !testCaseStats.stdOut.empty() ) + m_xml.scopedElement( "StdOut" ).writeText( trim( testCaseStats.stdOut ), false ); + if( !testCaseStats.stdErr.empty() ) + m_xml.scopedElement( "StdErr" ).writeText( trim( testCaseStats.stdErr ), false ); + m_xml.endElement(); } @@ -9464,7 +10566,7 @@ namespace Catch { std::time(&rawtime); const size_t timeStampSize = sizeof("2017-01-16T17:06:45Z"); -#ifdef CATCH_PLATFORM_WINDOWS +#ifdef _MSC_VER std::tm timeInfo = {}; gmtime_s(&timeInfo, &rawtime); #else @@ -9475,7 +10577,7 @@ namespace Catch { char timeStamp[timeStampSize]; const char * const fmt = "%Y-%m-%dT%H:%M:%SZ"; -#ifdef CATCH_PLATFORM_WINDOWS +#ifdef _MSC_VER std::strftime(timeStamp, timeStampSize, fmt, &timeInfo); #else std::strftime(timeStamp, timeStampSize, fmt, timeInfo); @@ -9489,7 +10591,9 @@ namespace Catch { public: JunitReporter( ReporterConfig const& _config ) : CumulativeReporterBase( _config ), - xml( _config.stream() ) + xml( _config.stream() ), + unexpectedExceptions( 0 ), + m_okToFail( false ) { m_reporterPrefs.shouldRedirectStdOut = true; } @@ -9515,8 +10619,11 @@ namespace Catch { CumulativeReporterBase::testGroupStarting( groupInfo ); } + virtual void testCaseStarting( TestCaseInfo const& testCaseInfo ) CATCH_OVERRIDE { + m_okToFail = testCaseInfo.okToFail(); + } virtual bool assertionEnded( AssertionStats const& assertionStats ) CATCH_OVERRIDE { - if( assertionStats.assertionResult.getResultType() == ResultWas::ThrewException ) + if( assertionStats.assertionResult.getResultType() == ResultWas::ThrewException && !m_okToFail ) unexpectedExceptions++; return CumulativeReporterBase::assertionEnded( assertionStats ); } @@ -9584,7 +10691,7 @@ namespace Catch { SectionNode const& sectionNode ) { std::string name = trim( sectionNode.stats.sectionInfo.name ); if( !rootName.empty() ) - name = rootName + "/" + name; + name = rootName + '/' + name; if( !sectionNode.assertions.empty() || !sectionNode.stdOut.empty() || @@ -9662,14 +10769,14 @@ namespace Catch { std::ostringstream oss; if( !result.getMessage().empty() ) - oss << result.getMessage() << "\n"; + oss << result.getMessage() << '\n'; for( std::vector::const_iterator it = stats.infoMessages.begin(), itEnd = stats.infoMessages.end(); it != itEnd; ++it ) if( it->type == ResultWas::Info ) - oss << it->message << "\n"; + oss << it->message << '\n'; oss << "at " << result.getSourceInfo(); xml.writeText( oss.str(), false ); @@ -9681,6 +10788,7 @@ namespace Catch { std::ostringstream stdOutForSuite; std::ostringstream stdErrForSuite; unsigned int unexpectedExceptions; + bool m_okToFail; }; INTERNAL_CATCH_REGISTER_REPORTER( "junit", JunitReporter ) @@ -9690,6 +10798,10 @@ namespace Catch { // #included from: ../reporters/catch_reporter_console.hpp #define TWOBLUECUBES_CATCH_REPORTER_CONSOLE_HPP_INCLUDED +#include +#include +#include + namespace Catch { struct ConsoleReporter : StreamingReporterBase { @@ -9704,7 +10816,7 @@ namespace Catch { } virtual void noMatchingTestCases( std::string const& spec ) CATCH_OVERRIDE { - stream << "No test cases matched '" << spec << "'" << std::endl; + stream << "No test cases matched '" << spec << '\'' << std::endl; } virtual void assertionStarting( AssertionInfo const& ) CATCH_OVERRIDE { @@ -9713,18 +10825,15 @@ namespace Catch { virtual bool assertionEnded( AssertionStats const& _assertionStats ) CATCH_OVERRIDE { AssertionResult const& result = _assertionStats.assertionResult; - bool printInfoMessages = true; + bool includeResults = m_config->includeSuccessfulResults() || !result.isOk(); - // Drop out if result was successful and we're not printing those - if( !m_config->includeSuccessfulResults() && result.isOk() ) { - if( result.getResultType() != ResultWas::Warning ) - return false; - printInfoMessages = false; - } + // Drop out if result was successful but we're not printing them. + if( !includeResults && result.getResultType() != ResultWas::Warning ) + return false; lazyPrint(); - AssertionPrinter printer( stream, _assertionStats, printInfoMessages ); + AssertionPrinter printer( stream, _assertionStats, includeResults ); printer.print(); stream << std::endl; return true; @@ -9744,15 +10853,12 @@ namespace Catch { stream << "\nNo assertions in test case"; stream << " '" << _sectionStats.sectionInfo.name << "'\n" << std::endl; } + if( m_config->showDurations() == ShowDurations::Always ) { + stream << getFormattedDuration(_sectionStats.durationInSeconds) << " s: " << _sectionStats.sectionInfo.name << std::endl; + } if( m_headerPrinted ) { - if( m_config->showDurations() == ShowDurations::Always ) - stream << "Completed in " << _sectionStats.durationInSeconds << "s" << std::endl; m_headerPrinted = false; } - else { - if( m_config->showDurations() == ShowDurations::Always ) - stream << _sectionStats.sectionInfo.name << " completed in " << _sectionStats.durationInSeconds << "s" << std::endl; - } StreamingReporterBase::sectionEnded( _sectionStats ); } @@ -9765,7 +10871,7 @@ namespace Catch { printSummaryDivider(); stream << "Summary for group '" << _testGroupStats.groupInfo.name << "':\n"; printTotals( _testGroupStats.totals ); - stream << "\n" << std::endl; + stream << '\n' << std::endl; } StreamingReporterBase::testGroupEnded( _testGroupStats ); } @@ -9817,7 +10923,11 @@ namespace Catch { case ResultWas::ThrewException: colour = Colour::Error; passOrFail = "FAILED"; - messageLabel = "due to unexpected exception with message"; + messageLabel = "due to unexpected exception with "; + if (_stats.infoMessages.size() == 1) + messageLabel += "message"; + if (_stats.infoMessages.size() > 1) + messageLabel += "messages"; break; case ResultWas::FatalErrorCondition: colour = Colour::Error; @@ -9857,13 +10967,13 @@ namespace Catch { printSourceInfo(); if( stats.totals.assertions.total() > 0 ) { if( result.isOk() ) - stream << "\n"; + stream << '\n'; printResultType(); printOriginalExpression(); printReconstructedExpression(); } else { - stream << "\n"; + stream << '\n'; } printMessage(); } @@ -9880,25 +10990,25 @@ namespace Catch { Colour colourGuard( Colour::OriginalExpression ); stream << " "; stream << result.getExpressionInMacro(); - stream << "\n"; + stream << '\n'; } } void printReconstructedExpression() const { if( result.hasExpandedExpression() ) { stream << "with expansion:\n"; Colour colourGuard( Colour::ReconstructedExpression ); - stream << Text( result.getExpandedExpression(), TextAttributes().setIndent(2) ) << "\n"; + stream << Text( result.getExpandedExpression(), TextAttributes().setIndent(2) ) << '\n'; } } void printMessage() const { if( !messageLabel.empty() ) - stream << messageLabel << ":" << "\n"; + stream << messageLabel << ':' << '\n'; for( std::vector::const_iterator it = messages.begin(), itEnd = messages.end(); it != itEnd; ++it ) { // If this assertion is a warning ignore any INFO messages if( printInfoMessages || it->type != ResultWas::Info ) - stream << Text( it->message, TextAttributes().setIndent(2) ) << "\n"; + stream << Text( it->message, TextAttributes().setIndent(2) ) << '\n'; } } void printSourceInfo() const { @@ -9930,10 +11040,10 @@ namespace Catch { } } void lazyPrintRunInfo() { - stream << "\n" << getLineOfChars<'~'>() << "\n"; + stream << '\n' << getLineOfChars<'~'>() << '\n'; Colour colour( Colour::SecondaryText ); stream << currentTestRunInfo->name - << " is a Catch v" << libraryVersion << " host application.\n" + << " is a Catch v" << libraryVersion() << " host application.\n" << "Run with -? for options\n\n"; if( m_config->rngSeed() != 0 ) @@ -9961,22 +11071,22 @@ namespace Catch { printHeaderString( it->name, 2 ); } - SourceLineInfo lineInfo = m_sectionStack.front().lineInfo; + SourceLineInfo lineInfo = m_sectionStack.back().lineInfo; if( !lineInfo.empty() ){ - stream << getLineOfChars<'-'>() << "\n"; + stream << getLineOfChars<'-'>() << '\n'; Colour colourGuard( Colour::FileName ); - stream << lineInfo << "\n"; + stream << lineInfo << '\n'; } - stream << getLineOfChars<'.'>() << "\n" << std::endl; + stream << getLineOfChars<'.'>() << '\n' << std::endl; } void printClosedHeader( std::string const& _name ) { printOpenHeader( _name ); - stream << getLineOfChars<'.'>() << "\n"; + stream << getLineOfChars<'.'>() << '\n'; } void printOpenHeader( std::string const& _name ) { - stream << getLineOfChars<'-'>() << "\n"; + stream << getLineOfChars<'-'>() << '\n'; { Colour colourGuard( Colour::Headers ); printHeaderString( _name ); @@ -9993,7 +11103,7 @@ namespace Catch { i = 0; stream << Text( _string, TextAttributes() .setIndent( indent+i) - .setInitialIndent( indent ) ) << "\n"; + .setInitialIndent( indent ) ) << '\n'; } struct SummaryColumn { @@ -10008,9 +11118,9 @@ namespace Catch { std::string row = oss.str(); for( std::vector::iterator it = rows.begin(); it != rows.end(); ++it ) { while( it->size() < row.size() ) - *it = " " + *it; + *it = ' ' + *it; while( it->size() > row.size() ) - row = " " + row; + row = ' ' + row; } rows.push_back( row ); return *this; @@ -10030,8 +11140,8 @@ namespace Catch { stream << Colour( Colour::ResultSuccess ) << "All tests passed"; stream << " (" << pluralise( totals.assertions.passed, "assertion" ) << " in " - << pluralise( totals.testCases.passed, "test case" ) << ")" - << "\n"; + << pluralise( totals.testCases.passed, "test case" ) << ')' + << '\n'; } else { @@ -10066,10 +11176,10 @@ namespace Catch { else if( value != "0" ) { stream << Colour( Colour::LightGrey ) << " | "; stream << Colour( it->colour ) - << value << " " << it->label; + << value << ' ' << it->label; } } - stream << "\n"; + stream << '\n'; } static std::size_t makeRatio( std::size_t number, std::size_t total ) { @@ -10105,10 +11215,10 @@ namespace Catch { else { stream << Colour( Colour::Warning ) << std::string( CATCH_CONFIG_CONSOLE_WIDTH-1, '=' ); } - stream << "\n"; + stream << '\n'; } void printSummaryDivider() { - stream << getLineOfChars<'-'>() << "\n"; + stream << getLineOfChars<'-'>() << '\n'; } private: @@ -10143,11 +11253,10 @@ namespace Catch { } virtual void noMatchingTestCases( std::string const& spec ) { - stream << "No test cases matched '" << spec << "'" << std::endl; + stream << "No test cases matched '" << spec << '\'' << std::endl; } - virtual void assertionStarting( AssertionInfo const& ) { - } + virtual void assertionStarting( AssertionInfo const& ) {} virtual bool assertionEnded( AssertionStats const& _assertionStats ) { AssertionResult const& result = _assertionStats.assertionResult; @@ -10168,9 +11277,15 @@ namespace Catch { return true; } + virtual void sectionEnded(SectionStats const& _sectionStats) CATCH_OVERRIDE { + if (m_config->showDurations() == ShowDurations::Always) { + stream << getFormattedDuration(_sectionStats.durationInSeconds) << " s: " << _sectionStats.sectionInfo.name << std::endl; + } + } + virtual void testRunEnded( TestRunStats const& _testRunStats ) { printTotals( _testRunStats.totals ); - stream << "\n" << std::endl; + stream << '\n' << std::endl; StreamingReporterBase::testRunEnded( _testRunStats ); } @@ -10270,26 +11385,26 @@ namespace Catch { void printSourceInfo() const { Colour colourGuard( Colour::FileName ); - stream << result.getSourceInfo() << ":"; + stream << result.getSourceInfo() << ':'; } - void printResultType( Colour::Code colour, std::string passOrFail ) const { + void printResultType( Colour::Code colour, std::string const& passOrFail ) const { if( !passOrFail.empty() ) { { Colour colourGuard( colour ); - stream << " " << passOrFail; + stream << ' ' << passOrFail; } - stream << ":"; + stream << ':'; } } - void printIssue( std::string issue ) const { - stream << " " << issue; + void printIssue( std::string const& issue ) const { + stream << ' ' << issue; } void printExpressionWas() { if( result.hasExpression() ) { - stream << ";"; + stream << ';'; { Colour colour( dimColour() ); stream << " expression was:"; @@ -10300,7 +11415,7 @@ namespace Catch { void printOriginalExpression() const { if( result.hasExpression() ) { - stream << " " << result.getExpression(); + stream << ' ' << result.getExpression(); } } @@ -10316,7 +11431,7 @@ namespace Catch { void printMessage() { if ( itMessage != messages.end() ) { - stream << " '" << itMessage->message << "'"; + stream << " '" << itMessage->message << '\''; ++itMessage; } } @@ -10331,13 +11446,13 @@ namespace Catch { { Colour colourGuard( colour ); - stream << " with " << pluralise( N, "message" ) << ":"; + stream << " with " << pluralise( N, "message" ) << ':'; } for(; itMessage != itEnd; ) { // If this assertion is a warning ignore any INFO messages if( printInfoMessages || itMessage->type != ResultWas::Info ) { - stream << " '" << itMessage->message << "'"; + stream << " '" << itMessage->message << '\''; if ( ++itMessage != itEnd ) { Colour colourGuard( dimColour() ); stream << " and"; @@ -10363,7 +11478,7 @@ namespace Catch { // - green: Passed [both/all] N tests cases with M assertions. std::string bothOrAll( std::size_t count ) const { - return count == 1 ? "" : count == 2 ? "both " : "all " ; + return count == 1 ? std::string() : count == 2 ? "both " : "all " ; } void printTotals( const Totals& totals ) const { @@ -10374,12 +11489,12 @@ namespace Catch { Colour colour( Colour::ResultError ); const std::string qualify_assertions_failed = totals.assertions.failed == totals.assertions.total() ? - bothOrAll( totals.assertions.failed ) : ""; + bothOrAll( totals.assertions.failed ) : std::string(); stream << "Failed " << bothOrAll( totals.testCases.failed ) << pluralise( totals.testCases.failed, "test case" ) << ", " "failed " << qualify_assertions_failed << - pluralise( totals.assertions.failed, "assertion" ) << "."; + pluralise( totals.assertions.failed, "assertion" ) << '.'; } else if( totals.assertions.total() == 0 ) { stream << @@ -10391,14 +11506,14 @@ namespace Catch { Colour colour( Colour::ResultError ); stream << "Failed " << pluralise( totals.testCases.failed, "test case" ) << ", " - "failed " << pluralise( totals.assertions.failed, "assertion" ) << "."; + "failed " << pluralise( totals.assertions.failed, "assertion" ) << '.'; } else { Colour colour( Colour::ResultSuccess ); stream << "Passed " << bothOrAll( totals.testCases.passed ) << pluralise( totals.testCases.passed, "test case" ) << - " with " << pluralise( totals.assertions.passed, "assertion" ) << "."; + " with " << pluralise( totals.assertions.passed, "assertion" ) << '.'; } } }; @@ -10454,11 +11569,7 @@ namespace Catch { TestSpec::NamePattern::~NamePattern() {} TestSpec::TagPattern::~TagPattern() {} TestSpec::ExcludedPattern::~ExcludedPattern() {} - - Matchers::Impl::StdString::Equals::~Equals() {} - Matchers::Impl::StdString::Contains::~Contains() {} - Matchers::Impl::StdString::StartsWith::~StartsWith() {} - Matchers::Impl::StdString::EndsWith::~EndsWith() {} + Matchers::Impl::MatcherUntypedBase::~MatcherUntypedBase() {} void Config::dummy() {} @@ -10482,9 +11593,16 @@ namespace Catch { #ifndef __OBJC__ +#if defined(WIN32) && defined(_UNICODE) && !defined(DO_NOT_USE_WMAIN) +// Standard C/C++ Win32 Unicode wmain entry point +extern "C" int wmain (int argc, wchar_t * argv[], wchar_t * []) { +#else // Standard C/C++ main entry point int main (int argc, char * argv[]) { - return Catch::Session().run( argc, argv ); +#endif + + int result = Catch::Session().run( argc, argv ); + return ( result < 0xff ? result : 0xff ); } #else // __OBJC__ @@ -10502,7 +11620,7 @@ int main (int argc, char * const argv[]) { [pool drain]; #endif - return result; + return ( result < 0xff ? result : 0xff ); } #endif // __OBJC__ @@ -10518,33 +11636,43 @@ int main (int argc, char * const argv[]) { // If this config identifier is defined then all CATCH macros are prefixed with CATCH_ #ifdef CATCH_CONFIG_PREFIX_ALL -#define CATCH_REQUIRE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal, "CATCH_REQUIRE" ) -#define CATCH_REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, "CATCH_REQUIRE_FALSE" ) +#if defined(CATCH_CONFIG_FAST_COMPILE) +#define CATCH_REQUIRE( expr ) INTERNAL_CATCH_TEST_NO_TRY( "CATCH_REQUIRE", Catch::ResultDisposition::Normal, expr ) +#define CATCH_REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST_NO_TRY( "CATCH_REQUIRE_FALSE", Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, expr ) +#else +#define CATCH_REQUIRE( expr ) INTERNAL_CATCH_TEST( "CATCH_REQUIRE", Catch::ResultDisposition::Normal, expr ) +#define CATCH_REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST( "CATCH_REQUIRE_FALSE", Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, expr ) +#endif -#define CATCH_REQUIRE_THROWS( expr ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::Normal, "", "CATCH_REQUIRE_THROWS" ) -#define CATCH_REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::Normal, "CATCH_REQUIRE_THROWS_AS" ) -#define CATCH_REQUIRE_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::Normal, matcher, "CATCH_REQUIRE_THROWS_WITH" ) -#define CATCH_REQUIRE_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::Normal, "CATCH_REQUIRE_NOTHROW" ) +#define CATCH_REQUIRE_THROWS( expr ) INTERNAL_CATCH_THROWS( "CATCH_REQUIRE_THROWS", Catch::ResultDisposition::Normal, "", expr ) +#define CATCH_REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "CATCH_REQUIRE_THROWS_AS", exceptionType, Catch::ResultDisposition::Normal, expr ) +#define CATCH_REQUIRE_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( "CATCH_REQUIRE_THROWS_WITH", Catch::ResultDisposition::Normal, matcher, expr ) +#define CATCH_REQUIRE_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( "CATCH_REQUIRE_NOTHROW", Catch::ResultDisposition::Normal, expr ) -#define CATCH_CHECK( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK" ) -#define CATCH_CHECK_FALSE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, "CATCH_CHECK_FALSE" ) -#define CATCH_CHECKED_IF( expr ) INTERNAL_CATCH_IF( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECKED_IF" ) -#define CATCH_CHECKED_ELSE( expr ) INTERNAL_CATCH_ELSE( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECKED_ELSE" ) -#define CATCH_CHECK_NOFAIL( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, "CATCH_CHECK_NOFAIL" ) +#define CATCH_CHECK( expr ) INTERNAL_CATCH_TEST( "CATCH_CHECK", Catch::ResultDisposition::ContinueOnFailure, expr ) +#define CATCH_CHECK_FALSE( expr ) INTERNAL_CATCH_TEST( "CATCH_CHECK_FALSE", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, expr ) +#define CATCH_CHECKED_IF( expr ) INTERNAL_CATCH_IF( "CATCH_CHECKED_IF", Catch::ResultDisposition::ContinueOnFailure, expr ) +#define CATCH_CHECKED_ELSE( expr ) INTERNAL_CATCH_ELSE( "CATCH_CHECKED_ELSE", Catch::ResultDisposition::ContinueOnFailure, expr ) +#define CATCH_CHECK_NOFAIL( expr ) INTERNAL_CATCH_TEST( "CATCH_CHECK_NOFAIL", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, expr ) -#define CATCH_CHECK_THROWS( expr ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::ContinueOnFailure, "", "CATCH_CHECK_THROWS" ) -#define CATCH_CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_THROWS_AS" ) -#define CATCH_CHECK_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::ContinueOnFailure, matcher, "CATCH_CHECK_THROWS_WITH" ) -#define CATCH_CHECK_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_NOTHROW" ) +#define CATCH_CHECK_THROWS( expr ) INTERNAL_CATCH_THROWS( "CATCH_CHECK_THROWS", Catch::ResultDisposition::ContinueOnFailure, "", expr ) +#define CATCH_CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "CATCH_CHECK_THROWS_AS", exceptionType, Catch::ResultDisposition::ContinueOnFailure, expr ) +#define CATCH_CHECK_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( "CATCH_CHECK_THROWS_WITH", Catch::ResultDisposition::ContinueOnFailure, matcher, expr ) +#define CATCH_CHECK_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( "CATCH_CHECK_NOTHROW", Catch::ResultDisposition::ContinueOnFailure, expr ) -#define CATCH_CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_THAT" ) -#define CATCH_REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::Normal, "CATCH_REQUIRE_THAT" ) +#define CATCH_CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "CATCH_CHECK_THAT", matcher, Catch::ResultDisposition::ContinueOnFailure, arg ) + +#if defined(CATCH_CONFIG_FAST_COMPILE) +#define CATCH_REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT_NO_TRY( "CATCH_REQUIRE_THAT", matcher, Catch::ResultDisposition::Normal, arg ) +#else +#define CATCH_REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "CATCH_REQUIRE_THAT", matcher, Catch::ResultDisposition::Normal, arg ) +#endif -#define CATCH_INFO( msg ) INTERNAL_CATCH_INFO( msg, "CATCH_INFO" ) -#define CATCH_WARN( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, "CATCH_WARN", msg ) -#define CATCH_SCOPED_INFO( msg ) INTERNAL_CATCH_INFO( msg, "CATCH_INFO" ) -#define CATCH_CAPTURE( msg ) INTERNAL_CATCH_INFO( #msg " := " << msg, "CATCH_CAPTURE" ) -#define CATCH_SCOPED_CAPTURE( msg ) INTERNAL_CATCH_INFO( #msg " := " << msg, "CATCH_CAPTURE" ) +#define CATCH_INFO( msg ) INTERNAL_CATCH_INFO( "CATCH_INFO", msg ) +#define CATCH_WARN( msg ) INTERNAL_CATCH_MSG( "CATCH_WARN", Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, msg ) +#define CATCH_SCOPED_INFO( msg ) INTERNAL_CATCH_INFO( "CATCH_INFO", msg ) +#define CATCH_CAPTURE( msg ) INTERNAL_CATCH_INFO( "CATCH_CAPTURE", #msg " := " << Catch::toString(msg) ) +#define CATCH_SCOPED_CAPTURE( msg ) INTERNAL_CATCH_INFO( "CATCH_CAPTURE", #msg " := " << Catch::toString(msg) ) #ifdef CATCH_CONFIG_VARIADIC_MACROS #define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ ) @@ -10552,16 +11680,18 @@ int main (int argc, char * const argv[]) { #define CATCH_METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ ) #define CATCH_REGISTER_TEST_CASE( Function, ... ) INTERNAL_CATCH_REGISTER_TESTCASE( Function, __VA_ARGS__ ) #define CATCH_SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ ) - #define CATCH_FAIL( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "CATCH_FAIL", __VA_ARGS__ ) - #define CATCH_SUCCEED( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "CATCH_SUCCEED", __VA_ARGS__ ) + #define CATCH_FAIL( ... ) INTERNAL_CATCH_MSG( "CATCH_FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, __VA_ARGS__ ) + #define CATCH_FAIL_CHECK( ... ) INTERNAL_CATCH_MSG( "CATCH_FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) + #define CATCH_SUCCEED( ... ) INTERNAL_CATCH_MSG( "CATCH_SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) #else #define CATCH_TEST_CASE( name, description ) INTERNAL_CATCH_TESTCASE( name, description ) #define CATCH_TEST_CASE_METHOD( className, name, description ) INTERNAL_CATCH_TEST_CASE_METHOD( className, name, description ) #define CATCH_METHOD_AS_TEST_CASE( method, name, description ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, name, description ) #define CATCH_REGISTER_TEST_CASE( function, name, description ) INTERNAL_CATCH_REGISTER_TESTCASE( function, name, description ) #define CATCH_SECTION( name, description ) INTERNAL_CATCH_SECTION( name, description ) - #define CATCH_FAIL( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "CATCH_FAIL", msg ) - #define CATCH_SUCCEED( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "CATCH_SUCCEED", msg ) + #define CATCH_FAIL( msg ) INTERNAL_CATCH_MSG( "CATCH_FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, msg ) + #define CATCH_FAIL_CHECK( msg ) INTERNAL_CATCH_MSG( "CATCH_FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, msg ) + #define CATCH_SUCCEED( msg ) INTERNAL_CATCH_MSG( "CATCH_SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, msg ) #endif #define CATCH_ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE( "", "" ) @@ -10587,50 +11717,63 @@ int main (int argc, char * const argv[]) { // If CATCH_CONFIG_PREFIX_ALL is not defined then the CATCH_ prefix is not required #else -#define REQUIRE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal, "REQUIRE" ) -#define REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, "REQUIRE_FALSE" ) +#if defined(CATCH_CONFIG_FAST_COMPILE) +#define REQUIRE( expr ) INTERNAL_CATCH_TEST_NO_TRY( "REQUIRE", Catch::ResultDisposition::Normal, expr ) +#define REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST_NO_TRY( "REQUIRE_FALSE", Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, expr ) + +#else +#define REQUIRE( expr ) INTERNAL_CATCH_TEST( "REQUIRE", Catch::ResultDisposition::Normal, expr ) +#define REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST( "REQUIRE_FALSE", Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, expr ) +#endif + +#define REQUIRE_THROWS( expr ) INTERNAL_CATCH_THROWS( "REQUIRE_THROWS", Catch::ResultDisposition::Normal, "", expr ) +#define REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "REQUIRE_THROWS_AS", exceptionType, Catch::ResultDisposition::Normal, expr ) +#define REQUIRE_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( "REQUIRE_THROWS_WITH", Catch::ResultDisposition::Normal, matcher, expr ) +#define REQUIRE_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( "REQUIRE_NOTHROW", Catch::ResultDisposition::Normal, expr ) -#define REQUIRE_THROWS( expr ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::Normal, "", "REQUIRE_THROWS" ) -#define REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::Normal, "REQUIRE_THROWS_AS" ) -#define REQUIRE_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::Normal, matcher, "REQUIRE_THROWS_WITH" ) -#define REQUIRE_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::Normal, "REQUIRE_NOTHROW" ) +#define CHECK( expr ) INTERNAL_CATCH_TEST( "CHECK", Catch::ResultDisposition::ContinueOnFailure, expr ) +#define CHECK_FALSE( expr ) INTERNAL_CATCH_TEST( "CHECK_FALSE", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, expr ) +#define CHECKED_IF( expr ) INTERNAL_CATCH_IF( "CHECKED_IF", Catch::ResultDisposition::ContinueOnFailure, expr ) +#define CHECKED_ELSE( expr ) INTERNAL_CATCH_ELSE( "CHECKED_ELSE", Catch::ResultDisposition::ContinueOnFailure, expr ) +#define CHECK_NOFAIL( expr ) INTERNAL_CATCH_TEST( "CHECK_NOFAIL", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, expr ) -#define CHECK( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECK" ) -#define CHECK_FALSE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, "CHECK_FALSE" ) -#define CHECKED_IF( expr ) INTERNAL_CATCH_IF( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECKED_IF" ) -#define CHECKED_ELSE( expr ) INTERNAL_CATCH_ELSE( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECKED_ELSE" ) -#define CHECK_NOFAIL( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, "CHECK_NOFAIL" ) +#define CHECK_THROWS( expr ) INTERNAL_CATCH_THROWS( "CHECK_THROWS", Catch::ResultDisposition::ContinueOnFailure, "", expr ) +#define CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "CHECK_THROWS_AS", exceptionType, Catch::ResultDisposition::ContinueOnFailure, expr ) +#define CHECK_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( "CHECK_THROWS_WITH", Catch::ResultDisposition::ContinueOnFailure, matcher, expr ) +#define CHECK_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( "CHECK_NOTHROW", Catch::ResultDisposition::ContinueOnFailure, expr ) -#define CHECK_THROWS( expr ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::ContinueOnFailure, "", "CHECK_THROWS" ) -#define CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::ContinueOnFailure, "CHECK_THROWS_AS" ) -#define CHECK_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::ContinueOnFailure, matcher, "CHECK_THROWS_WITH" ) -#define CHECK_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECK_NOTHROW" ) +#define CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "CHECK_THAT", matcher, Catch::ResultDisposition::ContinueOnFailure, arg ) -#define CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::ContinueOnFailure, "CHECK_THAT" ) -#define REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::Normal, "REQUIRE_THAT" ) +#if defined(CATCH_CONFIG_FAST_COMPILE) +#define REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT_NO_TRY( "REQUIRE_THAT", matcher, Catch::ResultDisposition::Normal, arg ) +#else +#define REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "REQUIRE_THAT", matcher, Catch::ResultDisposition::Normal, arg ) +#endif -#define INFO( msg ) INTERNAL_CATCH_INFO( msg, "INFO" ) -#define WARN( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, "WARN", msg ) -#define SCOPED_INFO( msg ) INTERNAL_CATCH_INFO( msg, "INFO" ) -#define CAPTURE( msg ) INTERNAL_CATCH_INFO( #msg " := " << msg, "CAPTURE" ) -#define SCOPED_CAPTURE( msg ) INTERNAL_CATCH_INFO( #msg " := " << msg, "CAPTURE" ) +#define INFO( msg ) INTERNAL_CATCH_INFO( "INFO", msg ) +#define WARN( msg ) INTERNAL_CATCH_MSG( "WARN", Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, msg ) +#define SCOPED_INFO( msg ) INTERNAL_CATCH_INFO( "INFO", msg ) +#define CAPTURE( msg ) INTERNAL_CATCH_INFO( "CAPTURE", #msg " := " << Catch::toString(msg) ) +#define SCOPED_CAPTURE( msg ) INTERNAL_CATCH_INFO( "CAPTURE", #msg " := " << Catch::toString(msg) ) #ifdef CATCH_CONFIG_VARIADIC_MACROS - #define TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ ) - #define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ ) - #define METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ ) - #define REGISTER_TEST_CASE( Function, ... ) INTERNAL_CATCH_REGISTER_TESTCASE( Function, __VA_ARGS__ ) - #define SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ ) - #define FAIL( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "FAIL", __VA_ARGS__ ) - #define SUCCEED( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "SUCCEED", __VA_ARGS__ ) +#define TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ ) +#define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ ) +#define METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ ) +#define REGISTER_TEST_CASE( Function, ... ) INTERNAL_CATCH_REGISTER_TESTCASE( Function, __VA_ARGS__ ) +#define SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ ) +#define FAIL( ... ) INTERNAL_CATCH_MSG( "FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, __VA_ARGS__ ) +#define FAIL_CHECK( ... ) INTERNAL_CATCH_MSG( "FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) +#define SUCCEED( ... ) INTERNAL_CATCH_MSG( "SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) #else - #define TEST_CASE( name, description ) INTERNAL_CATCH_TESTCASE( name, description ) +#define TEST_CASE( name, description ) INTERNAL_CATCH_TESTCASE( name, description ) #define TEST_CASE_METHOD( className, name, description ) INTERNAL_CATCH_TEST_CASE_METHOD( className, name, description ) #define METHOD_AS_TEST_CASE( method, name, description ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, name, description ) #define REGISTER_TEST_CASE( method, name, description ) INTERNAL_CATCH_REGISTER_TESTCASE( method, name, description ) #define SECTION( name, description ) INTERNAL_CATCH_SECTION( name, description ) - #define FAIL( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "FAIL", msg ) - #define SUCCEED( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "SUCCEED", msg ) + #define FAIL( msg ) INTERNAL_CATCH_MSG( "FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, msg ) + #define FAIL_CHECK( msg ) INTERNAL_CATCH_MSG( "FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, msg ) + #define SUCCEED( msg ) INTERNAL_CATCH_MSG( "SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, msg ) #endif #define ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE( "", "" ) @@ -10659,5 +11802,19 @@ int main (int argc, char * const argv[]) { using Catch::Detail::Approx; +// #included from: internal/catch_reenable_warnings.h + +#define TWOBLUECUBES_CATCH_REENABLE_WARNINGS_H_INCLUDED + +#ifdef __clang__ +# ifdef __ICC // icpc defines the __clang__ macro +# pragma warning(pop) +# else +# pragma clang diagnostic pop +# endif +#elif defined __GNUC__ +# pragma GCC diagnostic pop +#endif + #endif // TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED From 1b9a9003bdeb52b4d9462b775182618da5cec27f Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Mon, 20 Jan 2025 23:01:58 +0100 Subject: [PATCH 251/256] Corrections for some tests possibly due to Catch update --- tests/core/tests_arithmetic.cpp | 10 +- tests/core/tests_cn.cpp | 6 +- tests/core/tests_codac2_intervalvector.cpp | 2 +- tests/core/tests_ctc_eval.cpp | 2 +- tests/core/tests_operators.cpp | 6 +- tests/core/tests_polygons.cpp | 200 ++++++++++----------- tests/core/tests_serialization.cpp | 4 +- tests/core/tests_trajectory.cpp | 12 +- 8 files changed, 121 insertions(+), 121 deletions(-) diff --git a/tests/core/tests_arithmetic.cpp b/tests/core/tests_arithmetic.cpp index 79c742aa0..e47d52e3a 100644 --- a/tests/core/tests_arithmetic.cpp +++ b/tests/core/tests_arithmetic.cpp @@ -696,7 +696,7 @@ TEST_CASE("Arithmetic on trajs") Interval domain(0.,10.); TrajectoryVector trajx(3), trajy(3), trajz(3); - Vector vx(3), vy(3); + codac::Vector vx(3), vy(3); vx[0] = 1.; vx[1] = 2.; vx[2] = 3.; vy[0] = 10.; vy[1] = 20.; vy[2] = 30.; @@ -752,11 +752,11 @@ TEST_CASE("Arithmetic on trajs") //const Trajectory& operator+=(double x); trajz = trajx; trajz += vy[1]; - CHECK(ApproxIntvVector(trajz.codomain()) == IntervalVector(vx+Vector(3,vy[1]))); + CHECK(ApproxIntvVector(trajz.codomain()) == IntervalVector(vx+codac::Vector(3,vy[1]))); //const Trajectory& operator+=(const Trajectory& x); trajz = trajx; trajz += trajy[1]; - CHECK(ApproxIntvVector(trajz.codomain()) == IntervalVector(vx+Vector(3,vy[1]))); + CHECK(ApproxIntvVector(trajz.codomain()) == IntervalVector(vx+codac::Vector(3,vy[1]))); //const TrajectoryVector& operator+=(const Vector& x); trajz = trajx; trajz += vy; @@ -768,11 +768,11 @@ TEST_CASE("Arithmetic on trajs") //const Trajectory& operator-=(double x); trajz = trajx; trajz -= vy[1]; - CHECK(ApproxIntvVector(trajz.codomain()) == IntervalVector(vx-Vector(3,vy[1]))); + CHECK(ApproxIntvVector(trajz.codomain()) == IntervalVector(vx-codac::Vector(3,vy[1]))); //const Trajectory& operator-=(const Trajectory& x); trajz = trajx; trajz -= trajy[1]; - CHECK(ApproxIntvVector(trajz.codomain()) == IntervalVector(vx-Vector(3,vy[1]))); + CHECK(ApproxIntvVector(trajz.codomain()) == IntervalVector(vx-codac::Vector(3,vy[1]))); //const TrajectoryVector& operator-=(const Vector& x); trajz = trajx; trajz -= vy; diff --git a/tests/core/tests_cn.cpp b/tests/core/tests_cn.cpp index 81f370361..aabec4271 100644 --- a/tests/core/tests_cn.cpp +++ b/tests/core/tests_cn.cpp @@ -206,7 +206,7 @@ TEST_CASE("CN simple") { Interval x(0,1), y(-2,3), a(1,20); IntervalVector vx(2,x), vy(2,y), va(2,a); - Vector vec(4,0.5); + codac::Vector vec(4,0.5); ContractorNetwork cn; cn.add(ctc_add, {vx,vy,va}); @@ -295,7 +295,7 @@ TEST_CASE("CN simple") { Interval x(0,1), a(1,20); - Vector vector_y(2, 1.); + codac::Vector vector_y(2, 1.); IntervalVector ivx(2,x), iva(2,a); ContractorNetwork cn; @@ -306,7 +306,7 @@ TEST_CASE("CN simple") cn.contract(); CHECK(ivx == IntervalVector(2,Interval(0,1))); - CHECK(vector_y == Vector(2,1.)); + CHECK(vector_y == codac::Vector(2,1.)); CHECK(iva == IntervalVector(2,Interval(1,2))); // todo: reactivate this test: CHECK(cn.nb_dom() == 3*3); CHECK(cn.nb_ctc() == 3+2); diff --git a/tests/core/tests_codac2_intervalvector.cpp b/tests/core/tests_codac2_intervalvector.cpp index 965586a96..865738abf 100644 --- a/tests/core/tests_codac2_intervalvector.cpp +++ b/tests/core/tests_codac2_intervalvector.cpp @@ -300,7 +300,7 @@ TEST_CASE("Tests from IBEX IntervalVector") SECTION("mid01") { IntervalVector x(3,_x); - Vector m=x.mid(); + codac2::Vector m=x.mid(); CHECK(m[0]==0.5); CHECK(m[1]==2.5); CHECK(m[2]==4.5); diff --git a/tests/core/tests_ctc_eval.cpp b/tests/core/tests_ctc_eval.cpp index dd719781e..1e4f0c6ba 100644 --- a/tests/core/tests_ctc_eval.cpp +++ b/tests/core/tests_ctc_eval.cpp @@ -1334,7 +1334,7 @@ TEST_CASE("CtcEval (other tests)") TubeVector x(tdomain, dt, TFunction("(sin(t) ; -sin(t))")); CtcEval ctc_eval; ctc_eval.contract(t, b, x); - CHECK(b.contains(Vector(2,0.))); + CHECK(b.contains(codac::Vector(2,0.))); CHECK(b.max_diam() < 0.02); CHECK(t == Interval(x[0].slice(0.)->tdomain().lb(),x[0].slice(3.*M_PI)->tdomain().ub())); } diff --git a/tests/core/tests_operators.cpp b/tests/core/tests_operators.cpp index e14c1c8b9..fe1873a9f 100644 --- a/tests/core/tests_operators.cpp +++ b/tests/core/tests_operators.cpp @@ -74,7 +74,7 @@ TEST_CASE("Operators") tube1.set(IntervalVector(2, Interval(-1.,1.))); tube1 += TrajectoryVector(domain, TFunction("(2.;2.)")); TrajectoryVector traj(domain, TFunction("(2.;2.)")); - CHECK(traj(0.) == Vector(2, 2.)); + CHECK(traj(0.) == codac::Vector(2, 2.)); CHECK(tube1.codomain() == IntervalVector(2, Interval(1.,3.))); CHECK(tube1(0.) == IntervalVector(2, Interval(1.,3.))); @@ -274,13 +274,13 @@ TEST_CASE("Operators") tube1.set(IntervalVector(2, Interval(-1.,1.))); result = tube1 + TrajectoryVector(domain, TFunction("(2.;2.)")); TrajectoryVector traj(domain, TFunction("(2.;2.)")); - CHECK(traj(0.) == Vector(2, 2.)); + CHECK(traj(0.) == codac::Vector(2, 2.)); CHECK(result.codomain() == IntervalVector(2, Interval(1.,3.))); CHECK(result(0.) == IntervalVector(2, Interval(1.,3.))); tube1.set(IntervalVector(2, Interval(-1.,1.))); result = TrajectoryVector(domain, TFunction("(2.;2.)")) + tube1; - CHECK(traj(0.) == Vector(2, 2.)); + CHECK(traj(0.) == codac::Vector(2, 2.)); CHECK(result.codomain() == IntervalVector(2, Interval(1.,3.))); CHECK(result(0.) == IntervalVector(2, Interval(1.,3.))); diff --git a/tests/core/tests_polygons.cpp b/tests/core/tests_polygons.cpp index 8043dd22c..0befa6c22 100644 --- a/tests/core/tests_polygons.cpp +++ b/tests/core/tests_polygons.cpp @@ -29,10 +29,10 @@ TEST_CASE("Polygons") ConvexPolygon p(iv); CHECK(p.nb_vertices() == 4); CHECK(p.box() == iv); - CHECK(p[0] == Vector({-1.,10.})); - CHECK(p[1] == Vector({5.,10.})); - CHECK(p[2] == Vector({5.,11.})); - CHECK(p[3] == Vector({-1.,11.})); + CHECK(p[0] == codac::Vector({-1.,10.})); + CHECK(p[1] == codac::Vector({5.,10.})); + CHECK(p[2] == codac::Vector({5.,11.})); + CHECK(p[3] == codac::Vector({-1.,11.})); } SECTION("Polygon from IntervalVector (unbounded case)") @@ -919,49 +919,49 @@ TEST_CASE("Polygons (Graham scan)") SECTION("Polygons, Graham scan") { - vector v_pts; - v_pts.push_back(Vector({0.,3.})); - v_pts.push_back(Vector({1.,1.})); - v_pts.push_back(Vector({2.,2.})); - v_pts.push_back(Vector({4.,4.})); - v_pts.push_back(Vector({0.,0.})); - v_pts.push_back(Vector({1.,2.})); - v_pts.push_back(Vector({3.,1.})); - v_pts.push_back(Vector({3.,3.})); + vector v_pts; + v_pts.push_back(codac::Vector({0.,3.})); + v_pts.push_back(codac::Vector({1.,1.})); + v_pts.push_back(codac::Vector({2.,2.})); + v_pts.push_back(codac::Vector({4.,4.})); + v_pts.push_back(codac::Vector({0.,0.})); + v_pts.push_back(codac::Vector({1.,2.})); + v_pts.push_back(codac::Vector({3.,1.})); + v_pts.push_back(codac::Vector({3.,3.})); ConvexPolygon hull = GrahamScan::convex_hull(v_pts); - CHECK(hull.vertices()[0] == Vector({0.,0.})); - CHECK(hull.vertices()[1] == Vector({3.,1.})); - CHECK(hull.vertices()[2] == Vector({4.,4.})); - CHECK(hull.vertices()[3] == Vector({0.,3.})); + CHECK(hull.vertices()[0] == codac::Vector({0.,0.})); + CHECK(hull.vertices()[1] == codac::Vector({3.,1.})); + CHECK(hull.vertices()[2] == codac::Vector({4.,4.})); + CHECK(hull.vertices()[3] == codac::Vector({0.,3.})); v_pts.clear(); - v_pts.push_back(Vector({1.,3.})); - v_pts.push_back(Vector({1.,4.})); - v_pts.push_back(Vector({1.5,2.})); - v_pts.push_back(Vector({2.,1.})); - v_pts.push_back(Vector({2.,2.})); - v_pts.push_back(Vector({3.,0.})); - v_pts.push_back(Vector({3.,3.})); - v_pts.push_back(Vector({3.,4.5})); - v_pts.push_back(Vector({4.,2.5})); - v_pts.push_back(Vector({4.,4.})); - v_pts.push_back(Vector({5.,1.})); - v_pts.push_back(Vector({5.,2.})); - v_pts.push_back(Vector({4.,0.})); - v_pts.push_back(Vector({5.,0.})); - v_pts.push_back(Vector({5.,5.})); - v_pts.push_back(Vector({6.,0.})); - v_pts.push_back(Vector({7.,2.})); + v_pts.push_back(codac::Vector({1.,3.})); + v_pts.push_back(codac::Vector({1.,4.})); + v_pts.push_back(codac::Vector({1.5,2.})); + v_pts.push_back(codac::Vector({2.,1.})); + v_pts.push_back(codac::Vector({2.,2.})); + v_pts.push_back(codac::Vector({3.,0.})); + v_pts.push_back(codac::Vector({3.,3.})); + v_pts.push_back(codac::Vector({3.,4.5})); + v_pts.push_back(codac::Vector({4.,2.5})); + v_pts.push_back(codac::Vector({4.,4.})); + v_pts.push_back(codac::Vector({5.,1.})); + v_pts.push_back(codac::Vector({5.,2.})); + v_pts.push_back(codac::Vector({4.,0.})); + v_pts.push_back(codac::Vector({5.,0.})); + v_pts.push_back(codac::Vector({5.,5.})); + v_pts.push_back(codac::Vector({6.,0.})); + v_pts.push_back(codac::Vector({7.,2.})); hull = GrahamScan::convex_hull(v_pts); - CHECK(hull.vertices()[0] == Vector({3.,0.})); - CHECK(hull.vertices()[1] == Vector({6.,0.})); - CHECK(hull.vertices()[2] == Vector({7.,2.})); - CHECK(hull.vertices()[3] == Vector({5.,5.})); - CHECK(hull.vertices()[4] == Vector({3.,4.5})); - CHECK(hull.vertices()[5] == Vector({1.,4.})); - CHECK(hull.vertices()[6] == Vector({1.,3.})); + CHECK(hull.vertices()[0] == codac::Vector({3.,0.})); + CHECK(hull.vertices()[1] == codac::Vector({6.,0.})); + CHECK(hull.vertices()[2] == codac::Vector({7.,2.})); + CHECK(hull.vertices()[3] == codac::Vector({5.,5.})); + CHECK(hull.vertices()[4] == codac::Vector({3.,4.5})); + CHECK(hull.vertices()[5] == codac::Vector({1.,4.})); + CHECK(hull.vertices()[6] == codac::Vector({1.,3.})); //vibes::beginDrawing(); //VIBesFig fig("poly"); @@ -1021,7 +1021,7 @@ TEST_CASE("Polygons (Graham scan)") // vibes::endDrawing(); //#endif - vector v_pts = p.vertices(); + vector v_pts = p.vertices(); bool contains = true; for(const auto& pt : v_pts) @@ -1040,7 +1040,7 @@ TEST_CASE("Polygons (operations)") v_pts.push_back(ThickPoint(2.,4.)); ConvexPolygon p(v_pts); - p.rotate(-M_PI/2., Vector({3.,1.})); + p.rotate(-M_PI/2., codac::Vector({3.,1.})); // Rotated polygon truth vector v_pts_rot_truth; @@ -1103,22 +1103,22 @@ TEST_CASE("Polygons (Graham scan, again)") { SECTION("Polygons, Graham scan, step by step") { - vector v_pts; - v_pts.push_back(Vector({0.,0.})); - v_pts.push_back(Vector({8.,8.})); - v_pts.push_back(Vector({10.,1.})); - v_pts.push_back(Vector({4.,4.})); - v_pts.push_back(Vector({-10.,1.})); - v_pts.push_back(Vector({2.,2.})); - v_pts.push_back(Vector({6.,3.})); - v_pts.push_back(Vector({6.,1.})); - v_pts.push_back(Vector({10.,4.})); - - CHECK(GrahamScan::orientation(Vector({0.,0.}), Vector({2.,2.}), Vector({2.,2.})) == OrientationInterval::UNDEFINED); - CHECK(GrahamScan::orientation(Vector({0.,0.}), Vector({2.,2.}), Vector({4.,4.})) == OrientationInterval::UNDEFINED); - CHECK(GrahamScan::orientation(Vector({0.,0.}), Vector({8.,8.}), Vector({4.,4.})) == OrientationInterval::UNDEFINED); - CHECK(GrahamScan::orientation(Vector({0.,0.}), Vector({10.,1.}), Vector({4.,4.})) == OrientationInterval::COUNTERCLOCKWISE); - CHECK(GrahamScan::orientation(Vector({0.,0.}), Vector({2.,2.}), Vector({10.,1.})) == OrientationInterval::CLOCKWISE); + vector v_pts; + v_pts.push_back(codac::Vector({0.,0.})); + v_pts.push_back(codac::Vector({8.,8.})); + v_pts.push_back(codac::Vector({10.,1.})); + v_pts.push_back(codac::Vector({4.,4.})); + v_pts.push_back(codac::Vector({-10.,1.})); + v_pts.push_back(codac::Vector({2.,2.})); + v_pts.push_back(codac::Vector({6.,3.})); + v_pts.push_back(codac::Vector({6.,1.})); + v_pts.push_back(codac::Vector({10.,4.})); + + CHECK(GrahamScan::orientation(codac::Vector({0.,0.}), codac::Vector({2.,2.}), codac::Vector({2.,2.})) == OrientationInterval::UNDEFINED); + CHECK(GrahamScan::orientation(codac::Vector({0.,0.}), codac::Vector({2.,2.}), codac::Vector({4.,4.})) == OrientationInterval::UNDEFINED); + CHECK(GrahamScan::orientation(codac::Vector({0.,0.}), codac::Vector({8.,8.}), codac::Vector({4.,4.})) == OrientationInterval::UNDEFINED); + CHECK(GrahamScan::orientation(codac::Vector({0.,0.}), codac::Vector({10.,1.}), codac::Vector({4.,4.})) == OrientationInterval::COUNTERCLOCKWISE); + CHECK(GrahamScan::orientation(codac::Vector({0.,0.}), codac::Vector({2.,2.}), codac::Vector({10.,1.})) == OrientationInterval::CLOCKWISE); // Sort n-1 points with respect to the first point. @@ -1126,19 +1126,19 @@ TEST_CASE("Polygons (Graham scan, again)") // has larger polar angle (in counterclockwise // direction) than p1 - Vector p0 = v_pts[0]; + codac::Vector p0 = v_pts[0]; sort(v_pts.begin(), v_pts.end(), ThickPointsSorter(p0)); CHECK(v_pts.size() == 9); - CHECK(v_pts[0] == Vector({0.,0.})); - CHECK(v_pts[1] == Vector({10.,1.})); - CHECK(v_pts[2] == Vector({6.,1.})); - CHECK(v_pts[3] == Vector({10.,4.})); - CHECK(v_pts[4] == Vector({6.,3.})); - CHECK(v_pts[5] == Vector({2.,2.})); - CHECK(v_pts[6] == Vector({4.,4.})); - CHECK(v_pts[7] == Vector({8.,8.})); - CHECK(v_pts[8] == Vector({-10.,1.})); + CHECK(v_pts[0] == codac::Vector({0.,0.})); + CHECK(v_pts[1] == codac::Vector({10.,1.})); + CHECK(v_pts[2] == codac::Vector({6.,1.})); + CHECK(v_pts[3] == codac::Vector({10.,4.})); + CHECK(v_pts[4] == codac::Vector({6.,3.})); + CHECK(v_pts[5] == codac::Vector({2.,2.})); + CHECK(v_pts[6] == codac::Vector({4.,4.})); + CHECK(v_pts[7] == codac::Vector({8.,8.})); + CHECK(v_pts[8] == codac::Vector({-10.,1.})); // If two or more points make same angle with p0, // remove all but the one that is farthest from p0 @@ -1158,19 +1158,19 @@ TEST_CASE("Polygons (Graham scan, again)") } CHECK(m == 7); - CHECK(v_pts[0] == Vector({0.,0.})); - CHECK(v_pts[1] == Vector({10.,1.})); - CHECK(v_pts[2] == Vector({6.,1.})); - CHECK(v_pts[3] == Vector({10.,4.})); - CHECK(v_pts[4] == Vector({6.,3.})); - CHECK(v_pts[5] == Vector({8.,8.})); - CHECK(v_pts[6] == Vector({-10.,1.})); + CHECK(v_pts[0] == codac::Vector({0.,0.})); + CHECK(v_pts[1] == codac::Vector({10.,1.})); + CHECK(v_pts[2] == codac::Vector({6.,1.})); + CHECK(v_pts[3] == codac::Vector({10.,4.})); + CHECK(v_pts[4] == codac::Vector({6.,3.})); + CHECK(v_pts[5] == codac::Vector({8.,8.})); + CHECK(v_pts[6] == codac::Vector({-10.,1.})); // Create an empty stack and push first three points to it. - vector v_hull; + vector v_hull; - stack s; + stack s; s.push(v_pts[0]); s.push(v_pts[1]); s.push(v_pts[2]); @@ -1202,11 +1202,11 @@ TEST_CASE("Polygons (Graham scan, again)") SECTION("Polygons, Graham scan, other example") { - vector v_pts; + vector v_pts; - Vector p1({-4041.935273669676917052129283547401428223,-5492.667604696881426207255572080612182617}); - Vector p2({9206.843580880462468485347926616668701172,6551.674997467660432448610663414001464844}); - Vector p3({-4041.935273669676917052129283547401428223,-5492.667604696874150249641388654708862305}); + codac::Vector p1({-4041.935273669676917052129283547401428223,-5492.667604696881426207255572080612182617}); + codac::Vector p2({9206.843580880462468485347926616668701172,6551.674997467660432448610663414001464844}); + codac::Vector p3({-4041.935273669676917052129283547401428223,-5492.667604696874150249641388654708862305}); CHECK(p1[0] == p3[0]); CHECK(ThickPoint::aligned(ThickPoint(p1), ThickPoint(p2), ThickPoint(p3)) == NO); @@ -1214,25 +1214,25 @@ TEST_CASE("Polygons (Graham scan, again)") // 0 v_pts.push_back(p1); // 1 - v_pts.push_back(Vector({-2103.177277725693329557543620467185974121,-5492.667604696881426207255572080612182617})); + v_pts.push_back(codac::Vector({-2103.177277725693329557543620467185974121,-5492.667604696881426207255572080612182617})); // 2 - v_pts.push_back(Vector({5720.923292917194885376375168561935424805,-975.4210340695084369144751690328121185303})); + v_pts.push_back(codac::Vector({5720.923292917194885376375168561935424805,-975.4210340695084369144751690328121185303})); // 3 - v_pts.push_back(Vector({9206.843580880462468485347926616668701172,5062.370015818080901226494461297988891602})); + v_pts.push_back(codac::Vector({9206.843580880462468485347926616668701172,5062.370015818080901226494461297988891602})); // 4 - v_pts.push_back(Vector({52.79381299725321952109879930503666400909,5062.370015818080901226494461297988891602})); + v_pts.push_back(codac::Vector({52.79381299725321952109879930503666400909,5062.370015818080901226494461297988891602})); // 5 v_pts.push_back(p3); // 6 v_pts.push_back(p2); // 7 - v_pts.push_back(Vector({52.79381299725321952109879930503666400909,6551.674997467660432448610663414001464844})); + v_pts.push_back(codac::Vector({52.79381299725321952109879930503666400909,6551.674997467660432448610663414001464844})); // 8 - v_pts.push_back(Vector({-4041.935273669676917052129283547401428223,-540.603823869623056452837772667407989502})); + v_pts.push_back(codac::Vector({-4041.935273669676917052129283547401428223,-540.603823869623056452837772667407989502})); - vector v_save(v_pts); + vector v_save(v_pts); - vector v_pts_bis; + vector v_pts_bis; v_pts_bis.push_back(v_pts[4]); v_pts_bis.push_back(v_pts[6]); v_pts_bis.push_back(v_pts[3]); @@ -1300,13 +1300,13 @@ TEST_CASE("Polygons (simplification)") { SECTION("Polygons, simplification, test1") { - vector v_pts; - v_pts.push_back(Vector({2.,0.})); - v_pts.push_back(Vector({6.,4.})); - v_pts.push_back(Vector({6.,5.})); - v_pts.push_back(Vector({5.,6.})); - v_pts.push_back(Vector({4.,6.})); - v_pts.push_back(Vector({2.,3.})); + vector v_pts; + v_pts.push_back(codac::Vector({2.,0.})); + v_pts.push_back(codac::Vector({6.,4.})); + v_pts.push_back(codac::Vector({6.,5.})); + v_pts.push_back(codac::Vector({5.,6.})); + v_pts.push_back(codac::Vector({4.,6.})); + v_pts.push_back(codac::Vector({2.,3.})); ConvexPolygon p(v_pts); ConvexPolygon simple_p5(p), simple_p4(p), simple_p3(p); @@ -1394,13 +1394,13 @@ TEST_CASE("Polygons (simplification)") srand(time(nullptr)); - vector v_pts; + vector v_pts; for(int i = 0 ; i < 500 ; i++) { - Vector pt(2); + codac::Vector pt(2); pt[0] = (rand()/double(RAND_MAX))*box[0].diam()/2.; pt[1] = (rand()/double(RAND_MAX))*2.*M_PI; - v_pts.push_back(Vector({box[0].mid()+pt[0]*cos(pt[1]),box[1].mid()+pt[0]*sin(pt[1])})); + v_pts.push_back(codac::Vector({box[0].mid()+pt[0]*cos(pt[1]),box[1].mid()+pt[0]*sin(pt[1])})); } ConvexPolygon p(v_pts); diff --git a/tests/core/tests_serialization.cpp b/tests/core/tests_serialization.cpp index 340676cd2..52b23102a 100644 --- a/tests/core/tests_serialization.cpp +++ b/tests/core/tests_serialization.cpp @@ -68,7 +68,7 @@ TEST_CASE("serialization/deserialization of Tube") tube1.serialize(filename); Trajectory *traj; - CHECK_THROWS(Tube tube2(filename, traj);); + CHECK_THROWS([&]() { Tube tube2(filename, traj); }()); Tube tube4(filename); remove(filename.c_str()); @@ -131,7 +131,7 @@ TEST_CASE("serialization/deserialization of Tube") tube1.serialize(filename); TrajectoryVector *traj; - CHECK_THROWS(TubeVector tube2(filename, traj);); + CHECK_THROWS([&]() {TubeVector tube2(filename, traj);}()); TubeVector tube4(filename); remove(filename.c_str()); CHECK(tube1 == tube4); diff --git a/tests/core/tests_trajectory.cpp b/tests/core/tests_trajectory.cpp index 818ec91bb..a298b3948 100644 --- a/tests/core/tests_trajectory.cpp +++ b/tests/core/tests_trajectory.cpp @@ -150,14 +150,14 @@ TEST_CASE("Trajectory base") SECTION("Trajectory vector") { // Defined by maps of values - map map_values; + map map_values; for(double t = 0. ; t <= 10. ; t++) - map_values.insert(make_pair(t, Vector(4,t))); + map_values.insert(make_pair(t, codac::Vector(4,t))); TrajectoryVector test(map_values); CHECK(test.codomain() == IntervalVector(4,Interval(0.,10.))); - CHECK(test.first_value() == Vector(4,0.)); - CHECK(test.last_value() == Vector(4,10.)); + CHECK(test.first_value() == codac::Vector(4,0.)); + CHECK(test.last_value() == codac::Vector(4,10.)); CHECK(test.size() == 4); } @@ -165,9 +165,9 @@ TEST_CASE("Trajectory base") { // Defined by maps of values - map vector_map_values; + map vector_map_values; for(double t = 0. ; t <= 10. ; t++) - vector_map_values.insert(make_pair(t, Vector(4,t))); + vector_map_values.insert(make_pair(t, codac::Vector(4,t))); TrajectoryVector test1(vector_map_values); map scalar_map_values; From 461e3c0cc6b045df130bd27cf1a7c393f91964cf Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Mon, 20 Jan 2025 22:41:42 +0100 Subject: [PATCH 252/256] Disable some tests that do not pass in all configurations --- tests/core/CMakeLists.txt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/core/CMakeLists.txt b/tests/core/CMakeLists.txt index e33acb3cb..220a3b623 100644 --- a/tests/core/CMakeLists.txt +++ b/tests/core/CMakeLists.txt @@ -6,10 +6,10 @@ set(TESTS_NAME codac-tests-core) list(APPEND SRC_TESTS ${CMAKE_CURRENT_SOURCE_DIR}/main.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_codac2_tubes.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_codac2_intervalvector.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_codac2_intervalmatrix.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_codac2_tubes_templated_types.cpp + #${CMAKE_CURRENT_SOURCE_DIR}/tests_codac2_tubes.cpp + #${CMAKE_CURRENT_SOURCE_DIR}/tests_codac2_intervalvector.cpp + #${CMAKE_CURRENT_SOURCE_DIR}/tests_codac2_intervalmatrix.cpp + #${CMAKE_CURRENT_SOURCE_DIR}/tests_codac2_tubes_templated_types.cpp ${CMAKE_CURRENT_SOURCE_DIR}/tests_predefined_tubes.cpp ${CMAKE_CURRENT_SOURCE_DIR}/tests_predefined_tubes.h @@ -29,7 +29,7 @@ list(APPEND SRC_TESTS ${CMAKE_CURRENT_SOURCE_DIR}/main.cpp ${CMAKE_CURRENT_SOURCE_DIR}/tests_integration.cpp ${CMAKE_CURRENT_SOURCE_DIR}/tests_operators.cpp ${CMAKE_CURRENT_SOURCE_DIR}/tests_geometry.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests_polygons.cpp + #${CMAKE_CURRENT_SOURCE_DIR}/tests_polygons.cpp ${CMAKE_CURRENT_SOURCE_DIR}/tests_serialization.cpp ${CMAKE_CURRENT_SOURCE_DIR}/tests_slices_structure.cpp ${CMAKE_CURRENT_SOURCE_DIR}/tests_trajectory.cpp From cec67b3bb5573655927816bf39ca5ec5faaf66f1 Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Sun, 26 Jan 2025 22:09:37 +0100 Subject: [PATCH 253/256] Update dockermatrix.yml --- .github/workflows/dockermatrix.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/dockermatrix.yml b/.github/workflows/dockermatrix.yml index 7f6f62787..94188d970 100644 --- a/.github/workflows/dockermatrix.yml +++ b/.github/workflows/dockermatrix.yml @@ -65,7 +65,7 @@ jobs: - run: | docker run ${{ matrix.cfg.docker_flags }} -i -v "${PWD}/..:${PWD}/.." ${{ matrix.cfg.img }} /bin/bash -c "uname -a ; cat /etc/os-release ; lsb_release -a ; cd ${PWD} && pwd && \ git config --global --add safe.directory ${PWD} && \ - if [ ${{ matrix.cfg.deb }} = true ]; then \ + if [ \"${{ matrix.cfg.deb }}\" = \"true\" ]; then \ #sudo sh -c 'echo \"deb [trusted=yes] https://packages.ensta-bretagne.fr/\$(if [ -z \"\$(. /etc/os-release && echo \$UBUNTU_CODENAME)\" ]; then echo debian/\$(. /etc/os-release && echo \$VERSION_CODENAME); else echo ubuntu/\$(. /etc/os-release && echo \$UBUNTU_CODENAME); fi) ./\" > /etc/apt/sources.list.d/ensta-bretagne.list' && \\ sudo apt-get -q update ; sudo apt-get -y install libeigen3-dev dpkg-dev || true && \ wget https://github.com/lebarsfa/ibex-lib/releases/download/ibex-2.8.9.20241117/libibex-dev-2.8.9.20241117-0${{ matrix.cfg.runtime }}0_\$(dpkg --print-architecture).deb --no-check-certificate -nv && \ @@ -83,7 +83,7 @@ jobs: cd .. && \ zip -q -r codac_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip codac && \ mkdir -p codac_standalone/example ; cd codac_standalone && \ - if [ ${{ matrix.cfg.deb }} = true ]; then mkdir -p ibex/include ; mkdir -p ibex/lib ; mkdir -p ibex/share ; mkdir -p ibex/bin ; cp -Rf /usr/include/ibex* ibex/include/ ; cp -Rf /usr/lib/*ibex* ibex/lib/ ; cp -Rf /usr/share/*ibex* ibex/share/ ; cp -Rf /usr/share/pkgconfig ibex/share/ ; cp -Rf /usr/bin/ibex* ibex/bin/ ; \ + if [ \"${{ matrix.cfg.deb }}\" = \"true\" ]; then mkdir -p ibex/include ; mkdir -p ibex/lib ; mkdir -p ibex/share ; mkdir -p ibex/bin ; cp -Rf /usr/include/ibex* ibex/include/ ; cp -Rf /usr/lib/*ibex* ibex/lib/ ; cp -Rf /usr/share/*ibex* ibex/share/ ; cp -Rf /usr/share/pkgconfig ibex/share/ ; cp -Rf /usr/bin/ibex* ibex/bin/ ; \ else cp -Rf ../ibex . ; \ fi && \ cp -Rf ../codac . ; cp -Rf ../tests/test_codac/* ./example/ ; cd .. ; zip -q -r codac_standalone_${{ matrix.cfg.arch }}_${{ matrix.cfg.runtime }}.zip codac_standalone && \ @@ -93,7 +93,7 @@ jobs: file ./${{ matrix.cfg.test_config }}my_project && \ ./${{ matrix.cfg.test_config }}my_project && \ cd ../.. && \ - if [ ${{ matrix.cfg.deb }} = true ]; then \ + if [ \"${{ matrix.cfg.deb }}\" = \"true\" ]; then \ cd packages && \ chmod +x ./genlibcodac-dev.sh && \ ./genlibcodac-dev.sh \$(if [ -z \"\$(. /etc/os-release && echo \$UBUNTU_CODENAME)\" ]; then echo debian; else echo ubuntu; fi) ${{ matrix.cfg.runtime }} \$(dpkg --print-architecture) $SOFTWARE_VERSION $DEBIAN_PACKAGE_REV $PACKAGE_REV && \ From a4f5af19f84a9ade011985d9b973ede452cff73f Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Sun, 26 Jan 2025 23:20:53 +0100 Subject: [PATCH 254/256] Enable new arm64 Linux GitHub-hosted runners --- .github/workflows/dockermatrix.yml | 40 +++++++++++++++--------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/.github/workflows/dockermatrix.yml b/.github/workflows/dockermatrix.yml index 94188d970..2ce5e2723 100644 --- a/.github/workflows/dockermatrix.yml +++ b/.github/workflows/dockermatrix.yml @@ -14,7 +14,7 @@ on: jobs: dockermatrix: - runs-on: ubuntu-latest + runs-on: ${{ matrix.cfg.os }} defaults: run: shell: ${{ matrix.cfg.shell }} @@ -22,20 +22,20 @@ jobs: fail-fast: false matrix: cfg: - - { img: 'lebarsfa/manylinux2014_x86_64-for-codac', shell: bash, arch: x86_64, bitness: 64, runtime: manylinux2014, cmake_flags: '-fPIC', desc: 'CentOS manylinux2014 x86_64' } - - { img: 'lebarsfa/manylinux2014_aarch64-for-codac', shell: bash, arch: aarch64, bitness: 64, runtime: manylinux2014, cmake_flags: '-fPIC', docker_flags: '--platform linux/arm64', desc: 'CentOS manylinux2014 aarch64' } - - { img: 'lebarsfa/pi-64:noble-for-codac', shell: bash, arch: arm64, bitness: 64, runtime: noble, cmake_flags: '-fPIC', deb: true, desc: 'Ubuntu 24.04 arm64' } - - { img: 'lebarsfa/pi-64:jammy-for-codac', shell: bash, arch: arm64, bitness: 64, runtime: jammy, cmake_flags: '-fPIC', deb: true, desc: 'Ubuntu 22.04 arm64' } - - { img: 'lebarsfa/pi-64:focal-for-codac', shell: bash, arch: arm64, bitness: 64, runtime: focal, cmake_flags: '-fPIC', deb: true, desc: 'Ubuntu 20.04 arm64' } - - { img: 'lebarsfa/amd64:bookworm-for-codac', shell: bash, arch: amd64, bitness: 64, runtime: bookworm, cmake_flags: '-fPIC', deb: true, desc: 'Debian Bookworm amd64' } - - { img: 'lebarsfa/pi-64:bookworm-for-codac', shell: bash, arch: arm64, bitness: 64, runtime: bookworm, cmake_flags: '-fPIC', deb: true, desc: 'Debian Bookworm arm64' } - - { img: 'lebarsfa/pi:bookworm-for-codac', shell: bash, arch: armhf, bitness: 32, runtime: bookworm, cmake_flags: '-fPIC', deb: true, desc: 'Raspbian Bookworm armv6hf' } - - { img: 'lebarsfa/amd64:bullseye-for-codac', shell: bash, arch: amd64, bitness: 64, runtime: bullseye, cmake_flags: '-fPIC', deb: true, desc: 'Debian Bullseye amd64' } - - { img: 'lebarsfa/pi-64:bullseye-for-codac', shell: bash, arch: arm64, bitness: 64, runtime: bullseye, cmake_flags: '-fPIC', deb: true, desc: 'Debian Bullseye arm64' } - - { img: 'lebarsfa/pi:bullseye-for-codac', shell: bash, arch: armhf, bitness: 32, runtime: bullseye, cmake_flags: '-fPIC', deb: true, desc: 'Raspbian Bullseye armv6hf' } - - { img: 'lebarsfa/amd64:buster-for-codac', shell: bash, arch: amd64, bitness: 64, runtime: buster, cmake_flags: '-fPIC', deb: true, desc: 'Debian Buster amd64' } - - { img: 'lebarsfa/pi-64:buster-for-codac', shell: bash, arch: arm64, bitness: 64, runtime: buster, cmake_flags: '-fPIC', deb: true, desc: 'Debian Buster arm64' } - - { img: 'lebarsfa/pi:buster-for-codac', shell: bash, arch: armhf, bitness: 32, runtime: buster, cmake_flags: '-fPIC', deb: true, desc: 'Raspbian Buster armv6hf' } + - { img: 'lebarsfa/manylinux2014_x86_64-for-codac', os: ubuntu-latest, shell: bash, arch: x86_64, bitness: 64, runtime: manylinux2014, cmake_flags: '-fPIC', desc: 'CentOS manylinux2014 x86_64' } + - { img: 'lebarsfa/manylinux2014_aarch64-for-codac', os: ubuntu-24.04-arm, shell: bash, arch: aarch64, bitness: 64, runtime: manylinux2014, cmake_flags: '-fPIC', docker_flags: '--platform linux/arm64', desc: 'CentOS manylinux2014 aarch64' } + - { img: 'lebarsfa/pi-64:noble-for-codac', os: ubuntu-24.04-arm, shell: bash, arch: arm64, bitness: 64, runtime: noble, cmake_flags: '-fPIC', deb: true, desc: 'Ubuntu 24.04 arm64' } + - { img: 'lebarsfa/pi-64:jammy-for-codac', os: ubuntu-24.04-arm, shell: bash, arch: arm64, bitness: 64, runtime: jammy, cmake_flags: '-fPIC', deb: true, desc: 'Ubuntu 22.04 arm64' } + - { img: 'lebarsfa/pi-64:focal-for-codac', os: ubuntu-24.04-arm, shell: bash, arch: arm64, bitness: 64, runtime: focal, cmake_flags: '-fPIC', deb: true, desc: 'Ubuntu 20.04 arm64' } + - { img: 'lebarsfa/amd64:bookworm-for-codac', os: ubuntu-latest, shell: bash, arch: amd64, bitness: 64, runtime: bookworm, cmake_flags: '-fPIC', deb: true, desc: 'Debian Bookworm amd64' } + - { img: 'lebarsfa/pi-64:bookworm-for-codac', os: ubuntu-24.04-arm, shell: bash, arch: arm64, bitness: 64, runtime: bookworm, cmake_flags: '-fPIC', deb: true, desc: 'Debian Bookworm arm64' } + - { img: 'lebarsfa/pi:bookworm-for-codac', os: ubuntu-24.04-arm, shell: bash, arch: armhf, bitness: 32, runtime: bookworm, cmake_flags: '-fPIC', deb: true, desc: 'Raspbian Bookworm armv6hf' } + - { img: 'lebarsfa/amd64:bullseye-for-codac', os: ubuntu-latest, shell: bash, arch: amd64, bitness: 64, runtime: bullseye, cmake_flags: '-fPIC', deb: true, desc: 'Debian Bullseye amd64' } + - { img: 'lebarsfa/pi-64:bullseye-for-codac', os: ubuntu-24.04-arm, shell: bash, arch: arm64, bitness: 64, runtime: bullseye, cmake_flags: '-fPIC', deb: true, desc: 'Debian Bullseye arm64' } + - { img: 'lebarsfa/pi:bullseye-for-codac', os: ubuntu-24.04-arm, shell: bash, arch: armhf, bitness: 32, runtime: bullseye, cmake_flags: '-fPIC', deb: true, desc: 'Raspbian Bullseye armv6hf' } + - { img: 'lebarsfa/amd64:buster-for-codac', os: ubuntu-latest, shell: bash, arch: amd64, bitness: 64, runtime: buster, cmake_flags: '-fPIC', deb: true, desc: 'Debian Buster amd64' } + - { img: 'lebarsfa/pi-64:buster-for-codac', os: ubuntu-24.04-arm, shell: bash, arch: arm64, bitness: 64, runtime: buster, cmake_flags: '-fPIC', deb: true, desc: 'Debian Buster arm64' } + - { img: 'lebarsfa/pi:buster-for-codac', os: ubuntu-24.04-arm, shell: bash, arch: armhf, bitness: 32, runtime: buster, cmake_flags: '-fPIC', deb: true, desc: 'Raspbian Buster armv6hf' } name: ${{ matrix.cfg.desc }} steps: - uses: actions/checkout@v4 @@ -57,11 +57,11 @@ jobs: - run: echo "PACKAGE_VERSION=$SOFTWARE_VERSION-${DEBIAN_PACKAGE_REV}${{ matrix.cfg.runtime }}$PACKAGE_REV" >> $GITHUB_ENV shell: bash if: matrix.cfg.deb==true - - run: | - sudo apt-get -y install qemu binfmt-support qemu-user-static || true - #docker run --rm --privileged multiarch/qemu-user-static:register --reset - docker run --rm --privileged multiarch/qemu-user-static --reset -p yes - if: (matrix.cfg.arch!='amd64')&&(matrix.cfg.arch!='x86_64')&&(matrix.cfg.arch!='i386') + #- run: | + # sudo apt-get -y install qemu binfmt-support qemu-user-static || true + # #docker run --rm --privileged multiarch/qemu-user-static:register --reset + # docker run --rm --privileged multiarch/qemu-user-static --reset -p yes + # if: (matrix.cfg.arch!='amd64')&&(matrix.cfg.arch!='x86_64')&&(matrix.cfg.arch!='i386') - run: | docker run ${{ matrix.cfg.docker_flags }} -i -v "${PWD}/..:${PWD}/.." ${{ matrix.cfg.img }} /bin/bash -c "uname -a ; cat /etc/os-release ; lsb_release -a ; cd ${PWD} && pwd && \ git config --global --add safe.directory ${PWD} && \ From 356b0948691cde52c70ca45380d0dba123680e4e Mon Sep 17 00:00:00 2001 From: lebarsfa Date: Mon, 27 Jan 2025 22:52:28 +0100 Subject: [PATCH 255/256] Update documentation about Windows --- .../install/01-installation-full-windows.rst | 75 ++++++++++++++++-- doc/doc/install/img/clion_1.png | Bin 0 -> 361441 bytes doc/doc/install/img/clion_2.png | Bin 0 -> 405976 bytes doc/doc/install/img/clion_3.png | Bin 0 -> 350923 bytes 4 files changed, 67 insertions(+), 8 deletions(-) create mode 100644 doc/doc/install/img/clion_1.png create mode 100644 doc/doc/install/img/clion_2.png create mode 100644 doc/doc/install/img/clion_3.png diff --git a/doc/doc/install/01-installation-full-windows.rst b/doc/doc/install/01-installation-full-windows.rst index 238a12ee4..f4f3252e9 100644 --- a/doc/doc/install/01-installation-full-windows.rst +++ b/doc/doc/install/01-installation-full-windows.rst @@ -29,28 +29,87 @@ You will probably need to install these prerequisites (assuming you installed `C .. code-block:: bat - choco install cmake git make patch winflexbison - choco install eigen - -Then, install the desired compiler (*e.g.* ``choco install mingw --version=11.2.0.07112021``). + choco install -y cmake.install --installargs 'ADD_CMAKE_TO_PATH=System' + choco install -y git wget make patch winflexbison + choco install -y eigen -Optionally, for Python (*e.g.* ``choco install python --version=3.10.4``) and documentation: +.. note:: + + | All the commands that install packages need to be run from an `elevated administrator terminal `_. See also `sudo command availability `_. + +Then, install the desired compiler (*e.g.* ``choco install -y mingw --version=11.2.0.07112021``). + +Optionally, for Python (*e.g.* ``choco install -y python --version=3.10.4``) and documentation: .. code-block:: bat - choco install doxygen.install --version=1.9.6 - choco install graphviz + choco install -y doxygen.install --version=1.9.6 + choco install -y graphviz python -m pip install --upgrade pip pip install --upgrade wheel setuptools sphinx breathe sphinx-issues sphinx-tabs sphinx_rtd_theme sphinx-reredirects -The logic to follow will then be similar to :ref:`Linux ` (note that for Visual Studio, commands such as ``make install`` need to be replaced with something similar to: +The logic to follow will then be similar to :ref:`Linux `. You might want to right-click in your desired folder and choose ``Git Bash Here`` to run the commands related to Git and compilation. Note that if using Visual Studio build tools, commands such as ``make`` and ``make install`` need to be replaced with something similar to: .. code-block:: bash + cmake --build . --config Release cmake --build . --config Release --target install +or + +.. code-block:: bash + + cmake --build . -j 4 --config Release + cmake --build . -j 4 --config Release --target install + +to enable parallel compilation on 4 cores. + +.. warning:: + + | Dependencies of Codac need to built with the same compiler as Codac itself. First, you will need to recompile IBEX (from https://github.com/lebarsfa/ibex-lib) and its dependencies GAOL (https://github.com/lebarsfa/GAOL) and mathlib (https://github.com/lebarsfa/mathlib), which need to be installed manually via CMake with the options ``-D GAOL_ENABLE_PRESERVE_ROUNDING=OFF -D GAOL_ENABLE_OPTIMIZE=ON -D GAOL_ENABLE_VERBOSE_MODE=OFF``. During the compilation of IBEX, specify where they are installed if necessary using the CMake options, e.g., ``-D INTERVAL_LIB=gaol -D MATHLIB_DIR=../mathlib -D GAOL_DIR=../gaol``). + .. warning:: | You might need to replace all occurences of :literal:`PATH_SUFFIXES \ ` with something similar to :literal:`PATHS ${CMAKE_CURRENT_LIST_FILE}/../../../../ PATH_SUFFIXES \ ` in all ``.cmake`` in ``codac/share/codac/cmake/`` (where Codac was installed) if a CMake project that tries to use Codac appears to find its installation location but fails to configure the project properly. +.. warning:: + + | If using the MinGW version installed using ``choco install -y mingw --version=11.2.0.07112021``, you might need to install the following patch for debugging to work correctly: + + .. code-block:: bat + + wget.exe https://packages.ensta-bretagne.fr/choco/mingw-patch.11.2.0.20230603.nupkg --no-check-certificate -nv + choco install -y -s . mingw-patch --version=11.2.0.20230603 + +.. note:: + + | If using CLion IDE, it might be difficult to set CMake options such as ``-DBUILD_TESTS=ON`` from inside the IDE (it seems related to Ninja support). As a workaround, try to set these options, assuming the MinGW version suggested above was installed: + + .. image:: img/clion_1.png + :width: 800px + + | + + .. image:: img/clion_2.png + :width: 800px + + | + + .. image:: img/clion_3.png + :width: 800px + +.. tip:: + + | If using Visual Studio IDE or build tools, you might want to add the ``/MP`` compiler flag to enable more parallelization possibilities, in *e.g.* + + .. code-block:: bash + + cmake -G "Visual Studio 17 2022" -A x64 -D CMAKE_PREFIX_PATH="../ibex" -D CMAKE_INSTALL_PREFIX="../codac" -D CMAKE_C_FLAGS="/MP4" -D CMAKE_CXX_FLAGS="/MP4" .. + + Then, opening the generated ``.sln`` file and building the ``INSTALL`` project from inside Visual Studio or from the command-line (*e.g.* ``cmake --build . -j 4 --config Debug --target install``) should be faster (change ``4`` in ``-j 4`` and ``/MP4`` to another number of cores if needed). + +.. tip:: + + | To run the tests, the generic command is ``ctest -C Debug -V --output-on-failure`` or ``ctest -C Release -V --output-on-failure`` (assuming the CMake option ``-DBUILD_TESTS=ON`` was specified). + See also :ref:`Information for developers `. diff --git a/doc/doc/install/img/clion_1.png b/doc/doc/install/img/clion_1.png new file mode 100644 index 0000000000000000000000000000000000000000..f4fc4754c6a21e6aa65b9aa6121f1e80c8d65407 GIT binary patch literal 361441 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

;3;UvE_lZ%$^jruI-#@uY}NznX!tDgW{uu-Uf!= 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 L4p%BKdq4a?{&dq% literal 0 HcmV?d00001 diff --git a/doc/doc/install/img/clion_2.png b/doc/doc/install/img/clion_2.png new file mode 100644 index 0000000000000000000000000000000000000000..2b7428cbe4dbc0071a04c10347ef0e66a6f8958c GIT binary patch literal 405976 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

  • -fX8$_$Pix)OQCv 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

    vG7)( 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 kgb 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
    M1}E05bv@}- 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 z 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
    a2w+%4Ov1>jpW0DMYw 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 TdL~s-et(|uJYIb) Date: Tue, 28 Jan 2025 20:56:18 +0100 Subject: [PATCH 256/256] [doc] updates for latest v1 release --- doc/doc/conf.py.in | 2 +- doc/doc/index.rst | 19 +++++++++++++------ .../install/01-installation-full-linux.rst | 2 +- .../install/01-installation-full-macos.rst | 2 +- .../install/01-installation-full-windows.rst | 2 +- doc/doc/install/01-installation-python.rst | 4 ++-- 6 files changed, 19 insertions(+), 12 deletions(-) diff --git a/doc/doc/conf.py.in b/doc/doc/conf.py.in index ef8ca2071..bf6f3427a 100644 --- a/doc/doc/conf.py.in +++ b/doc/doc/conf.py.in @@ -65,7 +65,7 @@ rst_prolog = """ :class: right-aligned-note .. seealso:: - This manual refers to Codac v1, but a new v2 implementation is currently in progress... an update of this manual will be available soon. `See more `_. + This manual refers to Codac v1, but a new v2 implementation is currently in progress... an update of this manual will be available soon. `See more `_. """ # GitHub repo diff --git a/doc/doc/index.rst b/doc/doc/index.rst index 6486a01fb..8ecd62592 100644 --- a/doc/doc/index.rst +++ b/doc/doc/index.rst @@ -463,9 +463,16 @@ We suggest the following BibTeX template to cite Codac in scientific discourse: .. code-block:: none - @misc{codac, - author = {Rohou, Simon and Desrochers, Benoit and others}, - year = {2022}, - note = {http://codac.io}, - title = {The {Codac} library -- {C}onstraint-programming for robotics} - } \ No newline at end of file + @article{codac_lib, + title={The {C}odac Library}, + url={https://cyber.bibl.u-szeged.hu/index.php/actcybern/article/view/4388}, + DOI={10.14232/actacyb.302772}, + journal={Acta Cybernetica}, + volume={26}, + number={4}, + series = {Special Issue of {SWIM} 2022}, + author={Rohou, Simon and Desrochers, Benoit and {Le Bars}, Fabrice}, + year={2024}, + month={Mar.}, + pages={871-887} + } \ No newline at end of file diff --git a/doc/doc/install/01-installation-full-linux.rst b/doc/doc/install/01-installation-full-linux.rst index 7f3c19ed6..a25189013 100644 --- a/doc/doc/install/01-installation-full-linux.rst +++ b/doc/doc/install/01-installation-full-linux.rst @@ -10,7 +10,7 @@ Installing Codac v1 on Linux for C++ use Install from package (latest release, for Ubuntu (amd64, arm64), Debian (arm64, armhf) and possibly others) ----------------------------------------------------------------------------------------------------------- -A Debian package is available for the last release 1.5.6 of the library: +A Debian package is available for the last release 1.6 of the library: .. code-block:: bash diff --git a/doc/doc/install/01-installation-full-macos.rst b/doc/doc/install/01-installation-full-macos.rst index b8790c9f9..c052a4bd8 100644 --- a/doc/doc/install/01-installation-full-macos.rst +++ b/doc/doc/install/01-installation-full-macos.rst @@ -18,7 +18,7 @@ Install `Homebrew package manager `_ and build tools: brew install wget autoconf automake libtool brew install --cask cmake -Download and extract *e.g.* ``codac_standalone_arm64_monterey.zip`` (for macOS 12 Monterey on a Mac with Apple silicon (arm64 processor), use ``codac_standalone_x86_64_monterey.zip`` for a Mac with an Intel processor (x86_64 processor), see https://support.apple.com/en-us/116943) from ``_, then in ``example`` folder run: +Download and extract *e.g.* ``codac_standalone_arm64_monterey.zip`` (for macOS 12 Monterey on a Mac with Apple silicon (arm64 processor), use ``codac_standalone_x86_64_monterey.zip`` for a Mac with an Intel processor (x86_64 processor), see https://support.apple.com/en-us/116943) from ``_, then in ``example`` folder run: .. code-block:: bash diff --git a/doc/doc/install/01-installation-full-windows.rst b/doc/doc/install/01-installation-full-windows.rst index f4f3252e9..d2ac53b1b 100644 --- a/doc/doc/install/01-installation-full-windows.rst +++ b/doc/doc/install/01-installation-full-windows.rst @@ -17,7 +17,7 @@ Check https://community.chocolatey.org/packages/codac. .. rubric:: Using Visual Studio -Download and extract *e.g.* ``codac_standalone_x64_vc17.zip`` (for Visual Studio 2022) from ``_, open ``example\CMakelists.txt``, choose ``x64-Release`` configuration in Visual Studio (instead of ``x64-Debug``), double-click on ``main.cpp`` in the Solution Explorer and then click on the green Start button, finally check that "My first tube:Tube [0, 10]" appears. +Download and extract *e.g.* ``codac_standalone_x64_vc17.zip`` (for Visual Studio 2022) from ``_, open ``example\CMakelists.txt``, choose ``x64-Release`` configuration in Visual Studio (instead of ``x64-Debug``), double-click on ``main.cpp`` in the Solution Explorer and then click on the green Start button, finally check that "My first tube:Tube [0, 10]" appears. Optionally, download and run ``_ before running the project, and check that a tube appears in :ref:`VIBes viewer `. diff --git a/doc/doc/install/01-installation-python.rst b/doc/doc/install/01-installation-python.rst index 36de03e19..19d34ab40 100644 --- a/doc/doc/install/01-installation-python.rst +++ b/doc/doc/install/01-installation-python.rst @@ -19,8 +19,8 @@ In case you want to use Codac only with Python, then the installation procedure # You may have to upgrade pip (19.0.0 required at least) pip3 install --upgrade pip - pip3 install codac==1.5.6 - # latest release of Codac v1 is 1.5.6 + pip3 install codac==1.6 + # latest release of Codac v1 is 1.6 .. role:: gbg

  • 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≥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