cuju 0.1.0__py3-none-win_amd64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. cuju/__init__.py +142 -0
  2. cuju/_batch/__init__.py +4 -0
  3. cuju/_batch/allocator.py +52 -0
  4. cuju/_batch/assets.py +68 -0
  5. cuju/_batch/environment.py +1502 -0
  6. cuju/_batch/frame_stack.py +114 -0
  7. cuju/_batch/library.py +276 -0
  8. cuju/_batch/log.py +96 -0
  9. cuju/_batch/memory.py +70 -0
  10. cuju/_batch/operators.py +55 -0
  11. cuju/_batch/pinned.py +39 -0
  12. cuju/_batch/state.py +77 -0
  13. cuju/_checksums.py +60 -0
  14. cuju/_python_players.py +215 -0
  15. cuju/_replays.py +411 -0
  16. cuju/_version.py +6 -0
  17. cuju/action_log.py +371 -0
  18. cuju/check.py +479 -0
  19. cuju/cuju_cpu.dll +0 -0
  20. cuju/cuju_cuda.dll +0 -0
  21. cuju/data/SHA256SUMS +20 -0
  22. cuju/data/animations.bin +0 -0
  23. cuju/data/assets/11_vs_11_competition_l0_r0.cjb +0 -0
  24. cuju/data/assets/11_vs_11_easy_stochastic_l0_r0.cjb +0 -0
  25. cuju/data/assets/11_vs_11_hard_stochastic_l0_r0.cjb +0 -0
  26. cuju/data/assets/11_vs_11_kaggle_l0_r0.cjb +0 -0
  27. cuju/data/assets/11_vs_11_stochastic_l0_r0.cjb +0 -0
  28. cuju/data/assets/1_vs_1_easy_l0_r0.cjb +0 -0
  29. cuju/data/assets/5_vs_5_l0_r0.cjb +0 -0
  30. cuju/data/assets/academy_3_vs_1_with_keeper_l0_r0.cjb +0 -0
  31. cuju/data/assets/academy_corner_l0_r0.cjb +0 -0
  32. cuju/data/assets/academy_counterattack_easy_l0_r0.cjb +0 -0
  33. cuju/data/assets/academy_counterattack_hard_l0_r0.cjb +0 -0
  34. cuju/data/assets/academy_empty_goal_close_l0_r0.cjb +0 -0
  35. cuju/data/assets/academy_empty_goal_l0_r0.cjb +0 -0
  36. cuju/data/assets/academy_pass_and_shoot_with_keeper_l0_r0.cjb +0 -0
  37. cuju/data/assets/academy_run_pass_and_shoot_with_keeper_l0_r0.cjb +0 -0
  38. cuju/data/assets/academy_run_to_score_l0_r0.cjb +0 -0
  39. cuju/data/assets/academy_run_to_score_with_keeper_l0_r0.cjb +0 -0
  40. cuju/data/assets/academy_single_goal_versus_lazy_l0_r0.cjb +0 -0
  41. cuju/data/assets/test_example_multiagent_l0_r0.cjb +0 -0
  42. cuju/data/digests.json +186 -0
  43. cuju/data/scenarios/11_vs_11_competition.py +45 -0
  44. cuju/data/scenarios/11_vs_11_easy_stochastic.py +44 -0
  45. cuju/data/scenarios/11_vs_11_hard_stochastic.py +44 -0
  46. cuju/data/scenarios/11_vs_11_kaggle.py +46 -0
  47. cuju/data/scenarios/11_vs_11_stochastic.py +44 -0
  48. cuju/data/scenarios/1_vs_1_easy.py +25 -0
  49. cuju/data/scenarios/5_vs_5.py +33 -0
  50. cuju/data/scenarios/academy_3_vs_1_with_keeper.py +28 -0
  51. cuju/data/scenarios/academy_corner.py +45 -0
  52. cuju/data/scenarios/academy_counterattack_easy.py +45 -0
  53. cuju/data/scenarios/academy_counterattack_hard.py +45 -0
  54. cuju/data/scenarios/academy_empty_goal.py +25 -0
  55. cuju/data/scenarios/academy_empty_goal_close.py +25 -0
  56. cuju/data/scenarios/academy_pass_and_shoot_with_keeper.py +27 -0
  57. cuju/data/scenarios/academy_run_pass_and_shoot_with_keeper.py +27 -0
  58. cuju/data/scenarios/academy_run_to_score.py +30 -0
  59. cuju/data/scenarios/academy_run_to_score_with_keeper.py +30 -0
  60. cuju/data/scenarios/academy_single_goal_versus_lazy.py +49 -0
  61. cuju/data/scenarios/test_example_multiagent.py +28 -0
  62. cuju/dumps.py +909 -0
  63. cuju/env.py +1130 -0
  64. cuju/errors.py +92 -0
  65. cuju/gfootball/__init__.py +30 -0
  66. cuju/gfootball/dump_to_txt.py +52 -0
  67. cuju/gfootball/env/__init__.py +36 -0
  68. cuju/gfootball/env/config.py +284 -0
  69. cuju/gfootball/env/football_action_set.py +158 -0
  70. cuju/gfootball/env/football_env.py +68 -0
  71. cuju/gfootball/env/observation_preprocessing.py +98 -0
  72. cuju/gfootball/env/player_base.py +44 -0
  73. cuju/gfootball/env/players/__init__.py +9 -0
  74. cuju/gfootball/env/players/replay.py +40 -0
  75. cuju/gfootball/env/scenario_builder.py +70 -0
  76. cuju/gfootball/env/script_helpers.py +102 -0
  77. cuju/gfootball/env/wrappers.py +523 -0
  78. cuju/gfootball/replay.py +80 -0
  79. cuju/gfootball/scenarios.py +38 -0
  80. cuju/kaggle.py +266 -0
  81. cuju/multi_device.py +382 -0
  82. cuju/players.py +260 -0
  83. cuju/raw.py +93 -0
  84. cuju/scenarios.py +657 -0
  85. cuju/wrappers.py +325 -0
  86. cuju-0.1.0.dist-info/METADATA +287 -0
  87. cuju-0.1.0.dist-info/RECORD +96 -0
  88. cuju-0.1.0.dist-info/WHEEL +5 -0
  89. cuju-0.1.0.dist-info/licenses/LICENSE +202 -0
  90. cuju-0.1.0.dist-info/licenses/LICENSES/BSD-3-Clause.txt +31 -0
  91. cuju-0.1.0.dist-info/licenses/LICENSES/BSL-1.0.txt +23 -0
  92. cuju-0.1.0.dist-info/licenses/LICENSES/CC-BY-4.0.txt +396 -0
  93. cuju-0.1.0.dist-info/licenses/LICENSES/LGPL-2.1-or-later.txt +502 -0
  94. cuju-0.1.0.dist-info/licenses/LICENSES/MIT.txt +23 -0
  95. cuju-0.1.0.dist-info/licenses/LICENSES/SunPro.txt +8 -0
  96. cuju-0.1.0.dist-info/licenses/NOTICE +24 -0
