sim2sim-onepass 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.
@@ -0,0 +1,3 @@
1
+ from .version import __version__
2
+
3
+ __all__ = ["__version__"]
sim2sim_onepass/cli.py ADDED
@@ -0,0 +1,203 @@
1
+ from __future__ import annotations
2
+
3
+ import argparse
4
+ import json
5
+ import sys
6
+ from pathlib import Path
7
+
8
+ from sim2sim_onepass.paths import get_repo_paths
9
+ from sim2sim_onepass.utils.docs import first_lines, load_doc, repo_file_summary
10
+ from sim2sim_onepass.version import __version__
11
+ from sim2sim_onepass.wrappers.availability import report_environment, simulator_layout_available
12
+ from sim2sim_onepass.wrappers.quick_sanity import resolve_default_paths, run_quick_sanity, run_quick_sanity_demo
13
+ from sim2sim_onepass.wrappers.rollout import run_rollout_check
14
+
15
+
16
+ def print_json(payload: object) -> int:
17
+ print(json.dumps(payload, indent=2))
18
+ return 0
19
+
20
+
21
+ def cmd_info(_: argparse.Namespace) -> int:
22
+ paths = get_repo_paths()
23
+ payload = {
24
+ "project": "Sim2Sim-OnePass",
25
+ "version": __version__,
26
+ "package_identity": "lightweight companion package",
27
+ "repo_root": str(paths.root),
28
+ "canonical_outputs": str(paths.canonical_pass),
29
+ "pypi": "https://pypi.org/project/sim2sim-onepass/",
30
+ "docs": {label: str(path) for label, path in repo_file_summary()},
31
+ "website_branch": "gh-pages",
32
+ "package_scope": [
33
+ "repo navigation",
34
+ "embedded docs access",
35
+ "environment checks",
36
+ "quick sanity checks on paired datasets",
37
+ "thin rollout-check wrapper",
38
+ "guarded simulator workflow wrappers",
39
+ ],
40
+ }
41
+ return print_json(payload)
42
+
43
+
44
+ def cmd_doc(name: str) -> int:
45
+ print(first_lines(load_doc(name), limit=80))
46
+ return 0
47
+
48
+
49
+ def cmd_docs(_: argparse.Namespace) -> int:
50
+ for label, path in repo_file_summary():
51
+ print(f"{label}: {path}")
52
+ return 0
53
+
54
+
55
+ def cmd_check_env(_: argparse.Namespace) -> int:
56
+ return print_json(report_environment())
57
+
58
+
59
+ def cmd_quick_sanity(args: argparse.Namespace) -> int:
60
+ paths = get_repo_paths()
61
+ if args.demo:
62
+ return print_json(run_quick_sanity_demo())
63
+ default_b, default_m = resolve_default_paths(paths.root)
64
+ if args.bullet and args.mujoco:
65
+ bullet = Path(args.bullet)
66
+ mujoco = Path(args.mujoco)
67
+ elif default_b.exists() and default_m.exists():
68
+ bullet = default_b
69
+ mujoco = default_m
70
+ else:
71
+ raise RuntimeError(
72
+ "This command requires paired dataset inputs and does not run from package-only install by default.\n"
73
+ "Provide --bullet and --mujoco, or run `sim2sim-onepass quick-sanity --demo`."
74
+ )
75
+ return print_json(run_quick_sanity(bullet, mujoco))
76
+
77
+
78
+ def cmd_alignment_gate(_: argparse.Namespace) -> int:
79
+ if not simulator_layout_available():
80
+ print(
81
+ "This command requires the full curated Sim2Sim-OnePass repository layout.\n"
82
+ "It also requires simulator dependencies and public-repo workflow files.\n"
83
+ "This command is not available in lightweight package-only mode.",
84
+ file=sys.stderr,
85
+ )
86
+ return 2
87
+ print(
88
+ "This command requires the full curated Sim2Sim-OnePass repository layout and simulator dependencies.\n"
89
+ "Use the source workspace command preserved in QUICKSTART.md.",
90
+ file=sys.stderr,
91
+ )
92
+ return 2
93
+
94
+
95
+ def cmd_alignment_report(_: argparse.Namespace) -> int:
96
+ print(
97
+ "This command requires the full curated Sim2Sim-OnePass repository layout.\n"
98
+ "It depends on simulator collection scripts, paired dataset generation, and workflow files.\n"
99
+ "This command is not available in lightweight package-only mode.",
100
+ file=sys.stderr,
101
+ )
102
+ return 2
103
+
104
+
105
+ def cmd_rollout_check(args: argparse.Namespace) -> int:
106
+ result = run_rollout_check(
107
+ bullet=Path(args.bullet),
108
+ mujoco=Path(args.mujoco),
109
+ model_path=Path(args.model),
110
+ norm_path=Path(args.norm),
111
+ horizon=args.horizon,
112
+ nstart=args.nstart,
113
+ )
114
+ return print_json(result)
115
+
116
+
117
+ def build_parser() -> argparse.ArgumentParser:
118
+ parser = argparse.ArgumentParser(
119
+ prog="sim2sim-onepass",
120
+ description=(
121
+ "A lightweight companion package for the Sim2Sim-OnePass public research release.\n"
122
+ "It provides CLI access to documentation, results navigation, environment checks, and selected curated utilities."
123
+ ),
124
+ formatter_class=argparse.RawTextHelpFormatter,
125
+ epilog=(
126
+ "Command categories:\n"
127
+ " Standalone: info, quickstart, repo-map, results-summary, visual-index, docs, check-env\n"
128
+ " Dataset-dependent: quick-sanity, rollout-check\n"
129
+ " Full repo / simulator dependent: alignment-gate, alignment-report"
130
+ ),
131
+ )
132
+ sub = parser.add_subparsers(dest="command", required=True)
133
+
134
+ sub.add_parser("info", help="[standalone] show package and repo overview")
135
+ sub.add_parser("quickstart", help="[standalone] print quickstart guidance")
136
+ sub.add_parser("repo-map", help="[standalone] print repo structure guidance")
137
+ sub.add_parser("results-summary", help="[standalone] print canonical result summary")
138
+ sub.add_parser("visual-index", help="[standalone] print visual evidence index")
139
+ sub.add_parser("docs", help="[standalone] list key packaged docs")
140
+ sub.add_parser("check-env", help="[standalone] report Python and optional dependency availability")
141
+ sub.add_parser(
142
+ "alignment-gate",
143
+ help="[full repo] guarded simulator workflow wrapper; not standalone",
144
+ description="Full repo / simulator dependent command.",
145
+ )
146
+ sub.add_parser(
147
+ "alignment-report",
148
+ help="[full repo] guarded report wrapper; not standalone",
149
+ description="Full repo / simulator dependent command.",
150
+ )
151
+
152
+ quick = sub.add_parser(
153
+ "quick-sanity",
154
+ help="[dataset] inspect paired datasets or run tiny built-in demo",
155
+ description="Dataset-dependent command. Use --demo for the tiny standalone fixture.",
156
+ )
157
+ quick.add_argument("--bullet", default="")
158
+ quick.add_argument("--mujoco", default="")
159
+ quick.add_argument("--demo", action="store_true", help="run the tiny built-in synthetic paired fixture")
160
+
161
+ rollout = sub.add_parser(
162
+ "rollout-check",
163
+ help="[dataset+rollout extra] run rollout error check from paired data/model/norm",
164
+ description="Dataset-dependent command. Requires paired data, model, norm file, and the optional rollout extra.",
165
+ )
166
+ rollout.add_argument("--bullet", required=True)
167
+ rollout.add_argument("--mujoco", required=True)
168
+ rollout.add_argument("--model", required=True)
169
+ rollout.add_argument("--norm", required=True)
170
+ rollout.add_argument("--horizon", type=int, default=50)
171
+ rollout.add_argument("--nstart", type=int, default=50)
172
+
173
+ return parser
174
+
175
+
176
+ def main(argv: list[str] | None = None) -> int:
177
+ parser = build_parser()
178
+ args = parser.parse_args(argv)
179
+ mapping = {
180
+ "info": cmd_info,
181
+ "quickstart": lambda _: cmd_doc("QUICKSTART.md"),
182
+ "repo-map": lambda _: cmd_doc("REPO_MAP.md"),
183
+ "results-summary": lambda _: cmd_doc("RESULTS_SUMMARY.md"),
184
+ "visual-index": lambda _: cmd_doc("VISUAL_INDEX.md"),
185
+ "docs": cmd_docs,
186
+ "check-env": cmd_check_env,
187
+ "quick-sanity": cmd_quick_sanity,
188
+ "alignment-gate": cmd_alignment_gate,
189
+ "alignment-report": cmd_alignment_report,
190
+ "rollout-check": cmd_rollout_check,
191
+ }
192
+ try:
193
+ return mapping[args.command](args)
194
+ except FileNotFoundError as exc:
195
+ print(str(exc), file=sys.stderr)
196
+ return 2
197
+ except RuntimeError as exc:
198
+ print(str(exc), file=sys.stderr)
199
+ return 2
200
+
201
+
202
+ if __name__ == "__main__":
203
+ raise SystemExit(main())
@@ -0,0 +1,44 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ from pathlib import Path
5
+
6
+
7
+ def find_repo_root(start: Path | None = None) -> Path:
8
+ here = (start or Path(__file__)).resolve()
9
+ for candidate in [here, *here.parents]:
10
+ if (candidate / "README.md").exists() and (candidate / "RESULTS_SUMMARY.md").exists():
11
+ return candidate
12
+ return here.parents[2]
13
+
14
+
15
+ @dataclass(frozen=True)
16
+ class RepoPaths:
17
+ root: Path
18
+ readme: Path
19
+ quickstart: Path
20
+ repo_map: Path
21
+ results_summary: Path
22
+ visual_index: Path
23
+ package_notes: Path
24
+ outputs: Path
25
+ canonical_pass: Path
26
+ scripts: Path
27
+ configs: Path
28
+
29
+
30
+ def get_repo_paths(start: Path | None = None) -> RepoPaths:
31
+ root = find_repo_root(start)
32
+ return RepoPaths(
33
+ root=root,
34
+ readme=root / "README.md",
35
+ quickstart=root / "QUICKSTART.md",
36
+ repo_map=root / "REPO_MAP.md",
37
+ results_summary=root / "RESULTS_SUMMARY.md",
38
+ visual_index=root / "VISUAL_INDEX.md",
39
+ package_notes=root / "PYPI_PACKAGE_NOTES.md",
40
+ outputs=root / "outputs",
41
+ canonical_pass=root / "outputs" / "canonical_pass",
42
+ scripts=root / "scripts",
43
+ configs=root / "configs",
44
+ )
@@ -0,0 +1,30 @@
1
+ # Quickstart
2
+
3
+ Install editable from the repo:
4
+
5
+ ```powershell
6
+ python -m pip install -e .
7
+ ```
8
+
9
+ Published PyPI install:
10
+
11
+ ```powershell
12
+ python -m pip install sim2sim-onepass
13
+ ```
14
+
15
+ Package page:
16
+
17
+ ```text
18
+ https://pypi.org/project/sim2sim-onepass/
19
+ ```
20
+
21
+ Standalone-safe first commands:
22
+
23
+ ```powershell
24
+ sim2sim-onepass info
25
+ sim2sim-onepass check-env
26
+ sim2sim-onepass quickstart
27
+ sim2sim-onepass quick-sanity --demo
28
+ ```
29
+
30
+ For the full canonical docs and media paths, use the source repository checkout.
@@ -0,0 +1,19 @@
1
+ # Sim2Sim-OnePass Companion Package
2
+
3
+ This installed package is a lightweight companion package for the public Sim2Sim-OnePass research release.
4
+
5
+ It provides:
6
+
7
+ - installable CLI access
8
+ - embedded docs access
9
+ - results navigation
10
+ - environment checks
11
+ - selected lightweight utilities
12
+ - published package access via `https://pypi.org/project/sim2sim-onepass/`
13
+
14
+ It does not provide by default:
15
+
16
+ - full simulator environments
17
+ - full datasets
18
+ - full video/report artifacts
19
+ - complete standalone simulator reproduction from pip install alone
@@ -0,0 +1,8 @@
1
+ # Repo Map
2
+
3
+ This package ships a lightweight CLI and embedded markdown docs.
4
+
5
+ - standalone package code lives under `src/sim2sim_onepass/`
6
+ - canonical public docs stay in the source repository
7
+ - heavy outputs and media are intentionally not embedded in the wheel
8
+ - the website remains repo-based and is published separately
@@ -0,0 +1,9 @@
1
+ # Results Summary
2
+
3
+ The package can navigate to the canonical public results, but it does not embed the heavy media payload.
4
+
5
+ Use the source repository checkout to inspect:
6
+
7
+ - `RESULTS_SUMMARY.md`
8
+ - `VISUAL_INDEX.md`
9
+ - `outputs/canonical_pass/`
@@ -0,0 +1,10 @@
1
+ # Visual Index
2
+
3
+ The wheel keeps lightweight markdown only.
4
+
5
+ For the full visual proof bundle, use the source repository checkout and inspect:
6
+
7
+ - `VISUAL_INDEX.md`
8
+ - `outputs/canonical_pass/preview/`
9
+ - `outputs/canonical_pass/videos/`
10
+ - `outputs/canonical_pass/plots/`
@@ -0,0 +1 @@
1
+ """Embedded fallback docs for installed package users."""
@@ -0,0 +1 @@
1
+ """Utility helpers for the public package."""
@@ -0,0 +1,31 @@
1
+ from __future__ import annotations
2
+
3
+ from importlib import resources
4
+ from pathlib import Path
5
+
6
+ from sim2sim_onepass.paths import get_repo_paths
7
+
8
+
9
+ def load_doc(name: str) -> str:
10
+ paths = get_repo_paths()
11
+ repo_candidate = paths.root / name
12
+ if repo_candidate.exists():
13
+ return repo_candidate.read_text(encoding="utf-8")
14
+ return resources.files("sim2sim_onepass.resources").joinpath(name).read_text(encoding="utf-8")
15
+
16
+
17
+ def first_lines(text: str, limit: int = 40) -> str:
18
+ lines = text.strip().splitlines()
19
+ return "\n".join(lines[:limit]).strip() + ("\n..." if len(lines) > limit else "")
20
+
21
+
22
+ def repo_file_summary() -> list[tuple[str, Path]]:
23
+ paths = get_repo_paths()
24
+ return [
25
+ ("README", paths.readme),
26
+ ("QUICKSTART", paths.quickstart),
27
+ ("REPO_MAP", paths.repo_map),
28
+ ("RESULTS_SUMMARY", paths.results_summary),
29
+ ("VISUAL_INDEX", paths.visual_index),
30
+ ("PYPI_PACKAGE_NOTES", paths.package_notes),
31
+ ]
@@ -0,0 +1 @@
1
+ __version__ = "0.1.0"
@@ -0,0 +1 @@
1
+ """Thin public wrappers for curated workflows."""
@@ -0,0 +1,51 @@
1
+ from __future__ import annotations
2
+
3
+ import importlib.util
4
+ import sys
5
+ from pathlib import Path
6
+
7
+ from sim2sim_onepass.paths import get_repo_paths
8
+
9
+
10
+ def python_info() -> dict[str, str]:
11
+ return {
12
+ "python": sys.executable,
13
+ "version": sys.version.split()[0],
14
+ }
15
+
16
+
17
+ def module_available(name: str) -> bool:
18
+ return importlib.util.find_spec(name) is not None
19
+
20
+
21
+ def simulator_layout_available() -> bool:
22
+ paths = get_repo_paths()
23
+ required = [
24
+ paths.root / "pybullet_panda_gap",
25
+ paths.root / "mujoco_panda_gap",
26
+ paths.root / "plans",
27
+ ]
28
+ return all(path.exists() for path in required)
29
+
30
+
31
+ def report_environment() -> dict[str, object]:
32
+ paths = get_repo_paths()
33
+ return {
34
+ "python": python_info(),
35
+ "repo_root": str(paths.root),
36
+ "repo_docs_present": all(
37
+ p.exists()
38
+ for p in [
39
+ paths.readme,
40
+ paths.quickstart,
41
+ paths.repo_map,
42
+ paths.results_summary,
43
+ paths.visual_index,
44
+ ]
45
+ ),
46
+ "numpy": module_available("numpy"),
47
+ "torch": module_available("torch"),
48
+ "yaml": module_available("yaml"),
49
+ "simulator_layout": simulator_layout_available(),
50
+ "canonical_outputs": paths.canonical_pass.exists(),
51
+ }
@@ -0,0 +1,162 @@
1
+ from __future__ import annotations
2
+
3
+ from pathlib import Path
4
+ from typing import Tuple
5
+
6
+ import numpy as np
7
+
8
+
9
+ def demo_fixture() -> tuple[dict[str, np.ndarray], dict[str, np.ndarray]]:
10
+ S = np.array(
11
+ [
12
+ np.linspace(0.0, 0.36, 37, dtype=np.float32),
13
+ np.linspace(0.1, 0.46, 37, dtype=np.float32),
14
+ np.linspace(0.2, 0.56, 37, dtype=np.float32),
15
+ ],
16
+ dtype=np.float32,
17
+ )
18
+ A = np.array(
19
+ [
20
+ np.linspace(0.0, 0.06, 7, dtype=np.float32),
21
+ np.linspace(0.02, 0.08, 7, dtype=np.float32),
22
+ np.linspace(0.04, 0.10, 7, dtype=np.float32),
23
+ ],
24
+ dtype=np.float32,
25
+ )
26
+ bullet = {"S": S, "A": A, "S_next": S + 0.01}
27
+ mujoco = {"S": S + 0.001, "A": A, "S_next": S + 0.0125}
28
+ return bullet, mujoco
29
+
30
+
31
+ def resolve_default_paths(root: Path) -> Tuple[Path, Path]:
32
+ candidates = [
33
+ (root / "data" / "paired_bullet_seed0.npz", root / "data" / "paired_mujoco_seed0.npz"),
34
+ (root / "data_pybullet" / "paired_bullet_seed0.npz", root / "data_mujoco" / "paired_mujoco_seed0.npz"),
35
+ (
36
+ root / "outputs" / "canonical_pass" / "source_stress_metrics.json",
37
+ root / "outputs" / "canonical_pass" / "source_stress_metrics.json",
38
+ ),
39
+ ]
40
+ for b, m in candidates[:2]:
41
+ if b.exists() and m.exists():
42
+ return b, m
43
+ return candidates[1]
44
+
45
+
46
+ def load_npz(path: Path) -> tuple[np.lib.npyio.NpzFile, list[str]]:
47
+ data = np.load(path)
48
+ return data, list(data.keys())
49
+
50
+
51
+ def describe_array(x: np.ndarray) -> dict[str, object]:
52
+ x = np.asarray(x)
53
+ finite = x[np.isfinite(x)]
54
+ return {
55
+ "shape": tuple(x.shape),
56
+ "dtype": str(x.dtype),
57
+ "nan": int(np.isnan(x).sum()),
58
+ "inf": int(np.isinf(x).sum()),
59
+ "min": float(finite.min()) if finite.size else None,
60
+ "max": float(finite.max()) if finite.size else None,
61
+ "mean": float(finite.mean()) if finite.size else None,
62
+ "std": float(finite.std()) if finite.size else None,
63
+ }
64
+
65
+
66
+ def run_quick_sanity(bullet_path: Path, mujoco_path: Path) -> dict[str, object]:
67
+ if not bullet_path.exists():
68
+ raise FileNotFoundError(f"Bullet NPZ not found: {bullet_path}")
69
+ if not mujoco_path.exists():
70
+ raise FileNotFoundError(f"MuJoCo NPZ not found: {mujoco_path}")
71
+
72
+ b, bk = load_npz(bullet_path)
73
+ m, mk = load_npz(mujoco_path)
74
+
75
+ found = None
76
+ for s_key, a_key, sn_key in [("S", "A", "S_next"), ("obs", "act", "obs_next"), ("states", "actions", "next_states")]:
77
+ if s_key in b and a_key in b and sn_key in b and s_key in m and a_key in m and sn_key in m:
78
+ found = (s_key, a_key, sn_key)
79
+ break
80
+ if not found:
81
+ raise RuntimeError(f"Could not find canonical paired keys. Bullet keys={bk}; MuJoCo keys={mk}")
82
+
83
+ s_key, a_key, sn_key = found
84
+ Sb, Ab, Snb = b[s_key], b[a_key], b[sn_key]
85
+ Sm, Am, Snm = m[s_key], m[a_key], m[sn_key]
86
+
87
+ if Sb.shape != Sm.shape:
88
+ raise RuntimeError(f"State shape mismatch: {Sb.shape} vs {Sm.shape}")
89
+ if Ab.shape != Am.shape:
90
+ raise RuntimeError(f"Action shape mismatch: {Ab.shape} vs {Am.shape}")
91
+ if Snb.shape != Snm.shape:
92
+ raise RuntimeError(f"Next-state shape mismatch: {Snb.shape} vs {Snm.shape}")
93
+
94
+ drift_mean = np.abs(Sb.mean(axis=0) - Sm.mean(axis=0))
95
+ drift_std = np.abs(Sb.std(axis=0) - Sm.std(axis=0))
96
+ drift_score = drift_mean + drift_std
97
+ top = np.argsort(-drift_score)[:10]
98
+ err = np.linalg.norm(Snb - Snm, axis=1)
99
+
100
+ return {
101
+ "mode": "dataset",
102
+ "keys": {"bullet": bk, "mujoco": mk, "selected": [s_key, a_key, sn_key]},
103
+ "bullet": {
104
+ s_key: describe_array(Sb),
105
+ a_key: describe_array(Ab),
106
+ sn_key: describe_array(Snb),
107
+ },
108
+ "mujoco": {
109
+ s_key: describe_array(Sm),
110
+ a_key: describe_array(Am),
111
+ sn_key: describe_array(Snm),
112
+ },
113
+ "one_step": {
114
+ "mean": float(err.mean()),
115
+ "median": float(np.median(err)),
116
+ "p95": float(np.quantile(err, 0.95)),
117
+ "max": float(err.max()),
118
+ },
119
+ "top_drift_dims": [
120
+ {
121
+ "dim": int(i),
122
+ "score": float(drift_score[i]),
123
+ "mean_gap": float(drift_mean[i]),
124
+ "std_gap": float(drift_std[i]),
125
+ }
126
+ for i in top
127
+ ],
128
+ }
129
+
130
+
131
+ def run_quick_sanity_demo() -> dict[str, object]:
132
+ b, m = demo_fixture()
133
+ Sb, Ab, Snb = b["S"], b["A"], b["S_next"]
134
+ Sm, Am, Snm = m["S"], m["A"], m["S_next"]
135
+ drift_mean = np.abs(Sb.mean(axis=0) - Sm.mean(axis=0))
136
+ drift_std = np.abs(Sb.std(axis=0) - Sm.std(axis=0))
137
+ drift_score = drift_mean + drift_std
138
+ top = np.argsort(-drift_score)[:10]
139
+ err = np.linalg.norm(Snb - Snm, axis=1)
140
+
141
+ return {
142
+ "mode": "demo",
143
+ "keys": {"bullet": ["S", "A", "S_next"], "mujoco": ["S", "A", "S_next"], "selected": ["S", "A", "S_next"]},
144
+ "bullet": {"S": describe_array(Sb), "A": describe_array(Ab), "S_next": describe_array(Snb)},
145
+ "mujoco": {"S": describe_array(Sm), "A": describe_array(Am), "S_next": describe_array(Snm)},
146
+ "one_step": {
147
+ "mean": float(err.mean()),
148
+ "median": float(np.median(err)),
149
+ "p95": float(np.quantile(err, 0.95)),
150
+ "max": float(err.max()),
151
+ },
152
+ "top_drift_dims": [
153
+ {
154
+ "dim": int(i),
155
+ "score": float(drift_score[i]),
156
+ "mean_gap": float(drift_mean[i]),
157
+ "std_gap": float(drift_std[i]),
158
+ }
159
+ for i in top
160
+ ],
161
+ "note": "Built-in tiny synthetic paired fixture for package-only smoke testing.",
162
+ }
@@ -0,0 +1,168 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ from pathlib import Path
5
+
6
+ import numpy as np
7
+
8
+ from sim2sim_onepass.wrappers.state_project import project_state
9
+
10
+
11
+ def _require_torch():
12
+ try:
13
+ import torch
14
+ import torch.nn as nn
15
+ except ImportError as exc:
16
+ raise RuntimeError("rollout-check requires PyTorch. Install with `pip install sim2sim-onepass[rollout]`.") from exc
17
+ return torch, nn
18
+
19
+
20
+ def load_npz(path: Path) -> dict[str, np.ndarray]:
21
+ data = np.load(path)
22
+ return {k: data[k] for k in data.files}
23
+
24
+
25
+ class DeltaMLP:
26
+ def __init__(self, nn, in_dim: int, out_dim: int) -> None:
27
+ self.net = nn.Sequential(
28
+ nn.Linear(in_dim, 256),
29
+ nn.ReLU(),
30
+ nn.Linear(256, 256),
31
+ nn.ReLU(),
32
+ nn.Linear(256, out_dim),
33
+ )
34
+
35
+ def __call__(self, x):
36
+ return self.net(x)
37
+
38
+
39
+ def build_model(nn, in_dim: int, out_dim: int):
40
+ class DeltaMoE(nn.Module):
41
+ def __init__(self) -> None:
42
+ super().__init__()
43
+ self.free = DeltaMLP(nn, in_dim, out_dim).net
44
+ self.contact = DeltaMLP(nn, in_dim, out_dim).net
45
+ self.gate = nn.Sequential(
46
+ nn.Linear(in_dim, 128),
47
+ nn.ReLU(),
48
+ nn.Linear(128, 1),
49
+ nn.Sigmoid(),
50
+ )
51
+
52
+ def forward(self, x):
53
+ gate = self.gate(x)
54
+ free_pred = self.free(x)
55
+ contact_pred = self.contact(x)
56
+ return contact_pred * gate + free_pred * (1.0 - gate)
57
+
58
+ return DeltaMoE()
59
+
60
+
61
+ def decide_units(pred: np.ndarray, delta_std: np.ndarray) -> str:
62
+ pred_std = pred.std(axis=0).mean()
63
+ delta_std_mean = delta_std.mean()
64
+ if delta_std_mean == 0:
65
+ return "ambiguous"
66
+ ratio = pred_std / delta_std_mean
67
+ if 0.3 <= ratio <= 3.0:
68
+ return "original"
69
+ if 0.03 <= ratio < 0.3:
70
+ return "normalized"
71
+ return "ambiguous"
72
+
73
+
74
+ def run_rollout_check(
75
+ bullet: Path,
76
+ mujoco: Path,
77
+ model_path: Path,
78
+ norm_path: Path,
79
+ horizon: int = 50,
80
+ nstart: int = 50,
81
+ ) -> dict[str, float | str]:
82
+ torch, nn = _require_torch()
83
+ b = load_npz(bullet)
84
+ m = load_npz(mujoco)
85
+ for key in ("S", "A", "S_next", "episode_id", "t"):
86
+ if key not in b or key not in m:
87
+ raise RuntimeError(f"missing key {key}")
88
+
89
+ Sb = b["S"].astype(np.float32)
90
+ Ab = b["A"].astype(np.float32)
91
+ Snb = b["S_next"].astype(np.float32)
92
+ Snm = m["S_next"].astype(np.float32)
93
+ ep = b["episode_id"].astype(int)
94
+ t = b["t"].astype(int)
95
+
96
+ in_dim = Sb.shape[1] + Ab.shape[1]
97
+ out_dim = Snb.shape[1]
98
+ model = build_model(nn, in_dim, out_dim)
99
+ model.load_state_dict(torch.load(model_path, map_location="cpu"))
100
+ model.eval()
101
+
102
+ norm = json.loads(norm_path.read_text(encoding="utf-8"))
103
+ delta_mean = np.array(norm.get("delta_mean"), dtype=np.float32)
104
+ delta_std = np.array(norm.get("delta_std"), dtype=np.float32)
105
+
106
+ unique_eps = np.unique(ep)
107
+ rng = np.random.default_rng(0)
108
+ starts: list[int] = []
109
+ for e in unique_eps:
110
+ idx = np.where(ep == e)[0]
111
+ if idx.size < horizon + 1:
112
+ continue
113
+ order = np.argsort(t[idx])
114
+ idx = idx[order]
115
+ max_start = idx.size - horizon
116
+ if max_start <= 0:
117
+ continue
118
+ for _ in range(2):
119
+ starts.append(int(idx[rng.integers(0, max_start)]))
120
+ if not starts:
121
+ raise RuntimeError("no valid rollout starts")
122
+ rng.shuffle(starts)
123
+ starts = starts[:nstart]
124
+
125
+ sample_idx = np.array(starts[: min(len(starts), 10)])
126
+ X_sample = np.concatenate([Sb[sample_idx], Ab[sample_idx]], axis=1)
127
+ with torch.no_grad():
128
+ pred_sample = model(torch.from_numpy(X_sample).float()).numpy()
129
+ units = decide_units(pred_sample, delta_std)
130
+ if units == "ambiguous":
131
+ raise RuntimeError("cannot confidently determine model output units")
132
+
133
+ def pred_delta(x: np.ndarray) -> np.ndarray:
134
+ with torch.no_grad():
135
+ pred = model(torch.from_numpy(x).float()).numpy()
136
+ if units == "normalized":
137
+ return pred * delta_std + delta_mean
138
+ return pred
139
+
140
+ errs: list[float] = []
141
+ for s in starts:
142
+ e = ep[s]
143
+ idx = np.where(ep == e)[0]
144
+ order = np.argsort(t[idx])
145
+ idx = idx[order]
146
+ pos = int(np.where(idx == s)[0][0])
147
+ if pos + horizon > idx.size:
148
+ continue
149
+ for k in range(horizon):
150
+ i = idx[pos + k]
151
+ x = np.concatenate([Sb[i], Ab[i]], axis=0)[None, :]
152
+ delta = pred_delta(x)[0]
153
+ pred_state = project_state(Snb[i] + delta)
154
+ if not np.isfinite(pred_state).all():
155
+ raise RuntimeError(f"NaN/Inf at start={s} step={k}")
156
+ errs.append(float(np.linalg.norm(pred_state - Snm[i])))
157
+
158
+ if not errs:
159
+ raise RuntimeError("no rollout errors computed")
160
+ arr = np.asarray(errs, dtype=np.float32)
161
+ return {
162
+ "horizon": int(horizon),
163
+ "nstart": int(nstart),
164
+ "units": units,
165
+ "mean": float(arr.mean()),
166
+ "p95": float(np.quantile(arr, 0.95)),
167
+ "max": float(arr.max()),
168
+ }
@@ -0,0 +1,52 @@
1
+ from __future__ import annotations
2
+
3
+ import numpy as np
4
+
5
+ STATE_DIM = 37
6
+ Q_SLICE = slice(0, 7)
7
+ DQ_SLICE = slice(7, 14)
8
+ EE_POS_SLICE = slice(14, 17)
9
+ EE_QUAT_SLICE = slice(17, 21)
10
+ OBJ_POS_SLICE = slice(21, 24)
11
+ OBJ_QUAT_SLICE = slice(24, 28)
12
+ OBJ_LINVEL_SLICE = slice(28, 31)
13
+ OBJ_ANGVEL_SLICE = slice(31, 34)
14
+ CONTACT_FLAG_IDX = 34
15
+ CONTACT_FORCE_IDX = 35
16
+ MIN_DIST_IDX = 36
17
+ VEL_LIMIT = 5.0
18
+
19
+
20
+ def _wrap_angles(x: np.ndarray) -> np.ndarray:
21
+ return (x + np.pi) % (2.0 * np.pi) - np.pi
22
+
23
+
24
+ def _renorm_quat(q: np.ndarray) -> np.ndarray:
25
+ norm = np.linalg.norm(q, axis=-1, keepdims=True)
26
+ safe = norm[..., 0] >= 1e-6
27
+ qn = np.zeros_like(q, dtype=np.float32)
28
+ qn[safe] = q[safe] / norm[safe]
29
+ qn[~safe] = np.array([0.0, 0.0, 0.0, 1.0], dtype=np.float32)
30
+ return qn
31
+
32
+
33
+ def project_state(state: np.ndarray) -> np.ndarray:
34
+ state = np.asarray(state, dtype=np.float32)
35
+ if state.shape[-1] != STATE_DIM:
36
+ raise ValueError(f"Expected last dim {STATE_DIM}, got {state.shape}")
37
+ squeeze = state.ndim == 1
38
+ if squeeze:
39
+ state = state[None, :]
40
+ out = state.copy()
41
+ out[:, Q_SLICE] = _wrap_angles(out[:, Q_SLICE])
42
+ out[:, EE_QUAT_SLICE] = _renorm_quat(out[:, EE_QUAT_SLICE])
43
+ out[:, OBJ_QUAT_SLICE] = _renorm_quat(out[:, OBJ_QUAT_SLICE])
44
+ out[:, EE_POS_SLICE] = np.clip(out[:, EE_POS_SLICE], -2.0, 2.0)
45
+ out[:, OBJ_POS_SLICE] = np.clip(out[:, OBJ_POS_SLICE], -2.0, 2.0)
46
+ out[:, DQ_SLICE] = np.clip(out[:, DQ_SLICE], -VEL_LIMIT, VEL_LIMIT)
47
+ out[:, OBJ_LINVEL_SLICE] = np.clip(out[:, OBJ_LINVEL_SLICE], -VEL_LIMIT, VEL_LIMIT)
48
+ out[:, OBJ_ANGVEL_SLICE] = np.clip(out[:, OBJ_ANGVEL_SLICE], -VEL_LIMIT, VEL_LIMIT)
49
+ out[:, CONTACT_FLAG_IDX] = np.clip(out[:, CONTACT_FLAG_IDX], 0.0, 1.0)
50
+ out[:, CONTACT_FORCE_IDX] = np.clip(out[:, CONTACT_FORCE_IDX], 0.0, 50.0)
51
+ out[:, MIN_DIST_IDX] = np.clip(out[:, MIN_DIST_IDX], 0.0, 2.0)
52
+ return out[0] if squeeze else out
@@ -0,0 +1,156 @@
1
+ Metadata-Version: 2.4
2
+ Name: sim2sim-onepass
3
+ Version: 0.1.0
4
+ Summary: A lightweight companion package for the Sim2Sim-OnePass public research release.
5
+ Author: ANTHONY-OLEVESTER
6
+ License-Expression: Apache-2.0
7
+ Project-URL: Homepage, https://github.com/ANTHONY-OLEVESTER/Robotics_sim-to-sim_OnePass
8
+ Project-URL: Repository, https://github.com/ANTHONY-OLEVESTER/Robotics_sim-to-sim_OnePass
9
+ Project-URL: Documentation, https://github.com/ANTHONY-OLEVESTER/Robotics_sim-to-sim_OnePass
10
+ Project-URL: Issues, https://github.com/ANTHONY-OLEVESTER/Robotics_sim-to-sim_OnePass/issues
11
+ Keywords: robotics,simulation,mujoco,pybullet,research
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Intended Audience :: Science/Research
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Topic :: Scientific/Engineering
19
+ Requires-Python: >=3.10
20
+ Description-Content-Type: text/markdown
21
+ License-File: LICENSE
22
+ Requires-Dist: numpy>=1.24
23
+ Provides-Extra: rollout
24
+ Requires-Dist: torch>=2.0; extra == "rollout"
25
+ Provides-Extra: alignment
26
+ Requires-Dist: PyYAML>=6.0; extra == "alignment"
27
+ Provides-Extra: dev
28
+ Requires-Dist: build>=1.2.2; extra == "dev"
29
+ Requires-Dist: twine>=5.1.1; extra == "dev"
30
+ Dynamic: license-file
31
+
32
+ # Sim2Sim-OnePass Public Release
33
+
34
+ This curated layer is the fast path through the repository. It is designed so a new visitor can understand the claim, watch the evidence, inspect the canonical PASS artifacts, and run the shortest validation path without digging through the full development history.
35
+
36
+ ## Companion Package
37
+
38
+ This branch adds a PyPI-ready package layer named `sim2sim-onepass`. It is a lightweight companion package for the public research release, not a standalone robotics simulator system. It provides an installable CLI, embedded docs access, results navigation, environment checks, and selected lightweight utilities around the curated repo.
39
+
40
+ Local editable install:
41
+
42
+ ```powershell
43
+ python -m pip install -e .
44
+ ```
45
+
46
+ Published PyPI install:
47
+
48
+ ```powershell
49
+ python -m pip install sim2sim-onepass
50
+ ```
51
+
52
+ PyPI package page:
53
+
54
+ ```text
55
+ https://pypi.org/project/sim2sim-onepass/
56
+ ```
57
+
58
+ CLI entrypoint:
59
+
60
+ ```powershell
61
+ sim2sim-onepass --help
62
+ ```
63
+
64
+ What the package does provide:
65
+
66
+ - repo navigation and public documentation access
67
+ - embedded lightweight markdown docs
68
+ - environment checking
69
+ - quick sanity checks on paired datasets
70
+ - rollout-check wrapper for model + norm + paired data paths
71
+ - guarded simulator workflow commands with clear errors when the full simulator stack is not present
72
+
73
+ What the package does not provide by default:
74
+
75
+ - full simulator environments
76
+ - full datasets
77
+ - giant reports dumps
78
+ - replay videos and large binary outputs
79
+ - local environments and machine-specific folders
80
+ - the full simulator env trees and internal training workspace
81
+
82
+ ## Command Capability Levels
83
+
84
+ | Command | Category | Requirements |
85
+ | --- | --- | --- |
86
+ | `info` | Standalone | package install only |
87
+ | `quickstart` | Standalone | package install only |
88
+ | `repo-map` | Standalone | package install only |
89
+ | `results-summary` | Standalone | package install only |
90
+ | `visual-index` | Standalone | package install only |
91
+ | `docs` | Standalone | package install only |
92
+ | `check-env` | Standalone | package install only |
93
+ | `quick-sanity` | Dataset-dependent | paired datasets, or `--demo` for the tiny built-in fixture |
94
+ | `rollout-check` | Dataset-dependent | paired datasets, model, norm file, and optional extra `sim2sim-onepass[rollout]` |
95
+ | `alignment-gate` | Full repo / simulator dependent | full curated repo layout plus simulator dependencies |
96
+ | `alignment-report` | Full repo / simulator dependent | full curated repo layout plus simulator dependencies and workflow files |
97
+
98
+ ## Pick Your Route
99
+
100
+ | If you want to... | Open this |
101
+ | --- | --- |
102
+ | See the project in one screen | [RESULTS_SUMMARY.md](RESULTS_SUMMARY.md) |
103
+ | Watch the visual evidence first | [VISUAL_INDEX.md](VISUAL_INDEX.md) |
104
+ | Run the shortest validation path | [QUICKSTART.md](QUICKSTART.md) |
105
+ | Inspect the packaged PASS bundle | [outputs/canonical_pass/](outputs/canonical_pass/) |
106
+ | Understand how the repo is organized | [REPO_MAP.md](REPO_MAP.md) |
107
+
108
+ ## See It Before You Read It
109
+
110
+ | Triptych preview | Rollout figure |
111
+ | --- | --- |
112
+ | [![Triptych preview](outputs/canonical_pass/preview/triptych_frame0.png)](outputs/canonical_pass/preview/triptych_frame0.png) | [![Rollout figure](outputs/canonical_pass/plots/rollout_phys_p95.png)](outputs/canonical_pass/plots/rollout_phys_p95.png) |
113
+
114
+ Open the full visual package here:
115
+
116
+ - [VISUAL_INDEX.md](VISUAL_INDEX.md)
117
+ - [outputs/canonical_pass/videos/compare_triptych.mp4](outputs/canonical_pass/videos/compare_triptych.mp4)
118
+ - [outputs/canonical_pass/report.md](outputs/canonical_pass/report.md)
119
+
120
+ ## The Claim In Plain Terms
121
+
122
+ After enforcing deterministic cross-simulator alignment between PyBullet and MuJoCo, this repo learns a residual next-state correction that:
123
+
124
+ - reduces Bullet-to-MuJoCo one-step physical error,
125
+ - remains stable under long-horizon rollout checks,
126
+ - passes holdout and alignment gates,
127
+ - and can be inspected visually through replay videos, triptychs, and overlay plots.
128
+
129
+ ## Canonical Story
130
+
131
+ 1. Deterministic paired plans are executed in PyBullet and MuJoCo.
132
+ 2. A strict alignment gate blocks training if reset state, timing, or first-step consistency drift.
133
+ 3. A residual model predicts the MuJoCo-minus-Bullet next-state gap.
134
+ 4. Hard-mode stress evaluation verifies one-step accuracy, holdouts, and rollout stability.
135
+ 5. Behavioral acceptance exports replay videos, triptychs, and overlay plots for inspection.
136
+
137
+ ## Canonical Evidence
138
+
139
+ - Quantitative anchor: packaged in `outputs/canonical_pass/source_stress_report.md` and `outputs/canonical_pass/source_stress_metrics.json`
140
+ - Visual anchor: packaged in `outputs/canonical_pass/source_behavioral_report.md` and the copied videos and preview images
141
+ - Public-facing copied bundle: [outputs/canonical_pass/](outputs/canonical_pass/)
142
+ - Provenance map: [configs/canonical_sources.json](configs/canonical_sources.json)
143
+
144
+ ## Exact Quickstart
145
+
146
+ This public repo is optimized for inspection first. Open [QUICKSTART.md](QUICKSTART.md) for the shortest path through the packaged evidence and for command references preserved from the source workspace.
147
+
148
+ ## What Lives Where
149
+
150
+ - [outputs/canonical_pass/](outputs/canonical_pass/) contains the reviewer-facing proof artifacts.
151
+ - [examples/canonical_commands.ps1](examples/canonical_commands.ps1) contains exact rerun commands.
152
+ - [docs/CANONICAL_SOURCES.md](docs/CANONICAL_SOURCES.md) records provenance.
153
+ - [docs/PUBLISHING.md](docs/PUBLISHING.md) defines what to ship in a public release.
154
+ - The interactive website is published from the `gh-pages` branch.
155
+ - Package implementation lives under `src/sim2sim_onepass/`.
156
+ - License is [Apache-2.0](LICENSE).
@@ -0,0 +1,23 @@
1
+ sim2sim_onepass/__init__.py,sha256=v3x5oBkxwKJEZLv62QqSmP3iqNKLtZgrWZfH8eFzlQg,60
2
+ sim2sim_onepass/cli.py,sha256=XlXSmzxnQjrjSXkRmNuChKR-pPKnqZSXWGjyB6rlFAg,7671
3
+ sim2sim_onepass/paths.py,sha256=RexqsR1JtFlnXErnMrsz0rZ91tpmPkSTGJlRNrjbc1Q,1236
4
+ sim2sim_onepass/version.py,sha256=kUR5RAFc7HCeiqdlX36dZOHkUI5wI6V_43RpEcD8b-0,22
5
+ sim2sim_onepass/resources/QUICKSTART.md,sha256=_UtBSMQnReExxYay7wsczpcRpnx8GaQutU8WQZxO29w,488
6
+ sim2sim_onepass/resources/README.md,sha256=EAUFsHtI6WpYEmp5YZfDxhJ5S2v2ctT5mAY5Ggfri58,535
7
+ sim2sim_onepass/resources/REPO_MAP.md,sha256=2gL5G-3dQP3PwCpZouvAoxLiEvYUpVwTrbFmYs4A1-M,324
8
+ sim2sim_onepass/resources/RESULTS_SUMMARY.md,sha256=WMpT0yL6J6SEVR-mrl8OewJ884X8TYgXBSqogV5OeJ0,244
9
+ sim2sim_onepass/resources/VISUAL_INDEX.md,sha256=dYLu0mOQX2Gbf6qRcmgKBpE6ytwGJv_a_jObAYqwspA,268
10
+ sim2sim_onepass/resources/__init__.py,sha256=yzu-6U8H6kSaYEdsxuy0Ed-WrUfHxpnSJ1wAKQAhZF8,58
11
+ sim2sim_onepass/utils/__init__.py,sha256=kVHealcnpW7tQIPTax1nEOF_WPAMYSV8dg0P_LhzHu0,46
12
+ sim2sim_onepass/utils/docs.py,sha256=46PU5c5GMbPn_qdUOQs1w28TlAvHnCekDAUGwB8LYw8,978
13
+ sim2sim_onepass/wrappers/__init__.py,sha256=1QvoS4C-g77eI8c74Wur-JQ9qtUNOU87DjqKd2dY9iM,50
14
+ sim2sim_onepass/wrappers/availability.py,sha256=zfr1amaw6z4RW5yBb6W3w_hbk5bocSLJM68nuMM8w44,1329
15
+ sim2sim_onepass/wrappers/quick_sanity.py,sha256=nEdue9EtldtFYQTHkCNqc5DgLBhEPsoCiHuDg16p_hw,5776
16
+ sim2sim_onepass/wrappers/rollout.py,sha256=YMAfPyIA_m_1XPasCIHWg3X0ViadDfA8_w9FxB05rEI,5165
17
+ sim2sim_onepass/wrappers/state_project.py,sha256=mfevHUcpyc0KWrne0FhqsspMyJEkzIouIYPBYxs8xOM,1914
18
+ sim2sim_onepass-0.1.0.dist-info/licenses/LICENSE,sha256=lJ0vnK1Rqyj2IWRh9eU4g5hm7BA2Hl986eP9foo9jac,6131
19
+ sim2sim_onepass-0.1.0.dist-info/METADATA,sha256=fvd7nXH5rR_8_wgh0S0f1WymMvB7A5EPWUCiCXsa2OU,7094
20
+ sim2sim_onepass-0.1.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
21
+ sim2sim_onepass-0.1.0.dist-info/entry_points.txt,sha256=mYsgd-l4YxpXMa1Hj6dmz2aflkq47e15XlaKfTccaZI,61
22
+ sim2sim_onepass-0.1.0.dist-info/top_level.txt,sha256=Lr7UmBESV7svgOH8JVEsF2QjwVLtpvYUNYhCGo-0atA,16
23
+ sim2sim_onepass-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (82.0.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ sim2sim-onepass = sim2sim_onepass.cli:main
@@ -0,0 +1,118 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ 1. Definitions.
8
+
9
+ "License" shall mean the terms and conditions for use, reproduction, and
10
+ distribution as defined by Sections 1 through 9 of this document.
11
+
12
+ "Licensor" shall mean the copyright owner or entity authorized by the copyright
13
+ owner that is granting the License.
14
+
15
+ "Legal Entity" shall mean the union of the acting entity and all other entities
16
+ that control, are controlled by, or are under common control with that entity.
17
+ "Control" means (i) the power, direct or indirect, to cause the direction or
18
+ management of such entity, whether by contract or otherwise, or (ii) ownership
19
+ of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial
20
+ ownership of such entity.
21
+
22
+ "You" (or "Your") shall mean an individual or Legal Entity exercising
23
+ permissions granted by this License.
24
+
25
+ "Source" form shall mean the preferred form for making modifications, including
26
+ but not limited to software source code, documentation source, and configuration
27
+ files.
28
+
29
+ "Object" form shall mean any form resulting from mechanical transformation or
30
+ translation of a Source form, including but not limited to compiled object code,
31
+ generated documentation, and conversions to other media types.
32
+
33
+ "Work" shall mean the work of authorship, whether in Source or Object form, made
34
+ available under the License, as indicated by a copyright notice that is included
35
+ in or attached to the work.
36
+
37
+ "Derivative Works" shall mean any work, whether in Source or Object form, that
38
+ is based on (or derived from) the Work and for which the editorial revisions,
39
+ annotations, elaborations, or other modifications represent, as a whole, an
40
+ original work of authorship. For the purposes of this License, Derivative Works
41
+ shall not include works that remain separable from, or merely link (or bind by
42
+ name) to the interfaces of, the Work and Derivative Works thereof.
43
+
44
+ "Contribution" shall mean any work of authorship, including the original version
45
+ of the Work and any modifications or additions to that Work or Derivative Works
46
+ thereof, that is intentionally submitted to Licensor for inclusion in the Work
47
+ by the copyright owner or by an individual or Legal Entity authorized to submit
48
+ on behalf of the copyright owner.
49
+
50
+ "Contributor" shall mean Licensor and any individual or Legal Entity on behalf
51
+ of whom a Contribution has been received by Licensor and subsequently
52
+ incorporated within the Work.
53
+
54
+ 2. Grant of Copyright License. Subject to the terms and conditions of this
55
+ License, each Contributor hereby grants to You a perpetual, worldwide,
56
+ non-exclusive, no-charge, royalty-free, irrevocable copyright license to
57
+ reproduce, prepare Derivative Works of, publicly display, publicly perform,
58
+ sublicense, and distribute the Work and such Derivative Works in Source or
59
+ Object form.
60
+
61
+ 3. Grant of Patent License. Subject to the terms and conditions of this License,
62
+ each Contributor hereby grants to You a perpetual, worldwide, non-exclusive,
63
+ no-charge, royalty-free, irrevocable (except as stated in this section) patent
64
+ license to make, have made, use, offer to sell, sell, import, and otherwise
65
+ transfer the Work.
66
+
67
+ 4. Redistribution. You may reproduce and distribute copies of the Work or
68
+ Derivative Works thereof in any medium, with or without modifications, and in
69
+ Source or Object form, provided that You meet the following conditions:
70
+
71
+ (a) You must give any other recipients of the Work or Derivative Works a copy of
72
+ this License; and
73
+
74
+ (b) You must cause any modified files to carry prominent notices stating that You
75
+ changed the files; and
76
+
77
+ (c) You must retain, in the Source form of any Derivative Works that You
78
+ distribute, all copyright, patent, trademark, and attribution notices from the
79
+ Source form of the Work, excluding those notices that do not pertain to any part
80
+ of the Derivative Works; and
81
+
82
+ (d) If the Work includes a "NOTICE" text file as part of its distribution, then
83
+ any Derivative Works that You distribute must include a readable copy of the
84
+ attribution notices contained within such NOTICE file, excluding those notices
85
+ that do not pertain to any part of the Derivative Works, in at least one of the
86
+ following places: within a NOTICE text file distributed as part of the
87
+ Derivative Works; within the Source form or documentation, if provided along
88
+ with the Derivative Works; or within a display generated by the Derivative
89
+ Works, if and wherever such third-party notices normally appear.
90
+
91
+ 5. Submission of Contributions. Unless You explicitly state otherwise, any
92
+ Contribution intentionally submitted for inclusion in the Work by You to the
93
+ Licensor shall be under the terms and conditions of this License, without any
94
+ additional terms or conditions.
95
+
96
+ 6. Trademarks. This License does not grant permission to use the trade names,
97
+ trademarks, service marks, or product names of the Licensor, except as required
98
+ for reasonable and customary use in describing the origin of the Work.
99
+
100
+ 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in
101
+ writing, Licensor provides the Work on an "AS IS" BASIS, WITHOUT WARRANTIES OR
102
+ CONDITIONS OF ANY KIND, either express or implied, including, without
103
+ limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT,
104
+ MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible
105
+ for determining the appropriateness of using or redistributing the Work.
106
+
107
+ 8. Limitation of Liability. In no event and under no legal theory, whether in
108
+ tort (including negligence), contract, or otherwise, unless required by
109
+ applicable law, shall any Contributor be liable to You for damages, including
110
+ any direct, indirect, special, incidental, or consequential damages of any
111
+ character arising as a result of this License or out of the use or inability to
112
+ use the Work.
113
+
114
+ 9. Accepting Warranty or Additional Liability. While redistributing the Work or
115
+ Derivative Works thereof, You may choose to offer, and charge a fee for,
116
+ acceptance of support, warranty, indemnity, or other liability obligations.
117
+
118
+ END OF TERMS AND CONDITIONS
@@ -0,0 +1 @@
1
+ sim2sim_onepass