Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Simplify Arf/Arb interface #2

Draft
wants to merge 25 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
310da68
Simplify Arf/Arb interface
saraedum Jan 25, 2022
a6d04f6
Fix include directories for byexample tests
saraedum Jan 25, 2022
cb4a9aa
Add scoped Precision
saraedum Feb 2, 2022
abd5e6f
Add scoped Rounding
saraedum Feb 2, 2022
64533b0
Improve documentation and testing of Arf
saraedum Mar 12, 2022
ecbf5a0
Add Rounding & Precision tests
saraedum Mar 12, 2022
c3abb13
Run Arf benchmarks
saraedum Mar 12, 2022
59b5a43
Drop Yap helpers for cppyy
saraedum Mar 12, 2022
16b69b9
Fix problems with byexampe testing
saraedum Mar 12, 2022
16e88b6
Add randomized generator for testing
saraedum Mar 12, 2022
a30a4a4
Improve performance of Arb through explicit inlining
saraedum Mar 12, 2022
0287658
Add news
saraedum Mar 12, 2022
aa6e6d9
Improve news
saraedum Mar 12, 2022
d9fbc49
Fix crash when evaluating throwing operators
saraedum Mar 20, 2022
657601c
Merge remote-tracking branch 'saraedum/byexample' into byexample
saraedum Mar 20, 2022
8ab8204
Document test and benchmark binary operators Arf +-*/ Arf
saraedum Mar 22, 2022
44d0f15
Document and test more of the Arb interface
saraedum Mar 31, 2022
12873de
Do not build static libraries for our nightly conda channel
saraedum Dec 12, 2022
e6828ad
Make cxx example runner more robust
saraedum Dec 13, 2022
2630022
Add string parser/conversion to Arb
saraedum Dec 14, 2022
571218a
Fix error reporting when Arf parsing fails
saraedum Dec 14, 2022
d1c73bd
Better test parallelization
saraedum Dec 14, 2022
86a9b87
Split Arf test cases for better parallelization
saraedum Dec 14, 2022
2327725
Split arb test for better parallelization
saraedum Dec 14, 2022
8c093cd
Upgrade CI
saraedum Jan 14, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/abi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ jobs:
- uses: actions/checkout@v2
with: { submodules: recursive }
- uses: conda-incubator/setup-miniconda@v2
with: { mamba-version: "*", channels: "conda-forge", channel-priority: true }
with: { miniforge-variant: "Mambaforge", miniforge-version: "latest" }
- name: install ABI checker
run: |
sudo apt update
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/benchmark.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
submodules: recursive
fetch-depth: 0
- uses: conda-incubator/setup-miniconda@v2
with: { mamba-version: "*", python-version: "3.9", channels: "conda-forge", channel-priority: true }
with: { miniforge-variant: "Mambaforge", miniforge-version: "latest", python-version: "3.9" }
- name: Install dependencies
shell: bash -l {0}
run: |
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/coverage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
- uses: actions/checkout@v2
with: { submodules: recursive, fetch-depth: 0 }
- uses: conda-incubator/setup-miniconda@v2
with: { mamba-version: "*", channels: "conda-forge", channel-priority: true }
with: { miniforge-variant: "Mambaforge", miniforge-version: "latest" }
- name: install dependencies
shell: bash -l {0}
run: |
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ jobs:
- uses: actions/checkout@v2
with: { submodules: recursive }
- uses: conda-incubator/setup-miniconda@v2
with: { mamba-version: "*", channels: "conda-forge", channel-priority: true }
with: { miniforge-variant: "Mambaforge", miniforge-version: "latest" }
- name: install dependencies
shell: bash -l {0}
run: |
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ jobs:
echo "::set-output name=configure::"`[[ ${{ runner.os }} == "Linux" ]] && echo "--with-version-script"` `[[ "${{ matrix.sanitizer }}" == "valgrind" ]] && echo "--enable-valgrind"`
echo "::set-output name=shared::"`[[ "${{ matrix.sanitizer }}" == "address" ]] && echo "-fsanitize=address -fno-sanitize-recover -fno-omit-frame-pointer"` `[[ "${{ matrix.sanitizer }}" == "undefined" ]] && echo "-fsanitize=undefined -fno-sanitize-recover -fno-omit-frame-pointer"` `[[ "${{ matrix.on }}" == "macos-10.15" ]] && [[ "${{ matrix.sanitizer }}" == "address" || "${{ matrix.sanitizer }}" == "undefined" ]] && echo "-Wl,-undefined,dynamic_lookup"`
- uses: conda-incubator/setup-miniconda@v2
with: { mamba-version: "*", channels: "conda-forge", channel-priority: true }
with: { miniforge-variant: "Mambaforge", miniforge-version: "latest" }
- name: install valgrind
shell: bash -l {0}
run: |
Expand Down Expand Up @@ -130,7 +130,7 @@ jobs:
echo "::set-output name=configure::"`[[ ${{ runner.os }} == "Linux" ]] && echo "--with-version-script"` `[[ "${{ matrix.sanitizer }}" == "valgrind" ]] && echo "--enable-valgrind"`
echo "::set-output name=shared::"`[[ "${{ matrix.sanitizer }}" == "address" ]] && echo "-fsanitize=address -fno-sanitize-recover -fno-omit-frame-pointer"` `[[ "${{ matrix.sanitizer }}" == "undefined" ]] && echo "-fsanitize=undefined -fno-sanitize-recover -fno-omit-frame-pointer"` `[[ "${{ matrix.on }}" == "macos-10.15" ]] && [[ "${{ matrix.sanitizer }}" == "address" || "${{ matrix.sanitizer }}" == "undefined" ]] && echo "-Wl,-undefined,dynamic_lookup"`
- uses: conda-incubator/setup-miniconda@v2
with: { mamba-version: "*", channels: "conda-forge", python-version: "${{ matrix.python }}", channel-priority: true }
with: { miniforge-variant: "Mambaforge", miniforge-version: "latest", python-version: "${{ matrix.python }}" }
- name: install valgrind
shell: bash -l {0}
run: |
Expand Down Expand Up @@ -187,7 +187,7 @@ jobs:
- uses: actions/checkout@v2
with: { submodules: recursive }
- uses: conda-incubator/setup-miniconda@v2
with: { mamba-version: "*", channels: "conda-forge", channel-priority: true }
with: { miniforge-variant: "Mambaforge", miniforge-version: "latest" }
- name: install dependencies
shell: bash -l {0}
run: |
Expand Down
11 changes: 11 additions & 0 deletions doc/news/byexample.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
**Added:**

