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
2 changes: 1 addition & 1 deletion .github/workflows/nox.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:
fail-fast: false
matrix:
platform: [ubuntu-latest, macos-latest, windows-latest]
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
python-version: ["3.11", "3.12", "3.13"]

steps:
- uses: actions/checkout@v4
Expand Down
2 changes: 1 addition & 1 deletion adaptive/learner/average_learner.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from __future__ import annotations

from collections.abc import Callable
from math import sqrt
from typing import Callable

import cloudpickle
import numpy as np
Expand Down
3 changes: 1 addition & 2 deletions adaptive/learner/average_learner1D.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,9 @@
import math
import sys
from collections import defaultdict
from collections.abc import Iterable, Sequence
from collections.abc import Callable, Iterable, Sequence
from copy import deepcopy
from math import hypot
from typing import Callable

import numpy as np
import scipy.stats
Expand Down
20 changes: 5 additions & 15 deletions adaptive/learner/balancing_learner.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
from __future__ import annotations

import itertools
import sys
from collections import defaultdict
from collections.abc import Iterable, Sequence
from collections.abc import Callable, Iterable, Sequence
from contextlib import suppress
from functools import partial
from operator import itemgetter
from typing import Any, Callable, Union, cast
from typing import Any, Literal, TypeAlias, cast

import numpy as np

Expand All @@ -16,13 +15,6 @@
from adaptive.types import Int, Real
from adaptive.utils import cache_latest, named_product, restore

if sys.version_info >= (3, 10):
from typing import TypeAlias
else:
from typing_extensions import TypeAlias

from typing import Literal

try:
import pandas

Expand All @@ -38,11 +30,9 @@ def dispatch(child_functions: list[Callable], arg: Any) -> Any:

STRATEGY_TYPE: TypeAlias = Literal["loss_improvements", "loss", "npoints", "cycle"]

CDIMS_TYPE: TypeAlias = Union[
Sequence[dict[str, Any]],
tuple[Sequence[str], Sequence[tuple[Any, ...]]],
None,
]
CDIMS_TYPE: TypeAlias = (
Sequence[dict[str, Any]] | tuple[Sequence[str], Sequence[tuple[Any, ...]]] | None
)


class BalancingLearner(BaseLearner):
Expand Down
3 changes: 2 additions & 1 deletion adaptive/learner/base_learner.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
from __future__ import annotations

import abc
from collections.abc import Callable
from contextlib import suppress
from typing import TYPE_CHECKING, Any, Callable, TypeVar
from typing import TYPE_CHECKING, Any, TypeVar

import cloudpickle

Expand Down
3 changes: 2 additions & 1 deletion adaptive/learner/data_saver.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

import functools
from collections import OrderedDict
from typing import Any, Callable
from collections.abc import Callable
from typing import Any

from adaptive.learner.base_learner import BaseLearner, LearnerType
from adaptive.utils import copy_docstring_from
Expand Down
3 changes: 2 additions & 1 deletion adaptive/learner/integrator_learner.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@

import sys
from collections import defaultdict
from collections.abc import Callable
from math import sqrt
from operator import attrgetter
from typing import TYPE_CHECKING, Callable
from typing import TYPE_CHECKING

import cloudpickle
import numpy as np
Expand Down
44 changes: 15 additions & 29 deletions adaptive/learner/learner1D.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,9 @@
import collections.abc
import itertools
import math
import sys
from collections.abc import Sequence
from collections.abc import Callable, Sequence
from copy import copy, deepcopy
from typing import TYPE_CHECKING, Any, Callable, Optional, Union
from typing import TYPE_CHECKING, Any, TypeAlias

import cloudpickle
import numpy as np
Expand All @@ -24,12 +23,6 @@
partial_function_from_dataframe,
)

if sys.version_info >= (3, 10):
from typing import TypeAlias
else:
from typing_extensions import TypeAlias


try:
import pandas

Expand All @@ -42,28 +35,21 @@
# -- types --

# Commonly used types
Interval: TypeAlias = Union[tuple[float, float], tuple[float, float, int]]
NeighborsType: TypeAlias = SortedDict[float, list[Optional[float]]]
Interval: TypeAlias = tuple[float, float] | tuple[float, float, int]
NeighborsType: TypeAlias = SortedDict[float, list[float | None]]

