simhub 0.1.0__py3-none-any.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.
- simhub/__init__.py +239 -0
- simhub/asset_cli.py +58 -0
- simhub/base.py +564 -0
- simhub/batch.py +334 -0
- simhub/blender.py +860 -0
- simhub/blender_replay.py +144 -0
- simhub/checks.py +415 -0
- simhub/cleaning.py +92 -0
- simhub/client.py +569 -0
- simhub/datasets.py +304 -0
- simhub/emit.py +283 -0
- simhub/env.py +264 -0
- simhub/errors.py +116 -0
- simhub/evaluation.py +117 -0
- simhub/hf.py +130 -0
- simhub/ids.py +29 -0
- simhub/kinematics.py +205 -0
- simhub/mjcf.py +665 -0
- simhub/model_cli.py +83 -0
- simhub/models.py +233 -0
- simhub/named_assets.py +365 -0
- simhub/notebook.py +237 -0
- simhub/objects.py +180 -0
- simhub/openfoam.py +231 -0
- simhub/particulate.py +508 -0
- simhub/policies.py +152 -0
- simhub/project.py +427 -0
- simhub/recording.py +686 -0
- simhub/registry.py +101 -0
- simhub/replay.py +116 -0
- simhub/report.py +403 -0
- simhub/robots.py +349 -0
- simhub/rollout.py +613 -0
- simhub/run_cli.py +229 -0
- simhub/runner.py +535 -0
- simhub/schema.py +93 -0
- simhub/scorer.py +481 -0
- simhub/session.py +507 -0
- simhub/single.py +205 -0
- simhub/spec.py +352 -0
- simhub/studio.py +312 -0
- simhub/suite.py +237 -0
- simhub/task.py +628 -0
- simhub/types.py +518 -0
- simhub/viewer.py +444 -0
- simhub-0.1.0.dist-info/METADATA +514 -0
- simhub-0.1.0.dist-info/RECORD +49 -0
- simhub-0.1.0.dist-info/WHEEL +4 -0
- simhub-0.1.0.dist-info/entry_points.txt +2 -0
simhub/__init__.py
ADDED
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
"""simhub: define an environment, a policy and a task in Python; run them anywhere.
|
|
2
|
+
|
|
3
|
+
Two things ship from this package and they are built from one source tree.
|
|
4
|
+
|
|
5
|
+
**The library** -- what you import. Three small protocols (an environment, a
|
|
6
|
+
policy, a task), one episode loop, one recorder, and a client that launches
|
|
7
|
+
batches of the same thing on hosted GPUs::
|
|
8
|
+
|
|
9
|
+
import simhub
|
|
10
|
+
|
|
11
|
+
@simhub.environment("warehouse-pick")
|
|
12
|
+
def build(cell_seed: int = 7) -> simhub.Env:
|
|
13
|
+
from .mjcf import build_mjcf # heavy imports inside
|
|
14
|
+
return PickEnv(build_mjcf(cell_seed))
|
|
15
|
+
|
|
16
|
+
@simhub.policy("scripted")
|
|
17
|
+
def scripted(env):
|
|
18
|
+
from .policies import ScriptedPick
|
|
19
|
+
return ScriptedPick(env)
|
|
20
|
+
|
|
21
|
+
simhub.run("warehouse-pick", "scripted", out="out/one") # here, now
|
|
22
|
+
|
|
23
|
+
sh = simhub.connect()
|
|
24
|
+
batch = sh.project("./warehouse_pick").launch(
|
|
25
|
+
env="warehouse-pick",
|
|
26
|
+
policies=["scripted", "groot-ft"],
|
|
27
|
+
seeds=range(32),
|
|
28
|
+
task=simhub.Task(placed="eq:1", collisions="eq:0"),
|
|
29
|
+
record=simhub.Record(cameras=["hero", "wrist"], video="hero"),
|
|
30
|
+
)
|
|
31
|
+
batch.wait(); batch.report().table()
|
|
32
|
+
|
|
33
|
+
**The server** -- the artifact store, job queue and workers under
|
|
34
|
+
``simhub.api``, ``simhub.worker`` and ``simhub.adapters``. Never published;
|
|
35
|
+
installed from source in this repository.
|
|
36
|
+
|
|
37
|
+
Both live here so that ``protocol.py`` -- the thing most likely to drift --
|
|
38
|
+
exists exactly once. The two distributions must never be installed into the
|
|
39
|
+
same virtual environment; they provide the same import path.
|
|
40
|
+
|
|
41
|
+
Everything below is imported lazily. Importing ``simhub`` in an API container
|
|
42
|
+
must not cost numpy, and importing it in a notebook must not cost FastAPI.
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
from __future__ import annotations
|
|
46
|
+
|
|
47
|
+
from typing import TYPE_CHECKING, Any
|
|
48
|
+
|
|
49
|
+
__version__ = "0.1.0"
|
|
50
|
+
|
|
51
|
+
if TYPE_CHECKING: # pragma: no cover - names for a type checker, bound lazily below
|
|
52
|
+
from simhub.batch import Batch as Batch
|
|
53
|
+
from simhub.checks import CheckReport as CheckReport
|
|
54
|
+
from simhub.checks import check as check
|
|
55
|
+
from simhub.client import Client as Client
|
|
56
|
+
from simhub.client import SimhubError as SimhubError
|
|
57
|
+
from simhub.client import connect as connect
|
|
58
|
+
from simhub.env import Env as Env
|
|
59
|
+
from simhub.env import Obs as Obs
|
|
60
|
+
from simhub.env import StepInfo as StepInfo
|
|
61
|
+
from simhub.env import environment as environment
|
|
62
|
+
from simhub.env import make_env as make_env
|
|
63
|
+
from simhub.named_assets import NamedAsset as NamedAsset
|
|
64
|
+
from simhub.named_assets import asset as asset
|
|
65
|
+
from simhub.policies import Policy as Policy
|
|
66
|
+
from simhub.policies import make_policy as make_policy
|
|
67
|
+
from simhub.policies import policy as policy
|
|
68
|
+
from simhub.project import Project as Project
|
|
69
|
+
from simhub.recording import Recorder as Recorder
|
|
70
|
+
from simhub.recording import VideoWriter as VideoWriter
|
|
71
|
+
from simhub.report import Report as Report
|
|
72
|
+
from simhub.rollout import Record as Record
|
|
73
|
+
from simhub.rollout import RolloutResult as RolloutResult
|
|
74
|
+
from simhub.scorer import BestOfK as BestOfK
|
|
75
|
+
from simhub.scorer import MeanMetric as MeanMetric
|
|
76
|
+
from simhub.scorer import StageProgress as StageProgress
|
|
77
|
+
from simhub.scorer import SuccessRate as SuccessRate
|
|
78
|
+
from simhub.session import Session as Session
|
|
79
|
+
from simhub.spec import ArraySpec as ArraySpec
|
|
80
|
+
from simhub.spec import CameraSpec as CameraSpec
|
|
81
|
+
from simhub.spec import EnvSpec as EnvSpec
|
|
82
|
+
from simhub.spec import MetricSpec as MetricSpec
|
|
83
|
+
from simhub.spec import PolicySpec as PolicySpec
|
|
84
|
+
from simhub.suite import Episode as Episode
|
|
85
|
+
from simhub.suite import Eval as Eval
|
|
86
|
+
from simhub.suite import Suite as Suite
|
|
87
|
+
from simhub.task import Conditions as Conditions
|
|
88
|
+
from simhub.task import Score as Score
|
|
89
|
+
from simhub.task import Stages as Stages
|
|
90
|
+
from simhub.task import Task as Task
|
|
91
|
+
|
|
92
|
+
#: name -> the module it lives in. A table rather than a chain of try/imports
|
|
93
|
+
#: so that ``dir(simhub)`` is honest and a typo names the attribute rather
|
|
94
|
+
#: than failing somewhere inside an import.
|
|
95
|
+
#:
|
|
96
|
+
#: **No name here may also be a submodule of this package**, and that is a hard
|
|
97
|
+
#: rule rather than a style: importing ``simhub.policies`` makes the import
|
|
98
|
+
#: system set ``simhub.policies`` on this module, which would shadow a lazy
|
|
99
|
+
#: export of the same name -- so ``simhub.policy`` would be the decorator until
|
|
100
|
+
#: something imported the module and the module afterwards. That is why the
|
|
101
|
+
#: decorators live in ``policies.py`` rather than ``policy.py``, the scene check
|
|
102
|
+
#: in ``checks.py``, and the object resolver in ``objects.py``.
|
|
103
|
+
#:
|
|
104
|
+
#: ``rollout`` is the one that kept its module name and therefore is *not*
|
|
105
|
+
#: exported: ``simhub.rollout`` is the module, and :func:`simhub.run` is the
|
|
106
|
+
#: one-episode call.
|
|
107
|
+
_EXPORTS = {
|
|
108
|
+
"asset": "simhub.named_assets",
|
|
109
|
+
"NamedAsset": "simhub.named_assets",
|
|
110
|
+
"ArraySpec": "simhub.spec",
|
|
111
|
+
"CameraSpec": "simhub.spec",
|
|
112
|
+
"EnvSpec": "simhub.spec",
|
|
113
|
+
"MetricSpec": "simhub.spec",
|
|
114
|
+
"PolicySpec": "simhub.spec",
|
|
115
|
+
"Env": "simhub.env",
|
|
116
|
+
"Obs": "simhub.env",
|
|
117
|
+
"StepInfo": "simhub.env",
|
|
118
|
+
"environment": "simhub.env",
|
|
119
|
+
"make_env": "simhub.env",
|
|
120
|
+
"Policy": "simhub.policies",
|
|
121
|
+
"policy": "simhub.policies",
|
|
122
|
+
"make_policy": "simhub.policies",
|
|
123
|
+
"Task": "simhub.task",
|
|
124
|
+
"Conditions": "simhub.task",
|
|
125
|
+
"Stages": "simhub.task",
|
|
126
|
+
"Score": "simhub.task",
|
|
127
|
+
"Verdict": "simhub.task",
|
|
128
|
+
"Record": "simhub.rollout",
|
|
129
|
+
"RolloutResult": "simhub.rollout",
|
|
130
|
+
"Recorder": "simhub.recording",
|
|
131
|
+
"VideoWriter": "simhub.recording",
|
|
132
|
+
"annotate_frame": "simhub.recording",
|
|
133
|
+
"read_trajectory": "simhub.recording",
|
|
134
|
+
"Emitter": "simhub.emit",
|
|
135
|
+
"Environment": "simhub.base",
|
|
136
|
+
"PolicyBase": "simhub.base",
|
|
137
|
+
"MujocoEnvironment": "simhub.base",
|
|
138
|
+
"ScriptedPolicy": "simhub.base",
|
|
139
|
+
"manifest": "simhub.single",
|
|
140
|
+
"loaded": "simhub.registry",
|
|
141
|
+
"Project": "simhub.project",
|
|
142
|
+
"Client": "simhub.client",
|
|
143
|
+
"SimhubError": "simhub.client",
|
|
144
|
+
"connect": "simhub.client",
|
|
145
|
+
"Session": "simhub.session",
|
|
146
|
+
"Batch": "simhub.batch",
|
|
147
|
+
"Suite": "simhub.suite",
|
|
148
|
+
"Episode": "simhub.suite",
|
|
149
|
+
"Eval": "simhub.suite",
|
|
150
|
+
"Report": "simhub.report",
|
|
151
|
+
"SuccessRate": "simhub.scorer",
|
|
152
|
+
"MeanMetric": "simhub.scorer",
|
|
153
|
+
"StageProgress": "simhub.scorer",
|
|
154
|
+
"BestOfK": "simhub.scorer",
|
|
155
|
+
"AirTransport": "simhub.particulate",
|
|
156
|
+
"CoatingParameters": "simhub.particulate",
|
|
157
|
+
"ExtractionContact": "simhub.particulate",
|
|
158
|
+
"ParticleClass": "simhub.particulate",
|
|
159
|
+
"WetCoating": "simhub.particulate",
|
|
160
|
+
"CleaningTool": "simhub.cleaning",
|
|
161
|
+
"SurfaceContamination": "simhub.cleaning",
|
|
162
|
+
"render_cycles": "simhub.replay",
|
|
163
|
+
"check": "simhub.checks",
|
|
164
|
+
"CheckReport": "simhub.checks",
|
|
165
|
+
"assets": "simhub.objects",
|
|
166
|
+
"weights": "simhub.objects",
|
|
167
|
+
"check_access": "simhub.objects",
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
__all__ = [*sorted(_EXPORTS), "run", "__version__"]
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def __getattr__(name: str) -> Any:
|
|
174
|
+
module = _EXPORTS.get(name)
|
|
175
|
+
if module is None:
|
|
176
|
+
raise AttributeError(f"module 'simhub' has no attribute {name!r}")
|
|
177
|
+
import importlib
|
|
178
|
+
|
|
179
|
+
return getattr(importlib.import_module(module), name)
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def __dir__():
|
|
183
|
+
return sorted(__all__)
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def run(
|
|
187
|
+
env: str,
|
|
188
|
+
policy: str = "simhub:hold",
|
|
189
|
+
*,
|
|
190
|
+
seed: int = 0,
|
|
191
|
+
out: Any = None,
|
|
192
|
+
task: Any = None,
|
|
193
|
+
record: Any = None,
|
|
194
|
+
max_steps: Any = None,
|
|
195
|
+
env_kwargs: Any = None,
|
|
196
|
+
policy_kwargs: Any = None,
|
|
197
|
+
prompt: Any = None,
|
|
198
|
+
):
|
|
199
|
+
"""One episode, in this interpreter, writing the hosted output layout.
|
|
200
|
+
|
|
201
|
+
A development affordance, and saying so plainly matters: anybody can step
|
|
202
|
+
MuJoCo in a Python loop for free, so a library whose pitch is "we wrap your
|
|
203
|
+
loop" has no reason to exist. What this is for is the debug cycle -- a
|
|
204
|
+
``NameError`` should cost a second, not a round trip -- and for one
|
|
205
|
+
property that is worth more than it looks.
|
|
206
|
+
|
|
207
|
+
**The laptop predicts the cluster.** This is the same
|
|
208
|
+
:func:`~simhub.rollout.rollout` the container calls, writing the same
|
|
209
|
+
files, so a local result and a hosted result are the same artifacts read by
|
|
210
|
+
the same report code. A test asserts they stay identical for a fixed seed;
|
|
211
|
+
the moment they diverge, iterating locally stops being worth anything.
|
|
212
|
+
|
|
213
|
+
What it is *not* is the product. A 3-billion-parameter policy on a real
|
|
214
|
+
GPU, sixty episodes at once, photorealistic rendering, a fine-tune on what
|
|
215
|
+
you recorded, and a number with a content digest behind it are all the
|
|
216
|
+
hosted path, and none of them fits here.
|
|
217
|
+
"""
|
|
218
|
+
from pathlib import Path
|
|
219
|
+
|
|
220
|
+
from simhub.env import make_env
|
|
221
|
+
from simhub.policies import make_policy
|
|
222
|
+
from simhub.rollout import Record, rollout
|
|
223
|
+
|
|
224
|
+
instance = make_env(env, **(env_kwargs or {}))
|
|
225
|
+
if prompt:
|
|
226
|
+
import dataclasses
|
|
227
|
+
|
|
228
|
+
instance.spec = dataclasses.replace(instance.spec, prompt=prompt)
|
|
229
|
+
controller = make_policy(policy, instance, **(policy_kwargs or {}))
|
|
230
|
+
return rollout(
|
|
231
|
+
instance,
|
|
232
|
+
controller,
|
|
233
|
+
seed=seed,
|
|
234
|
+
out=Path(out) if out is not None else None,
|
|
235
|
+
task=task,
|
|
236
|
+
record=record or Record(),
|
|
237
|
+
max_steps=max_steps,
|
|
238
|
+
policy_name=policy,
|
|
239
|
+
)
|
simhub/asset_cli.py
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"""Authenticated CLI for named environment assets, shared by both installs."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import argparse
|
|
5
|
+
import json
|
|
6
|
+
import sys
|
|
7
|
+
from typing import Optional, Sequence
|
|
8
|
+
|
|
9
|
+
from simhub.client import Client, SimhubError
|
|
10
|
+
from simhub.named_assets import AssetError, fetch
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def main(argv: Optional[Sequence[str]] = None) -> int:
|
|
14
|
+
parser = argparse.ArgumentParser(prog="simhub asset", description=__doc__)
|
|
15
|
+
parser.add_argument("--url", help="API URL (default: SIMHUB_API_URL)")
|
|
16
|
+
commands = parser.add_subparsers(dest="command", required=True)
|
|
17
|
+
upload = commands.add_parser("upload", help="Upload a named URDF and its meshes, or a mesh")
|
|
18
|
+
upload.add_argument("source")
|
|
19
|
+
upload.add_argument("--env", required=True, help="hosted environment name; created if absent")
|
|
20
|
+
upload.add_argument("--name", required=True, help="asset name within the environment")
|
|
21
|
+
upload.add_argument("--root", help="allowed dependency root (default: source directory)")
|
|
22
|
+
upload.add_argument("--package", action="append", default=[], metavar="NAME=PATH",
|
|
23
|
+
help="resolve a ROS package:// prefix; repeat for multiple packages")
|
|
24
|
+
upload.add_argument("--message", default="")
|
|
25
|
+
listing = commands.add_parser("list", help="List the latest revision of each named asset")
|
|
26
|
+
listing.add_argument("--env", required=True)
|
|
27
|
+
listing.add_argument("--version", default="latest")
|
|
28
|
+
get = commands.add_parser("get", help="Download and verify an asset and its dependencies")
|
|
29
|
+
get.add_argument("reference", help="environment/name[@version]")
|
|
30
|
+
get.add_argument("--out", required=True)
|
|
31
|
+
args = parser.parse_args(argv)
|
|
32
|
+
try:
|
|
33
|
+
with Client(url=args.url) as client:
|
|
34
|
+
if args.command == "upload":
|
|
35
|
+
packages = {}
|
|
36
|
+
for item in args.package:
|
|
37
|
+
key, separator, path = item.partition("=")
|
|
38
|
+
if not separator or not key or not path or key in packages:
|
|
39
|
+
raise AssetError("each --package must be a unique NAME=PATH")
|
|
40
|
+
packages[key] = path
|
|
41
|
+
result = client.upload_asset(
|
|
42
|
+
args.source, environment=args.env, name=args.name, root=args.root,
|
|
43
|
+
packages=packages, message=args.message,
|
|
44
|
+
)
|
|
45
|
+
elif args.command == "list":
|
|
46
|
+
result = client.named_assets(args.env, args.version)
|
|
47
|
+
else:
|
|
48
|
+
resolved = fetch(client, args.reference, into=args.out)
|
|
49
|
+
result = {"path": str(resolved.path), "digest": resolved.digest}
|
|
50
|
+
print(json.dumps(result, indent=2))
|
|
51
|
+
return 0
|
|
52
|
+
except (AssetError, SimhubError, OSError, ValueError) as exc:
|
|
53
|
+
print(str(exc), file=sys.stderr)
|
|
54
|
+
return 2
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
if __name__ == "__main__":
|
|
58
|
+
raise SystemExit(main())
|