* Added Precision and Rounding contexts to make normal arithmetic available in a convenient way in the C++ interface.

**Removed:**

* Removed yap expression building. In practice this turned out not to be terribly useful. If somebody wants to micro-optimize arithmetic, they are going to use the C API directly anyway.

**Performance:**

* Improved performance of operations that suffer a lot from an extra call-overhead. In principle LTO should be able to do much of this inlining at link-time. However, when building a shared library this does not work reliably, not even for calls inside the shared library even if they live in the same object file (with GCC.) I did dig into the source of GCC and ld to understand why this happens but unfortunately I forget the details now. Maybe this gets fixed some day but currently, no mix of `-fno-semantic-interposition`, whole program optimization, inline attributes and keywords, makes this work with GCC without moving code to the header files (and that's what we do here.)
142 changes: 88 additions & 54 deletions libarbxx/arbxx/arb.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,51 +34,50 @@

namespace arbxx {

// After some discussion with the Arb author, it seems that 64 and 128 are good
// default precisions for which Arb should be particularly fast. We use this
// only when we need some random precision to at which to start an algorithm.
// For callers of the library, this should not have any effect other than
// changing the performance of certain calls slightly.
inline constexpr const prec ARB_PRECISION_FAST = 64;

/// TODO
/// A wrapper for [::arb_t]() elements, i.e., floating point numbers surrounded
/// by a real ball of imprecision, so we get C++ style memory management. We
/// use some Yap magic to get nice operators (which is tricky otherwise because
/// we cannot pass the additional prec and rnd parameters to operators in C++.)
///
/// If you don't like that magic and only want memory management, just create
/// element and then use Arb functions directly:
///
/// by a real ball of imprecision, providing C++ style memory management.
///
/// #include <arbxx/arb.hpp>
/// arbxx::Arb x, y;
///
/// arb_add(x.arb_t(), x.arb_t(), y.arb_t(), 64);
/// Instances of this class can be used as arguments in the C API of Arb.
///
/// Using yap this can be rewritten as any of the following:
/// arb_add(x, x, y, 64);
///
/// #include <arbxx/yap/arb.hpp>
/// Some parts of the C API that naturally translate to class members, have
/// been implemented, see below. However, arithmetic operators are not readily
/// available.
///
/// arbxx::Arb x, y;
/// x * y
/// // -> No precision has been specified in this scope.
///
/// They only become available once a precision has been fixed.
///
/// x += y(64);
/// x = (x + y)(64);
/// #include <arbxx/precision.hpp>
///
/// Note that the latter might use an additional temporary Arb. See the
/// yap/arb.hpp header for more details.
/// arbxx::Precision prec{64};
///
/// Note that methods here are usually named as their counterparts in arb.h with
/// the leading arb_ removed.
/// x * y
/// // -> 0
///
/// Note that many methods provided by arb's C API are not yet provided by this
/// C++ wrapper. If something is missing for you, please let us know on our
/// [GitHub issues page](https://github.com/flatsurf/arbxx/issues).
class LIBARBXX_API Arb {
/// Note that these operators are less efficient than calling the corresponding
/// C function from Arb directly. (See comments below.)
class LIBARBXX_API Arb : boost::arithmetic<Arb>,
boost::arithmetic<Arb, Arf>,
boost::multipliable<Arb, short>,
boost::multipliable<Arb, unsigned short>,
boost::multipliable<Arb, int>,
boost::multipliable<Arb, unsigned int>,
boost::multipliable<Arb, long>,
boost::multipliable<Arb, unsigned long>,
boost::multipliable<Arb, long long>,
boost::multipliable<Arb, unsigned long long>,
boost::multipliable<Arb, mpz_class> {
public:
/// Create an exact zero element.
///
/// arbxx::Arb x;
/// std::cout << x;
/// x
/// // -> 0
///
Arb() noexcept;
Expand Down Expand Up @@ -109,20 +108,6 @@ class LIBARBXX_API Arb {
///
explicit Arb(const mpz_class&);

/// Create an element containing this rational.
/// Typically, the result won't be exact, in particular not if the rational
/// cannot be represented exactly in base 2.
///
/// arbxx::Arb x{mpq_class{1, 2}};
/// std::cout << x;
/// // -> 0.500000
///
/// arbxx::Arb y{mpq_class{1, 3}};
/// std::cout << std::setprecision(32) << y;
/// // -> [0.33333333333333333331526329712524 +/- 2.72e-20]
///
explicit Arb(const mpq_class&);

/// Create an element containing this rational using [arb_set_fmpq](), i.e.,
/// by performing the division of numerator and denominator with precision
/// `prec`.
Expand Down Expand Up @@ -187,8 +172,12 @@ class LIBARBXX_API Arb {
/// std::cout << x;
/// // -> [3.25000 +/- 1.01e-4]
///
/// Note that, as in the above example, the radius is not guaranteed to be
/// preserved when going from an element to its string representation and
/// back. See https://github.com/fredrik-johansson/arb/issues/412.
Arb(const std::string&, const prec);

/// TODO
~Arb() noexcept;

/// ==* `operator=(Arb)` *==
Expand Down Expand Up @@ -242,7 +231,7 @@ class LIBARBXX_API Arb {
/// Note that this is different from the semantic in Arb where false is
/// returned in both of the latter cases.
///
/// arbxx::Arb x{mpq_class{1, 3}};
/// arbxx::Arb x{mpq_class{1, 3}, 64};
/// (x < 1).has_value()
/// // -> true
///
Expand Down Expand Up @@ -389,14 +378,37 @@ class LIBARBXX_API Arb {
LIBARBXX_API friend std::optional<bool> operator<=(const mpq_class&, const Arb&);
LIBARBXX_API friend std::optional<bool> operator>=(const mpq_class&, const Arb&);

/// TODO
LIBARBXX_API friend Arb& operator+=(Arb&, const Arb&);
LIBARBXX_API friend Arb& operator-=(Arb&, const Arb&);
LIBARBXX_API friend Arb& operator*=(Arb&, const Arb&);
LIBARBXX_API friend Arb& operator/=(Arb&, const Arb&);

/// TODO
LIBARBXX_API friend Arb& operator+=(Arb&, const Arf&);
LIBARBXX_API friend Arb& operator-=(Arb&, const Arf&);
LIBARBXX_API friend Arb& operator*=(Arb&, const Arf&);
LIBARBXX_API friend Arb& operator/=(Arb&, const Arf&);

/// TODO
LIBARBXX_API friend Arb& operator*=(Arb&, short);
LIBARBXX_API friend Arb& operator*=(Arb&, unsigned short);
LIBARBXX_API friend Arb& operator*=(Arb&, int);
LIBARBXX_API friend Arb& operator*=(Arb&, unsigned int);
LIBARBXX_API friend Arb& operator*=(Arb&, long);
LIBARBXX_API friend Arb& operator*=(Arb&, unsigned long);
LIBARBXX_API friend Arb& operator*=(Arb&, long long);
LIBARBXX_API friend Arb& operator*=(Arb&, unsigned long long);
LIBARBXX_API friend Arb& operator*=(Arb&, const mpz_class&);

/// Return whether this Arb element exactly represents a floating point
/// number, i.e., whether its radius is zero, see [arb_is_exact]().
///
/// arbxx::Arb x{1};
/// x.is_exact()
/// // -> true
///
/// arbxx::Arb y{mpq_class{1, 3}};
/// arbxx::Arb y{mpq_class{1, 3}, 64};
/// y.is_exact()
/// // -> false
///
Expand All @@ -416,20 +428,30 @@ class LIBARBXX_API Arb {
///
bool is_finite() const;

/// Return the lower and the upper bound of this ball.
/// Return an lower and the upper bound of this ball.
/// See [arb_get_interval_arf]().
///
/// arbxx::Arb x{mpq_class{1, 3}};
/// arbxx::Arb x{mpq_class{1, 3}, 64};
/// auto bounds = static_cast<std::pair<arbxx::Arf, arbxx::Arf>>(x);
/// std::cout << bounds.first << ", " << bounds.second;
/// // -> 0.333333=1537228672809129301p-62, 0.333333=3074457345618258603p-63
///
explicit operator std::pair<Arf, Arf>() const;

/// Return a printable representation of this element.
/// Note that parsing the string might not produce the same element, see
/// https://github.com/fredrik-johansson/arb/issues/412.
///
/// arbxx::Arb x{mpq_class{1, 3}, 64};
/// static_cast<std::string>(x)
/// // -> [0.333333333333333333315263297125241592766542453318834304809570313 +/- 2.72e-20]
///
explicit operator std::string() const;

/// Return the midpoint of this ball rounded to the closest double.
/// Note that ties are rounded to even.
///
/// arbxx::Arb x{mpq_class{1, 3}};
/// arbxx::Arb x{mpq_class{1, 3}, 64};
/// static_cast<double>(x)
/// // -> 0.333333
///
Expand All @@ -439,23 +461,35 @@ class LIBARBXX_API Arb {
///
/// #include <arbxx/arf.hpp>
///
/// arbxx::Arb x{mpq_class{1, 3}};
/// arbxx::Arb x{mpq_class{1, 3}, 64};
/// std::cout << static_cast<arbxx::Arf>(x);
/// // -> 0.333333=6148914691236517205p-64
///
explicit operator Arf() const;

// TODO: Expose this operator also as midpoint().

/// Write this element to the output stream, see [arb_get_str]().
LIBARBXX_API friend std::ostream& operator<<(std::ostream&, const Arb&);

/// Return a reference to the underlying [arb_t]() element for direct
/// manipulation with the C API of Arb.
::arb_t& arb_t();

/// TODO
inline operator ::arb_t&() {
return t;
}

/// Return a const reference to the underlying [arb_t]() element for direct
/// manipulation with the C API of Arb.
const ::arb_t& arb_t() const;

// TODO
inline operator const ::arb_t&() const {
return t;
}

/// Return an exact zero element, i.e., the ball of radius zero centered at
/// zero.
///
Expand Down Expand Up @@ -559,6 +593,10 @@ class LIBARBXX_API Arb {
///
bool equal(const Arb&) const;

/// Return whether this element contains the entire other ball, see
/// [arb_contains]().
bool contains(const Arb&) const;

/// Swap two elements efficiently, see [arb_swap]().
/// Used by some STL containers.
///
Expand All @@ -572,10 +610,6 @@ class LIBARBXX_API Arb {
///
LIBARBXX_API friend void swap(Arb&, Arb&);

// Syntactic sugar for Yap, so that expresions such as x += y(64, Arf::Rount::NEAR) work.
template <typename... Args>
LIBARBXX_LOCAL decltype(auto) operator()(Args&&...) const;

private:
/// TODO
/// The underlying arb_t; use arb_t() to get a reference to it.
Expand Down
Loading