# Types for loss_per_interval functions
XsType0: TypeAlias = tuple[float, float]
YsType0: TypeAlias = Union[tuple[float, float], tuple[np.ndarray, np.ndarray]]
XsType1: TypeAlias = tuple[
Optional[float], Optional[float], Optional[float], Optional[float]
]
YsType1: TypeAlias = Union[
tuple[Optional[float], Optional[float], Optional[float], Optional[float]],
tuple[
Optional[np.ndarray],
Optional[np.ndarray],
Optional[np.ndarray],
Optional[np.ndarray],
],
]
XsTypeN: TypeAlias = tuple[Optional[float], ...]
YsTypeN: TypeAlias = Union[
tuple[Optional[float], ...], tuple[Optional[np.ndarray], ...]
]
YsType0: TypeAlias = tuple[float, float] | tuple[np.ndarray, np.ndarray]
XsType1: TypeAlias = tuple[float | None, float | None, float | None, float | None]
YsType1: TypeAlias = (
tuple[float | None, float | None, float | None, float | None]
| tuple[
np.ndarray | None, np.ndarray | None, np.ndarray | None, np.ndarray | None
]
)
XsTypeN: TypeAlias = tuple[float | None, ...]
YsTypeN: TypeAlias = tuple[float | None, ...] | tuple[np.ndarray | None, ...]


__all__ = [
Expand Down Expand Up @@ -598,7 +584,7 @@ def tell(self, x: float, y: Float | Sequence[Float] | np.ndarray) -> None:
)

# either it is a float/int, if not, try casting to a np.array
if not isinstance(y, (float, int)):
if not isinstance(y, float | int):
y = np.asarray(y, dtype=float)

# Add point to the real data dict
Expand Down
3 changes: 1 addition & 2 deletions adaptive/learner/learner2D.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,9 @@
import itertools
import warnings
from collections import OrderedDict
from collections.abc import Iterable
from collections.abc import Callable, Iterable
from copy import copy
from math import sqrt
from typing import Callable

import cloudpickle
import numpy as np
Expand Down
10 changes: 2 additions & 8 deletions adaptive/learner/sequence_learner.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
from __future__ import annotations

import sys
from copy import copy
from typing import TYPE_CHECKING, Any
from typing import TYPE_CHECKING, Any, TypeAlias

import cloudpickle
from sortedcontainers import SortedDict, SortedSet
Expand All @@ -16,8 +15,7 @@
)

if TYPE_CHECKING:
from collections.abc import Sequence
from typing import Callable
from collections.abc import Callable, Sequence

try:
import pandas
Expand All @@ -27,10 +25,6 @@
except ModuleNotFoundError:
with_pandas = False

if sys.version_info >= (3, 10):
from typing import TypeAlias
else:
from typing_extensions import TypeAlias

PointType: TypeAlias = tuple[Int, Any]

Expand Down
89 changes: 29 additions & 60 deletions adaptive/runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,77 +8,48 @@
import itertools
import pickle
import platform
import sys
import time
import traceback
import warnings
from collections.abc import Callable
from contextlib import suppress
from datetime import datetime, timedelta
from importlib.util import find_spec
from typing import TYPE_CHECKING, Any, Callable, Literal, Optional, Union
from typing import TYPE_CHECKING, Any, Literal, TypeAlias

import loky

from adaptive import (
BalancingLearner,
DataSaver,
IntegratorLearner,
SequenceLearner,
)
from adaptive import BalancingLearner, DataSaver, IntegratorLearner, SequenceLearner
from adaptive.learner.base_learner import LearnerType
from adaptive.notebook_integration import in_ipynb, live_info, live_plot
from adaptive.utils import SequentialExecutor

ExecutorTypes: TypeAlias = Union[
concurrent.ProcessPoolExecutor,
concurrent.ThreadPoolExecutor,
SequentialExecutor,
loky.reusable_executor._ReusablePoolExecutor,
]
FutureTypes: TypeAlias = Union[concurrent.Future, asyncio.Future, asyncio.Task]

if TYPE_CHECKING:
import holoviews


if sys.version_info >= (3, 10):
from typing import TypeAlias
else:
from typing_extensions import TypeAlias


with_ipyparallel = find_spec("ipyparallel") is not None
with_distributed = find_spec("distributed") is not None
with_mpi4py = find_spec("mpi4py") is not None

if TYPE_CHECKING:
ExecutorTypes = Optional[()]
FutureTypes = Optional[()]

if with_distributed:
import distributed

ExecutorTypes = Optional[
Union[
ExecutorTypes, distributed.Client, distributed.cfexecutor.ClientExecutor
]
]

if with_mpi4py:
import mpi4py.futures

