#!/usr/bin/env python3
"""
Generates quick start module for https://pytorch.org/get-started/locally/ page
If called from update-quick-start-module.yml workflow (--autogenerate parameter set)
Will output new quick-start-module.js, and new published_version.json file
based on the current release matrix.
If called standalone will generate quick-start-module.js from existing
published_version.json file
"""
import argparse
import copy
import json
from enum import Enum
from pathlib import Path
from typing import Dict
BASE_DIR = Path(__file__).parent.parent
class OperatingSystem(Enum):
LINUX: str = "linux"
WINDOWS: str = "windows"
MACOS: str = "macos"
PRE_CXX11_ABI = "pre-cxx11"
CXX11_ABI = "cxx11-abi"
DEBUG = "debug"
RELEASE = "release"
DEFAULT = "default"
ENABLE = "enable"
DISABLE = "disable"
MACOS = "macos"
# Mapping json to release matrix default values
acc_arch_ver_default = {
"nightly": {
"accnone": ("cpu", ""),
"cuda.x": ("cuda", "11.8"),
"cuda.y": ("cuda", "12.1"),
"cuda.z": ("cuda", "12.4"),
"rocm5.x": ("rocm", "6.0"),
},
"release": {
"accnone": ("cpu", ""),
"cuda.x": ("cuda", "11.8"),
"cuda.y": ("cuda", "12.1"),
"cuda.z": ("cuda", "12.4"),
"rocm5.x": ("rocm", "6.0"),
},
}
# Initialize arch version to default values
# these default values will be overwritten by
# extracted values from the release marix
acc_arch_ver_map = acc_arch_ver_default
LIBTORCH_DWNL_INSTR = {
PRE_CXX11_ABI: "Download here (Pre-cxx11 ABI):",
CXX11_ABI: "Download here (cxx11 ABI):",
RELEASE: "Download here (Release version):",
DEBUG: "Download here (Debug version):",
MACOS: "Download arm64 libtorch here (ROCm and CUDA are not supported):",
}
def load_json_from_basedir(filename: str):
try:
with open(BASE_DIR / filename) as fptr:
return json.load(fptr)
except FileNotFoundError as exc:
raise ImportError(f"File {filename} not found error: {exc.strerror}") from exc
except json.JSONDecodeError as exc:
raise ImportError(f"Invalid JSON {filename}") from exc
def read_published_versions():
return load_json_from_basedir("published_versions.json")
def write_published_versions(versions):
with open(BASE_DIR / "published_versions.json", "w") as outfile:
json.dump(versions, outfile, indent=2)
# Create releases JSON for PyTorch website.
# Matrix is being used to populate config data for
# the "Start Locally" installation options table.
def write_releases_file(matrix):
with open(BASE_DIR / "releases.json", "w") as outfile:
json.dump(matrix, outfile, indent=2)
def read_matrix_for_os(osys: OperatingSystem, channel: str):
jsonfile = load_json_from_basedir(f"{osys.value}_{channel}_matrix.json")
return jsonfile["include"]
def read_quick_start_module_template():
with open(BASE_DIR / "_includes" / "quick-start-module.js") as fptr:
return fptr.read()
def get_package_type(pkg_key: str, os_key: OperatingSystem) -> str:
if pkg_key != "pip":
return pkg_key
return "manywheel" if os_key == OperatingSystem.LINUX.value else "wheel"
def get_gpu_info(acc_key, instr, acc_arch_map):
gpu_arch_type, gpu_arch_version = acc_arch_map[acc_key]
if DEFAULT in instr:
gpu_arch_type, gpu_arch_version = acc_arch_map["accnone"]
return (gpu_arch_type, gpu_arch_version)
# This method is used for generating new published_versions.json file
# It will modify versions json object with installation instructions
# Provided by generate install matrix Github Workflow, stored in release_matrix
# json object.
def update_versions(versions, release_matrix, release_version):
version = "preview"
template = "preview"
acc_arch_map = acc_arch_ver_map[release_version]
if release_version != "nightly":
version = release_matrix[OperatingSystem.LINUX.value][0]["stable_version"]
if version not in versions["versions"]:
versions["versions"][version] = copy.deepcopy(
versions["versions"][template]
)
versions["latest_stable"] = version
# Perform update of the json file from release matrix
for os_key, os_vers in versions["versions"][version].items():
for pkg_key, pkg_vers in os_vers.items():
for acc_key, instr in pkg_vers.items():
package_type = get_package_type(pkg_key, os_key)
gpu_arch_type, gpu_arch_version = get_gpu_info(
acc_key, instr, acc_arch_map
)
pkg_arch_matrix = [
x
for x in release_matrix[os_key]
if (x["package_type"], x["gpu_arch_type"], x["gpu_arch_version"])
== (package_type, gpu_arch_type, gpu_arch_version)
]
if pkg_arch_matrix:
if package_type != "libtorch":
instr["command"] = pkg_arch_matrix[0]["installation"]
else:
if os_key == OperatingSystem.LINUX.value:
rel_entry_dict = {
x["devtoolset"]: x["installation"]
for x in pkg_arch_matrix
if x["libtorch_variant"] == "shared-with-deps"
}
if instr["versions"] is not None:
for ver in [CXX11_ABI, PRE_CXX11_ABI]:
# temporarily remove setting pre-cxx11-abi. For Release 2.7 we
# should remove pre-cxx11-abi completely.
if ver == PRE_CXX11_ABI:
continue
else:
instr["versions"][LIBTORCH_DWNL_INSTR[ver]] = (
rel_entry_dict[ver]
)
elif os_key == OperatingSystem.WINDOWS.value:
rel_entry_dict = {
x["libtorch_config"]: x["installation"]
for x in pkg_arch_matrix
}
if instr["versions"] is not None:
for ver in [RELEASE, DEBUG]:
instr["versions"][LIBTORCH_DWNL_INSTR[ver]] = (
rel_entry_dict[ver]
)
elif os_key == OperatingSystem.MACOS.value:
if instr["versions"] is not None:
instr["versions"][LIBTORCH_DWNL_INSTR[MACOS]] = (
pkg_arch_matrix[0]["installation"]
)
# This method is used for generating new quick-start-module.js
# from the versions json object
def gen_install_matrix(versions) -> Dict[str, str]:
result = {}
version_map = {
"preview": "preview",
"stable": versions["latest_stable"],
}
for ver, ver_key in version_map.items():
for os_key, os_vers in versions["versions"][ver_key].items():
for pkg_key, pkg_vers in os_vers.items():
for acc_key, instr in pkg_vers.items():
extra_key = "python" if pkg_key != "libtorch" else "cplusplus"
key = f"{ver},{pkg_key},{os_key},{acc_key},{extra_key}"
note = instr["note"]
lines = [note] if note is not None else []
if pkg_key == "libtorch":
ivers = instr["versions"]
if ivers is not None:
lines += [
f"{lab}
{val}"
for (lab, val) in ivers.items()
]
else:
command = instr["command"]
if command is not None:
lines.append(command)
result[key] = "
".join(lines)
return result
# This method is used for extracting two latest verisons of cuda and
# last verion of rocm. It will modify the acc_arch_ver_map object used
# to update getting started page.
def extract_arch_ver_map(release_matrix):
def gen_ver_list(chan, gpu_arch_type):
return {
x["desired_cuda"]: x["gpu_arch_version"]
for x in release_matrix[chan]["linux"]
if x["gpu_arch_type"] == gpu_arch_type
}
for chan in ("nightly", "release"):
cuda_ver_list = gen_ver_list(chan, "cuda")
rocm_ver_list = gen_ver_list(chan, "rocm")
cuda_list = sorted(cuda_ver_list.values())
acc_arch_ver_map[chan]["rocm5.x"] = ("rocm", max(rocm_ver_list.values()))
for cuda_ver, label in zip(cuda_list, ["cuda.x", "cuda.y", "cuda.z"]):
acc_arch_ver_map[chan][label] = ("cuda", cuda_ver)
def main():
parser = argparse.ArgumentParser()
parser.add_argument("--autogenerate", dest="autogenerate", action="store_true")
parser.set_defaults(autogenerate=True)
options = parser.parse_args()
versions = read_published_versions()
if options.autogenerate:
release_matrix = {}
for val in ("nightly", "release"):
release_matrix[val] = {}
for osys in OperatingSystem:
release_matrix[val][osys.value] = read_matrix_for_os(osys, val)
write_releases_file(release_matrix)
extract_arch_ver_map(release_matrix)
for val in ("nightly", "release"):
update_versions(versions, release_matrix[val], val)
write_published_versions(versions)
template = read_quick_start_module_template()
versions_str = json.dumps(gen_install_matrix(versions))
template = template.replace("{{ installMatrix }}", versions_str)
template = template.replace(
"{{ VERSION }}", f"\"Stable ({versions['latest_stable']})\""
)
print(template.replace("{{ ACC ARCH MAP }}", json.dumps(acc_arch_ver_map)))
if __name__ == "__main__":
main()