cuju/__init__.py ADDED
@@ -0,0 +1,142 @@
1
+ # Copyright 2026 The cuju Authors
2
+ # Copyright 2019 Google LLC
3
+ # SPDX-License-Identifier: Apache-2.0
4
+
5
+ """Batched Google Research Football (GRF) environments on the GPU or the CPU, bit for bit.
6
+
7
+ `BatchEnvironment` steps many environments per call, and its outputs are PyTorch tensors that share
8
+ its buffers. The guide and the reference are in the repository's `docs/` (`index.md`, `api.md`).
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ import importlib.util
14
+ import sys
15
+ from typing import ClassVar
16
+
17
+ from ._batch.assets import bundled_assets, bundled_scenarios
18
+ from ._batch.environment import BatchEnvironment, memory_estimate
19
+ from ._batch.library import library_path
20
+ from ._batch.memory import _MemoryEstimate
21
+ from ._version import __version__
22
+ from .errors import (
23
+ ActionRangeError,
24
+ ArgumentError,
25
+ ChoiceError,
26
+ ClosedError,
27
+ ConfigurationError,
28
+ DeviceError,
29
+ EnvironmentIndexError,
30
+ Error,
31
+ LibraryError,
32
+ MissingFileError,
33
+ StateError,
34
+ UndefinedPathError,
35
+ )
36
+
37
+ __all__ = [
38
+ 'ActionRangeError',
39
+ 'ArgumentError',
40
+ 'BatchEnvironment',
41
+ 'ChoiceError',
42
+ 'ClosedError',
43
+ 'ConfigurationError',
44
+ 'DeviceError',
45
+ 'EnvironmentIndexError',
46
+ 'Error',
47
+ 'LibraryError',
48
+ 'MissingFileError',
49
+ 'MultiDeviceBatch',
50
+ 'StateError',
51
+ 'UndefinedPathError',
52
+ '__version__',
53
+ 'bundled_assets',
54
+ 'bundled_scenarios',
55
+ 'library_path',
56
+ 'memory_estimate',
57
+ ]
58
+
59
+ # The names keep `cuju` as their module, so that their reprs and pickles name the public path.
60
+ for _name in (
61
+ BatchEnvironment,
62
+ bundled_assets,
63
+ bundled_scenarios,
64
+ library_path,
65
+ memory_estimate,
66
+ _MemoryEstimate,
67
+ ):
68
+ _name.__module__ = __name__
69
+ del _name
70
+
71
+ # The digests and action logs build on `BatchEnvironment`.
72
+ from .action_log import ActionLog, Digest, digest, replay # noqa: E402
73
+
74
+ __all__ += ['ActionLog', 'Digest', 'digest', 'replay']
75
+
76
+ # Last, since it builds on `BatchEnvironment`.
77
+ from .multi_device import MultiDeviceBatch # noqa: E402
78
+
79
+
80
+ class _RegisteringLoader:
81
+ """Runs the loader of gymnasium or gym, then registers GRF's ids with the module."""
82
+
83
+ def __init__(self, loader, name):
84
+ self._loader = loader
85
+ self._name = name
86
+
87
+ def create_module(self, spec):
88
+ return self._loader.create_module(spec)
89
+
90
+ def exec_module(self, module):
91
+ self._loader.exec_module(module)
92
+ _RegistrationFinder.waiting.discard(self._name)
93
+ _register_ids(self._name)
94
+
95
+ def __getattr__(self, name):
96
+ return getattr(self._loader, name)
97
+
98
+
99
+ class _RegistrationFinder:
100
+ """Hands gymnasium and gym, when imported, a loader that then registers GRF's ids."""
101
+
102
+ waiting: ClassVar[set[str]] = set()
103
+ # The names being looked up by the other finders, which this one then leaves to them.
104
+ finding: ClassVar[set[str]] = set()
105
+
106
+ def find_spec(self, name, path=None, target=None):
107
+ if name not in self.waiting or name in self.finding:
108
+ return None
109
+ self.finding.add(name)
110
+ try:
111
+ spec = importlib.util.find_spec(name)
112
+ finally:
113
+ self.finding.discard(name)
114
+ if spec is not None and spec.loader is not None:
115
+ spec.loader = _RegisteringLoader(spec.loader, name)
116
+ return spec
117
+
118
+
119
+ def _register_ids(module):
120
+ # A `cuju.env` that is still importing registers the ids itself once it is done.
121
+ try:
122
+ from .env import _register_with
123
+ except ImportError:
124
+ return
125
+ _register_with(module)
126
+
127
+
128
+ def _register_gymnasium_ids():
129
+ """Registers GRF's ids with gymnasium and gym before 0.26, as importing `gfootball` does.
130
+
131
+ Each gets them once it is imported, so that `import cuju` imports neither.
132
+ """
133
+ for module in ('gymnasium', 'gym'):
134
+ if module in sys.modules:
135
+ _register_ids(module)
136
+ else:
137
+ _RegistrationFinder.waiting.add(module)
138
+ if not any(isinstance(finder, _RegistrationFinder) for finder in sys.meta_path):
139
+ sys.meta_path.insert(0, _RegistrationFinder())
140
+
141
+
142
+ _register_gymnasium_ids()
@@ -0,0 +1,4 @@
1
+ # Copyright 2026 The cuju Authors
2
+ # SPDX-License-Identifier: Apache-2.0
3
+
4
+ """The implementation of `BatchEnvironment`, which `cuju` exports."""
@@ -0,0 +1,52 @@
1
+ # Copyright 2026 The cuju Authors
2
+ # SPDX-License-Identifier: Apache-2.0
3
+ """The device memory of a batch through PyTorch's caching allocator."""
4
+
5
+ from __future__ import annotations
6
+
7
+ import ctypes
8
+
9
+ import torch
10
+
11
+ # The C signatures of `allocate` and `release` in `cuju_configuration`.
12
+ ALLOCATE = ctypes.CFUNCTYPE(ctypes.c_void_p, ctypes.c_size_t, ctypes.c_void_p)
13
+ RELEASE = ctypes.CFUNCTYPE(None, ctypes.c_void_p, ctypes.c_void_p)
14
+
15
+
16
+ class TorchAllocator:
17
+ """The `allocate` and `release` functions that the library calls, over torch's allocator.
18
+
19
+ The memory comes from the default stream of the device, which the library's copies and fills
20
+ use, and the library releases it only once the device is idle, so torch may hand it out again
21
+ at once.
22
+
23
+ Attributes:
24
+ allocate, release: The C functions, which must outlive the batch.
25
+ errors: The exceptions that torch raised in `allocate`, such as `torch.OutOfMemoryError`.
26
+ """
27
+
28
+ def __init__(self, device: torch.device) -> None:
29
+ stream = torch.cuda.default_stream(device)
30
+ errors: list[BaseException] = []
31
+
32
+ def allocate(size, _context):
33
+ try:
34
+ return torch.cuda.caching_allocator_alloc(size, device, stream)
35
+ # An exception cannot cross the library, so the batch raises it after the call.
36
+ except Exception as error:
37
+ errors.append(error)
38
+ return None
39
+
40
+ def release(pointer, _context):
41
+ torch.cuda.caching_allocator_delete(pointer)
42
+
43
+ self.allocate = ALLOCATE(allocate)
44
+ self.release = RELEASE(release)
45
+ self.errors = errors
46
+
47
+ def pointers(self) -> tuple[int, int]:
48
+ """Returns the addresses of `allocate` and `release` for `cuju_configuration`."""
49
+ return (
50
+ ctypes.cast(self.allocate, ctypes.c_void_p).value,
51
+ ctypes.cast(self.release, ctypes.c_void_p).value,
52
+ )
cuju/_batch/assets.py ADDED
@@ -0,0 +1,68 @@
1
+ # Copyright 2026 The cuju Authors
2
+ # SPDX-License-Identifier: Apache-2.0
3
+
4
+ """The asset bundles that the package ships, and the bundle that a name or path asks for."""
5
+
6
+ from __future__ import annotations
7
+
8
+ import os
9
+ import re
10
+
11
+ from ..errors import MissingFileError
12
+
13
+ _DATA_DIRECTORY = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'data')
14
+
15
+
16
+ def bundled_assets() -> list[str]:
17
+ """Returns the names of the asset bundles that the package ships, such as `5_vs_5_l4_r0`.
18
+
19
+ `BatchEnvironment` takes such a name `<scenario>_l<left agents>_r<right agents>` in place of a
20
+ path, with any number of agents of a shipped scenario.
21
+ """
22
+ directory = os.path.join(_DATA_DIRECTORY, 'assets')
23
+ if not os.path.isdir(directory):
24
+ return []
25
+ return sorted(name[: -len('.cjb')] for name in os.listdir(directory) if name.endswith('.cjb'))
26
+
27
+
28
+ def _split_name(name):
29
+ """Returns the scenario, left and right agents of `<scenario>_l<left>_r<right>`, or `None`."""
30
+ match = re.fullmatch(r'(.+)_l(\d+)_r(\d+)', name)
31
+ return None if match is None else (match.group(1), int(match.group(2)), int(match.group(3)))
32
+
33
+
34
+ def bundled_scenarios() -> list[str]:
35
+ """Returns the scenarios of the asset bundles that the package ships, such as `5_vs_5`."""
36
+ return sorted({split[0] for split in map(_split_name, bundled_assets()) if split})
37
+
38
+
39
+ def _resolve_asset(asset: str) -> tuple[str, tuple[int, int]]:
40
+ """Returns the bundle path of a path or name, and the agents per side it asks for (-1 keeps)."""
41
+ if callable(asset):
42
+ raise TypeError(
43
+ 'expected the path of a scenario file or of a bundle, found a function; pass the path '
44
+ 'of its file, or build a bundle with cuju.scenarios.build_bundle'
45
+ )
46
+ # GRF: `create_environment` gives a scenario file or a bare name one left agent.
47
+ if os.path.isfile(asset):
48
+ return asset, (1, 0) if asset.endswith('.py') else (-1, -1)
49
+ if asset.endswith(('.py', '.cjb')) or os.sep in asset or '/' in asset:
50
+ raise MissingFileError(f'expected a scenario file or a bundle at {asset!r}, found no file')
51
+ directory = os.path.join(_DATA_DIRECTORY, 'assets')
52
+ shipped = os.path.join(directory, asset + '.cjb')
53
+ if os.path.isfile(shipped):
54
+ return shipped, (-1, -1)
55
+ split = _split_name(asset)
56
+ if split is None and asset in bundled_scenarios():
57
+ split = (asset, 1, 0)
58
+ if split:
59
+ scenario, left, right = split
60
+ # The bundle with the fewest agents serves any counts; the library sets up the agents anew.
61
+ bases = [name for name in bundled_assets() if (_split_name(name) or ('',))[0] == scenario]
62
+ if bases:
63
+ base = min(bases, key=lambda name: sum(_split_name(name)[1:]))
64
+ return os.path.join(directory, base + '.cjb'), (left, right)
65
+ raise MissingFileError(
66
+ f'expected the path of an asset bundle or `<scenario>_l<left>_r<right>` of one of '
67
+ f'{bundled_scenarios()}, found {asset!r}'
68
+ )