ExecutorTypes = Optional[Union[ExecutorTypes, mpi4py.futures.MPIPoolExecutor]]

if with_ipyparallel:
import ipyparallel
from ipyparallel.client.asyncresult import AsyncResult
import distributed
import ipyparallel
import mpi4py.futures

ExecutorTypes: TypeAlias = (
concurrent.ProcessPoolExecutor
| concurrent.ThreadPoolExecutor
| SequentialExecutor
| loky.reusable_executor._ReusablePoolExecutor
| distributed.Client
| distributed.cfexecutor.ClientExecutor
| mpi4py.futures.MPIPoolExecutor
| ipyparallel.Client
| ipyparallel.client.view.ViewExecutor
)
FutureTypes: TypeAlias = concurrent.Future | asyncio.Future

ExecutorTypes = Optional[
Union[
ExecutorTypes, ipyparallel.Client, ipyparallel.client.view.ViewExecutor
]
]
FutureTypes = Optional[Union[FutureTypes, AsyncResult]]

with suppress(ModuleNotFoundError):
import uvloop
Expand Down Expand Up @@ -203,7 +174,7 @@ def __init__(

self._max_tasks = ntasks

self._pending_tasks: dict[concurrent.Future, int] = {}
self._pending_tasks: dict[FutureTypes, int] = {}

# if we instantiate our own executor, then we are also responsible
# for calling 'shutdown'
Expand Down Expand Up @@ -292,7 +263,8 @@ def _process_futures(
pid = self._pending_tasks.pop(fut)
try:
y = fut.result()
t = time.time() - fut.start_time # total execution time
# total execution time
t = time.time() - fut.start_time # type: ignore[union-attr]
except Exception as e:
self._tracebacks[pid] = traceback.format_exc()
self._to_retry[pid] = self._to_retry.get(pid, 0) + 1
Expand Down Expand Up @@ -508,12 +480,12 @@ def _run(self) -> None:
try:
while not self.goal(self.learner):
futures = self._get_futures()
done, _ = concurrent.wait(futures, return_when=first_completed)
self._process_futures(done)
done, _ = concurrent.wait(futures, return_when=first_completed) # type: ignore[arg-type]
self._process_futures(done) # type: ignore[arg-type]
finally:
remaining = self._remove_unfinished()
if remaining:
concurrent.wait(remaining)
concurrent.wait(remaining) # type: ignore[arg-type]
# Some futures get their result set, despite being cancelled.
# see https://github.com/python-adaptive/adaptive/issues/319
with_result = {f for f in remaining if not f.cancelled() and f.done()}
Expand Down Expand Up @@ -835,13 +807,12 @@ async def _run(self) -> None:
try:
while not self.goal(self.learner):
futures = self._get_futures()
kw = {"loop": self.ioloop} if sys.version_info[:2] < (3, 10) else {}
done, _ = await asyncio.wait(futures, return_when=first_completed, **kw) # type: ignore[arg-type]
done, _ = await asyncio.wait(futures, return_when=first_completed) # type: ignore[arg-type,type-var]
self._process_futures(done)
finally:
remaining = self._remove_unfinished()
if remaining:
await asyncio.wait(remaining)
await asyncio.wait(remaining) # type: ignore[type-var]
self._cleanup()

def elapsed_time(self) -> float:
Expand Down Expand Up @@ -1062,9 +1033,7 @@ def _get_ncores(
import mpi4py.futures
if with_ipyparallel and isinstance(ex, ipyparallel.client.view.ViewExecutor):
return len(ex.view)
elif isinstance(
ex, (concurrent.ProcessPoolExecutor, concurrent.ThreadPoolExecutor)
):
elif isinstance(ex, concurrent.ProcessPoolExecutor | concurrent.ThreadPoolExecutor):
return ex._max_workers # type: ignore[union-attr]
elif isinstance(ex, loky.reusable_executor._ReusablePoolExecutor):
return ex._max_workers # type: ignore[union-attr]
Expand Down Expand Up @@ -1119,7 +1088,7 @@ def stop_after(*, seconds=0, minutes=0, hours=0) -> Callable[[LearnerType], bool

class _TimeGoal:
def __init__(self, dt: timedelta | datetime | int | float):
self.dt = dt if isinstance(dt, (timedelta, datetime)) else timedelta(seconds=dt)
self.dt = dt if isinstance(dt, timedelta | datetime) else timedelta(seconds=dt)
self.start_time = None

def __call__(self, _):
Expand Down
Loading
Loading