Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
126 changes: 116 additions & 10 deletions src/runner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,15 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//

// C++ headers
#include <type_traits> // for std::is_same_v

// libsemigroups headers
#include <libsemigroups/runner.hpp>

// pybind11....
#include <pybind11/chrono.h>
#include <pybind11/eval.h> // for pybind11::exec
#include <pybind11/functional.h>
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
Expand All @@ -31,11 +35,69 @@
namespace libsemigroups {
namespace py = pybind11;

namespace {
using std::chrono::high_resolution_clock;
using std::chrono::system_clock;
using std::chrono::time_point_cast;

// The template is required here, rather than just specifying
// high_resolution_clock::time_point as the parameter type, so that the
// discarded branch of the constexpr if does not get fully checked by the
// compiler.
template <typename TimePoint>
system_clock::time_point to_system_impl(TimePoint const& tp) {
static_assert(
std::is_same_v<TimePoint, high_resolution_clock::time_point>);

if constexpr (std::is_same_v<high_resolution_clock, system_clock>) {
return time_point_cast<system_clock::duration>(tp);
} else {
// Account for the difference between system_clock and
// high_resolution_clock. This function is heavily based on the code
// described in the answers to this stack overflow issues:
// https://stackoverflow.com/questions/35282308/convert-between-c11-clocks
//
// Whilst the times between the following two lines won't be exactly the
// same, the testing done in that stack overflow thread showed that the
// error in conversion is on the order of magnitude of hundreds of
// nanoseconds. Since Python's datetime module works with microsecond
// precision, this error is acceptable.
auto sys_now = system_clock::now();
auto high_res_now = high_resolution_clock::now();
return time_point_cast<system_clock::duration>(tp - high_res_now
+ sys_now);
}
}

system_clock::time_point
to_system(high_resolution_clock::time_point const& tp) {
return to_system_impl(tp);
}
} // namespace

void init_reporter(py::module& m) {
m.def("delta",
&delta,
py::arg("t"),
R"pbdoc(
m.def(
"delta",
[](std::chrono::system_clock::time_point const& t) {
py::exec(R"(
from warnings import warn

warn(
"delta is deprecated, and will be removed from libsemigroups_pybind11 in v2. Instead, use datetime.now() - t.",
DeprecationWarning,
2
)
)");

// We don't call libsemigroups::delta due to complications in the
// conversion between C++ time points and Python time points.
// Specifically relating to the fact they seem to be different on Mac
// and Linux
return std::chrono::duration_cast<std::chrono::nanoseconds>(
std::chrono::system_clock::now() - t);
},
py::arg("t"),
R"pbdoc(
:sig=(t: datetime.datetime) -> datetime.timedelta:
The time between the given point and now.

Expand All @@ -50,6 +112,13 @@ The time between the given point and now.

:rtype:
datetime.timedelta

.. deprecated:: 1.1
This will be removed from ``libsemigroups_pybind11`` in v2. Instead, use
``datetime.datetime.now() - t``.

.. seealso::
:any:`datetime.datetime.now`.
)pbdoc");

py::class_<Reporter> thing(m,
Expand Down Expand Up @@ -165,9 +234,10 @@ Get the minimum elapsed time between reports.
:rtype:
datetime.timedelta
)pbdoc");
thing.def("start_time",
&Reporter::start_time,
R"pbdoc(
thing.def(
"start_time",
[](Reporter const& self) { return to_system(self.start_time()); },
R"pbdoc(
Get the start time.

This is the time point at which :any:`reset_start_time()` was last called,
Expand All @@ -188,9 +258,42 @@ Reset the start time (and last report) to now.
:returns: *self*.
:rtype: Reporter
)pbdoc");
thing.def("last_report",
&Reporter::last_report,
R"pbdoc(
thing.def(
"last_report",
[](Reporter& self) {
// As far as the Python interpreter is concerned, the following code
// block comes from a file called "<string>". When displaying the
// warning to the user, we skip this 'file' in the stack trace to
// improve the readability of the message.
// Additionally, if this function was called from the
// CxxWrapper.__get_attr__ function, we also ignore the cxx_wrapper.py
// file in the stack trace, for the same reasons. We need to do this
// dynamically because the file path depends on where the user has
// installed libsemigroups_pybind11.
py::exec(R"(
from warnings import warn
import os, sys

paths_to_skip = ["<string>"]
level = 2
message = "Reporter.last_report is deprecated, and will be removed from libsemigroups_pybind11 in v2."

try:
if "libsemigroups_pybind11/detail/cxx_wrapper.py" in __file__:
paths_to_skip.append(os.path.dirname(__file__))
level += 1
except NameError:
pass

if sys.version_info[1] >= 12:
warn(message, DeprecationWarning, skip_file_prefixes=tuple(paths_to_skip))
else:
warn(message, DeprecationWarning, level)
)");

return to_system(self.last_report());
},
R"pbdoc(
Get the time point of the last report. This function returns the time point of the
last report, as set by one of:

Expand All @@ -203,6 +306,9 @@ last report, as set by one of:

:rtype:
datetime.datetime

.. deprecated:: 1.1
This will be removed from ``libsemigroups_pybind11`` in v2.
)pbdoc");
thing.def("reset_last_report",
&Reporter::reset_last_report,
Expand Down
63 changes: 34 additions & 29 deletions src/todd-coxeter-impl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -460,7 +460,7 @@ Set the definition policy.

This function can be used to specify how to handle definitions. For details see
:any:`options.def_policy`. The default value of this setting is
``options.def_policy.no_stack_if_no_space``.
:any:`def_policy.no_stack_if_no_space`.

:param val: the policy to use.
:type val: ToddCoxeter.options.def_policy
Expand Down Expand Up @@ -494,7 +494,7 @@ The current value of the definition policy setting.
This function can be used to specify how which version of definition handling
to use. For details see :any:`options.def_version`.

The default value of this setting is ``options.def_version.two``.
The default value of this setting is :any:`def_version.two`.

:param val: the version to use.
:type val: ToddCoxeter.options.def_version
Expand All @@ -514,11 +514,11 @@ function returns the approx number of Felsch style definitions in each
phase of the `ACE <https://staff.itee.uq.edu.au/havas/>`_ style
strategies:

- ``options.strategy.CR``;
- ``options.strategy.R_over_C``;
- ``options.strategy.R_over_C``;
- ``options.strategy.Cr``; and
- ``options.strategy.Rc``.
- :any:`strategy.CR`;
- :any:`strategy.R_over_C`;
- :any:`strategy.R_over_C`;
- :any:`strategy.Cr`; and
- :any:`strategy.Rc`.

If the strategy is not one of those listed above, then this setting is
ignored.
Expand Down Expand Up @@ -546,11 +546,11 @@ This function can be used to set the approx number of Felsch style definitions
in each phase of the `ACE <https://staff.itee.uq.edu.au/havas/>`_
style strategies:

* ``options.strategy.CR``;
* ``options.strategy.R_over_C``;
* ``options.strategy.R_over_C``;
* ``options.strategy.Cr``; and
* ``options.strategy.Rc``.
* :any:`strategy.CR`;
* :any:`strategy.R_over_C`;
* :any:`strategy.R_over_C`;
* :any:`strategy.Cr`; and
* :any:`strategy.Rc`.

If the strategy is not one of those listed above, then this setting is
ignored.
Expand All @@ -576,11 +576,11 @@ Get the number of HLT style definitions in ACE strategies. This function
returns the approx number of HLT style definitions in each phase of
the `ACE <https://staff.itee.uq.edu.au/havas/>`_ style strategies:

- ``options.strategy.CR``;
- ``options.strategy.R_over_C``;
- ``options.strategy.R_over_C``;
- ``options.strategy.Cr``; and
- ``options.strategy.Rc``.
- :any:`strategy.CR`;
- :any:`strategy.R_over_C`;
- :any:`strategy.R_over_C`;
- :any:`strategy.Cr`; and
- :any:`strategy.Rc`.

If the strategy is not one of those listed above, then this setting is
ignored.
Expand Down Expand Up @@ -608,11 +608,11 @@ This function can be used to set the approx number of HLT style definitions in
each phase of the `ACE <https://staff.itee.uq.edu.au/havas/>`_
style strategies:

* ``options.strategy.CR``;
* ``options.strategy.R_over_C``;
* ``options.strategy.R_over_C``;
* ``options.strategy.Cr``; and
* ``options.strategy.Rc``.
* :any:`strategy.CR`;
* :any:`strategy.R_over_C`;
* :any:`strategy.R_over_C`;
* :any:`strategy.Cr`; and
* :any:`strategy.Rc`.

If the strategy is not one of those listed above, then this setting is ignored.

Expand Down Expand Up @@ -690,7 +690,7 @@ The default value of this setting is ``100000``.

Get the current value of the lookahead extent. This function returns the
current value of the lookahead extent setting. The default value of this
setting is ``options.lookahead_extent.partial``.
setting is :any:`lookahead_extent.partial`.

:returns:
The current lookahead extent.
Expand All @@ -710,8 +710,8 @@ Set the lookahead extent.

This function can be used to specify the extent of any lookaheads that might
take place in a congruence enumeration. The possible values are
``options.lookahead_extent.partial`` or ``options.lookahead_extent.full``. The
default value of this setting is ``options.lookahead_extent.partial``.
:any:`lookahead_extent.partial` or :any:`lookahead_extent.full`. The
default value of this setting is :any:`lookahead_extent.partial`.

:param val: the extent.
:type val: ToddCoxeter.options.lookahead_extent
Expand Down Expand Up @@ -969,6 +969,8 @@ lookaheads to be stopped early if the number of nodes being killed is too small
want to stop the lookahead early, since lookaheads take some time but may not
result in many nodes being killed).

The default value is `0.01`

:param val: the proportion of active nodes.
:type val: float

Expand Down Expand Up @@ -1006,9 +1008,10 @@ Set the style of lookahead.

This function can be used to set the style of any lookaheads that are performed
during the congruence enumeration. The possible values are
``options.lookahead_style.hlt`` and ``options.lookahead_style.felsch``.
:any:`lookahead_style.hlt` and
:any:`lookahead_style.felsch`.

The default value of this setting is ``options.lookahead_style.hlt``.
The default value of this setting is :any:`lookahead_style.hlt`.

:param val: the style of lookahead to use.
:type val: ToddCoxeter.options.lookahead_style
Expand All @@ -1018,7 +1021,9 @@ The default value of this setting is ``options.lookahead_style.hlt``.
)pbdoc");
thing.def(
"lower_bound",
[](ToddCoxeterImpl_ const& self) { return self.lower_bound(); },
[](ToddCoxeterImpl_ const& self) {
return from_int(self.lower_bound());
},
R"pbdoc(
:sig=(self: ToddCoxeter) -> int:

Expand Down Expand Up @@ -1120,7 +1125,7 @@ description of this setting.
Specify the congruence enumeration strategy.

The strategy used during the enumeration can be specified using this function.
The default value is :any:`options.strategy`.
The default value is :any:`strategy.hlt`.

:param val: value indicating which strategy to use.
:type val: ToddCoxeter.options.strategy
Expand Down
25 changes: 25 additions & 0 deletions tests/test_delta.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Copyright (c) 2022-2024 J. D. Mitchell
#
# Distributed under the terms of the GPL license version 3.
#
# The full license is in the file LICENSE, distributed with this software.
"""
This module contains some tests for the delta function.
"""

from datetime import datetime, timedelta
from time import sleep
import pytest

from libsemigroups_pybind11 import delta


def test_delta():
"""Simple test case for the bindings of delta"""
current_time = datetime.now()
wait_time = 10**-3
sleep(wait_time)
with pytest.deprecated_call():
diff = delta(current_time)
assert isinstance(diff, timedelta)
assert diff > timedelta(seconds=wait_time)
14 changes: 8 additions & 6 deletions tests/test_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,23 +11,26 @@
arising from runner.*pp in libsemigroups.
"""

from datetime import timedelta
from datetime import timedelta, datetime
import pytest

from libsemigroups_pybind11 import Reporter, delta
from libsemigroups_pybind11 import Reporter


def test_reporter_000():
"""
Simple test case for the bindings of Reporter.
"""
creation_time = datetime.now()
r = Reporter()
assert not r.report()
assert r.report_every() == timedelta(seconds=1)
r.report_every(timedelta(seconds=2))
assert r.report_every() == timedelta(seconds=2)
r.last_report()
r.reset_last_report()
assert delta(r.last_report()) < timedelta(seconds=1)
assert isinstance(r.start_time(), datetime)
assert creation_time < r.start_time() < datetime.now()
with pytest.deprecated_call():
r.last_report()
r.report_prefix("Banana")
assert r.report_prefix() == "Banana"
r.init()
Expand All @@ -40,7 +43,6 @@ def test_reporter_000():
assert s is not r
assert s.report_prefix() == "Banana"
assert s.report_every() == timedelta(seconds=32)
assert s.last_report() == r.last_report()
s.init()
assert s.report_prefix() == ""
assert s.report_every() == timedelta(seconds=1)
Expand Down
Loading