Upload 64 files
Browse filesThis view is limited to 50 files because it contains too many changes.
See raw diff
- .gitattributes +26 -0
- Activate.ps1 +502 -0
- INSTALLER +1 -0
- LICENSE +0 -0
- METADATA +549 -0
- NOTICE +456 -0
- RECORD +0 -0
- REQUESTED +0 -0
- WHEEL +5 -0
- __init__.py +10 -0
- activate +70 -0
- activate.bat +34 -0
- code_template.py +103 -0
- context.py +134 -0
- datasets-cli.exe +3 -0
- deactivate.bat +22 -0
- distutils-precedence.pth +3 -0
- entry_points.txt +6 -0
- f2py.exe +3 -0
- gen.py +0 -0
- gen_aoti_c_shim.py +508 -0
- gen_backend_stubs.py +615 -0
- gen_executorch.py +1000 -0
- gen_functionalization_type.py +882 -0
- gen_lazy_tensor.py +585 -0
- gen_schema_utils.py +97 -0
- gen_vmap_plumbing.py +275 -0
- get_gprof +75 -0
- get_objgraph +54 -0
- huggingface-cli.exe +3 -0
- import_pb_to_tensorboard.exe +3 -0
- isympy.1 +188 -0
- isympy.exe +3 -0
- isympy.py +342 -0
- local.py +63 -0
- markdown-it.exe +3 -0
- markdown_py.exe +3 -0
- model.py +0 -0
- native_function_generation.py +651 -0
- normalizer.exe +3 -0
- numpy-2.0.2-cp312-cp312-win_amd64.whl +0 -0
- numpy-config.exe +3 -0
- pip.exe +3 -0
- pip3.12.exe +3 -0
- pip3.exe +3 -0
- pygmentize.exe +3 -0
- python.exe +3 -0
- pythonw.exe +3 -0
- saved_model_cli.exe +3 -0
- six.py +1003 -0
.gitattributes
CHANGED
|
@@ -33,3 +33,29 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
| 36 |
+
datasets-cli.exe filter=lfs diff=lfs merge=lfs -text
|
| 37 |
+
f2py.exe filter=lfs diff=lfs merge=lfs -text
|
| 38 |
+
huggingface-cli.exe filter=lfs diff=lfs merge=lfs -text
|
| 39 |
+
import_pb_to_tensorboard.exe filter=lfs diff=lfs merge=lfs -text
|
| 40 |
+
isympy.exe filter=lfs diff=lfs merge=lfs -text
|
| 41 |
+
markdown_py.exe filter=lfs diff=lfs merge=lfs -text
|
| 42 |
+
markdown-it.exe filter=lfs diff=lfs merge=lfs -text
|
| 43 |
+
normalizer.exe filter=lfs diff=lfs merge=lfs -text
|
| 44 |
+
numpy-config.exe filter=lfs diff=lfs merge=lfs -text
|
| 45 |
+
pip.exe filter=lfs diff=lfs merge=lfs -text
|
| 46 |
+
pip3.12.exe filter=lfs diff=lfs merge=lfs -text
|
| 47 |
+
pip3.exe filter=lfs diff=lfs merge=lfs -text
|
| 48 |
+
pygmentize.exe filter=lfs diff=lfs merge=lfs -text
|
| 49 |
+
python.exe filter=lfs diff=lfs merge=lfs -text
|
| 50 |
+
pythonw.exe filter=lfs diff=lfs merge=lfs -text
|
| 51 |
+
saved_model_cli.exe filter=lfs diff=lfs merge=lfs -text
|
| 52 |
+
tensorboard.exe filter=lfs diff=lfs merge=lfs -text
|
| 53 |
+
tf_upgrade_v2.exe filter=lfs diff=lfs merge=lfs -text
|
| 54 |
+
tflite_convert.exe filter=lfs diff=lfs merge=lfs -text
|
| 55 |
+
toco_from_protos.exe filter=lfs diff=lfs merge=lfs -text
|
| 56 |
+
toco.exe filter=lfs diff=lfs merge=lfs -text
|
| 57 |
+
torchfrtrace.exe filter=lfs diff=lfs merge=lfs -text
|
| 58 |
+
torchrun.exe filter=lfs diff=lfs merge=lfs -text
|
| 59 |
+
tqdm.exe filter=lfs diff=lfs merge=lfs -text
|
| 60 |
+
transformers-cli.exe filter=lfs diff=lfs merge=lfs -text
|
| 61 |
+
wheel.exe filter=lfs diff=lfs merge=lfs -text
|
Activate.ps1
ADDED
|
@@ -0,0 +1,502 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<#
|
| 2 |
+
.Synopsis
|
| 3 |
+
Activate a Python virtual environment for the current PowerShell session.
|
| 4 |
+
|
| 5 |
+
.Description
|
| 6 |
+
Pushes the python executable for a virtual environment to the front of the
|
| 7 |
+
$Env:PATH environment variable and sets the prompt to signify that you are
|
| 8 |
+
in a Python virtual environment. Makes use of the command line switches as
|
| 9 |
+
well as the `pyvenv.cfg` file values present in the virtual environment.
|
| 10 |
+
|
| 11 |
+
.Parameter VenvDir
|
| 12 |
+
Path to the directory that contains the virtual environment to activate. The
|
| 13 |
+
default value for this is the parent of the directory that the Activate.ps1
|
| 14 |
+
script is located within.
|
| 15 |
+
|
| 16 |
+
.Parameter Prompt
|
| 17 |
+
The prompt prefix to display when this virtual environment is activated. By
|
| 18 |
+
default, this prompt is the name of the virtual environment folder (VenvDir)
|
| 19 |
+
surrounded by parentheses and followed by a single space (ie. '(.venv) ').
|
| 20 |
+
|
| 21 |
+
.Example
|
| 22 |
+
Activate.ps1
|
| 23 |
+
Activates the Python virtual environment that contains the Activate.ps1 script.
|
| 24 |
+
|
| 25 |
+
.Example
|
| 26 |
+
Activate.ps1 -Verbose
|
| 27 |
+
Activates the Python virtual environment that contains the Activate.ps1 script,
|
| 28 |
+
and shows extra information about the activation as it executes.
|
| 29 |
+
|
| 30 |
+
.Example
|
| 31 |
+
Activate.ps1 -VenvDir C:\Users\MyUser\Common\.venv
|
| 32 |
+
Activates the Python virtual environment located in the specified location.
|
| 33 |
+
|
| 34 |
+
.Example
|
| 35 |
+
Activate.ps1 -Prompt "MyPython"
|
| 36 |
+
Activates the Python virtual environment that contains the Activate.ps1 script,
|
| 37 |
+
and prefixes the current prompt with the specified string (surrounded in
|
| 38 |
+
parentheses) while the virtual environment is active.
|
| 39 |
+
|
| 40 |
+
.Notes
|
| 41 |
+
On Windows, it may be required to enable this Activate.ps1 script by setting the
|
| 42 |
+
execution policy for the user. You can do this by issuing the following PowerShell
|
| 43 |
+
command:
|
| 44 |
+
|
| 45 |
+
PS C:\> Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
|
| 46 |
+
|
| 47 |
+
For more information on Execution Policies:
|
| 48 |
+
https://go.microsoft.com/fwlink/?LinkID=135170
|
| 49 |
+
|
| 50 |
+
#>
|
| 51 |
+
Param(
|
| 52 |
+
[Parameter(Mandatory = $false)]
|
| 53 |
+
[String]
|
| 54 |
+
$VenvDir,
|
| 55 |
+
[Parameter(Mandatory = $false)]
|
| 56 |
+
[String]
|
| 57 |
+
$Prompt
|
| 58 |
+
)
|
| 59 |
+
|
| 60 |
+
<# Function declarations --------------------------------------------------- #>
|
| 61 |
+
|
| 62 |
+
<#
|
| 63 |
+
.Synopsis
|
| 64 |
+
Remove all shell session elements added by the Activate script, including the
|
| 65 |
+
addition of the virtual environment's Python executable from the beginning of
|
| 66 |
+
the PATH variable.
|
| 67 |
+
|
| 68 |
+
.Parameter NonDestructive
|
| 69 |
+
If present, do not remove this function from the global namespace for the
|
| 70 |
+
session.
|
| 71 |
+
|
| 72 |
+
#>
|
| 73 |
+
function global:deactivate ([switch]$NonDestructive) {
|
| 74 |
+
# Revert to original values
|
| 75 |
+
|
| 76 |
+
# The prior prompt:
|
| 77 |
+
if (Test-Path -Path Function:_OLD_VIRTUAL_PROMPT) {
|
| 78 |
+
Copy-Item -Path Function:_OLD_VIRTUAL_PROMPT -Destination Function:prompt
|
| 79 |
+
Remove-Item -Path Function:_OLD_VIRTUAL_PROMPT
|
| 80 |
+
}
|
| 81 |
+
|
| 82 |
+
# The prior PYTHONHOME:
|
| 83 |
+
if (Test-Path -Path Env:_OLD_VIRTUAL_PYTHONHOME) {
|
| 84 |
+
Copy-Item -Path Env:_OLD_VIRTUAL_PYTHONHOME -Destination Env:PYTHONHOME
|
| 85 |
+
Remove-Item -Path Env:_OLD_VIRTUAL_PYTHONHOME
|
| 86 |
+
}
|
| 87 |
+
|
| 88 |
+
# The prior PATH:
|
| 89 |
+
if (Test-Path -Path Env:_OLD_VIRTUAL_PATH) {
|
| 90 |
+
Copy-Item -Path Env:_OLD_VIRTUAL_PATH -Destination Env:PATH
|
| 91 |
+
Remove-Item -Path Env:_OLD_VIRTUAL_PATH
|
| 92 |
+
}
|
| 93 |
+
|
| 94 |
+
# Just remove the VIRTUAL_ENV altogether:
|
| 95 |
+
if (Test-Path -Path Env:VIRTUAL_ENV) {
|
| 96 |
+
Remove-Item -Path env:VIRTUAL_ENV
|
| 97 |
+
}
|
| 98 |
+
|
| 99 |
+
# Just remove VIRTUAL_ENV_PROMPT altogether.
|
| 100 |
+
if (Test-Path -Path Env:VIRTUAL_ENV_PROMPT) {
|
| 101 |
+
Remove-Item -Path env:VIRTUAL_ENV_PROMPT
|
| 102 |
+
}
|
| 103 |
+
|
| 104 |
+
# Just remove the _PYTHON_VENV_PROMPT_PREFIX altogether:
|
| 105 |
+
if (Get-Variable -Name "_PYTHON_VENV_PROMPT_PREFIX" -ErrorAction SilentlyContinue) {
|
| 106 |
+
Remove-Variable -Name _PYTHON_VENV_PROMPT_PREFIX -Scope Global -Force
|
| 107 |
+
}
|
| 108 |
+
|
| 109 |
+
# Leave deactivate function in the global namespace if requested:
|
| 110 |
+
if (-not $NonDestructive) {
|
| 111 |
+
Remove-Item -Path function:deactivate
|
| 112 |
+
}
|
| 113 |
+
}
|
| 114 |
+
|
| 115 |
+
<#
|
| 116 |
+
.Description
|
| 117 |
+
Get-PyVenvConfig parses the values from the pyvenv.cfg file located in the
|
| 118 |
+
given folder, and returns them in a map.
|
| 119 |
+
|
| 120 |
+
For each line in the pyvenv.cfg file, if that line can be parsed into exactly
|
| 121 |
+
two strings separated by `=` (with any amount of whitespace surrounding the =)
|
| 122 |
+
then it is considered a `key = value` line. The left hand string is the key,
|
| 123 |
+
the right hand is the value.
|
| 124 |
+
|
| 125 |
+
If the value starts with a `'` or a `"` then the first and last character is
|
| 126 |
+
stripped from the value before being captured.
|
| 127 |
+
|
| 128 |
+
.Parameter ConfigDir
|
| 129 |
+
Path to the directory that contains the `pyvenv.cfg` file.
|
| 130 |
+
#>
|
| 131 |
+
function Get-PyVenvConfig(
|
| 132 |
+
[String]
|
| 133 |
+
$ConfigDir
|
| 134 |
+
) {
|
| 135 |
+
Write-Verbose "Given ConfigDir=$ConfigDir, obtain values in pyvenv.cfg"
|
| 136 |
+
|
| 137 |
+
# Ensure the file exists, and issue a warning if it doesn't (but still allow the function to continue).
|
| 138 |
+
$pyvenvConfigPath = Join-Path -Resolve -Path $ConfigDir -ChildPath 'pyvenv.cfg' -ErrorAction Continue
|
| 139 |
+
|
| 140 |
+
# An empty map will be returned if no config file is found.
|
| 141 |
+
$pyvenvConfig = @{ }
|
| 142 |
+
|
| 143 |
+
if ($pyvenvConfigPath) {
|
| 144 |
+
|
| 145 |
+
Write-Verbose "File exists, parse `key = value` lines"
|
| 146 |
+
$pyvenvConfigContent = Get-Content -Path $pyvenvConfigPath
|
| 147 |
+
|
| 148 |
+
$pyvenvConfigContent | ForEach-Object {
|
| 149 |
+
$keyval = $PSItem -split "\s*=\s*", 2
|
| 150 |
+
if ($keyval[0] -and $keyval[1]) {
|
| 151 |
+
$val = $keyval[1]
|
| 152 |
+
|
| 153 |
+
# Remove extraneous quotations around a string value.
|
| 154 |
+
if ("'""".Contains($val.Substring(0, 1))) {
|
| 155 |
+
$val = $val.Substring(1, $val.Length - 2)
|
| 156 |
+
}
|
| 157 |
+
|
| 158 |
+
$pyvenvConfig[$keyval[0]] = $val
|
| 159 |
+
Write-Verbose "Adding Key: '$($keyval[0])'='$val'"
|
| 160 |
+
}
|
| 161 |
+
}
|
| 162 |
+
}
|
| 163 |
+
return $pyvenvConfig
|
| 164 |
+
}
|
| 165 |
+
|
| 166 |
+
|
| 167 |
+
<# Begin Activate script --------------------------------------------------- #>
|
| 168 |
+
|
| 169 |
+
# Determine the containing directory of this script
|
| 170 |
+
$VenvExecPath = Split-Path -Parent $MyInvocation.MyCommand.Definition
|
| 171 |
+
$VenvExecDir = Get-Item -Path $VenvExecPath
|
| 172 |
+
|
| 173 |
+
Write-Verbose "Activation script is located in path: '$VenvExecPath'"
|
| 174 |
+
Write-Verbose "VenvExecDir Fullname: '$($VenvExecDir.FullName)"
|
| 175 |
+
Write-Verbose "VenvExecDir Name: '$($VenvExecDir.Name)"
|
| 176 |
+
|
| 177 |
+
# Set values required in priority: CmdLine, ConfigFile, Default
|
| 178 |
+
# First, get the location of the virtual environment, it might not be
|
| 179 |
+
# VenvExecDir if specified on the command line.
|
| 180 |
+
if ($VenvDir) {
|
| 181 |
+
Write-Verbose "VenvDir given as parameter, using '$VenvDir' to determine values"
|
| 182 |
+
}
|
| 183 |
+
else {
|
| 184 |
+
Write-Verbose "VenvDir not given as a parameter, using parent directory name as VenvDir."
|
| 185 |
+
$VenvDir = $VenvExecDir.Parent.FullName.TrimEnd("\\/")
|
| 186 |
+
Write-Verbose "VenvDir=$VenvDir"
|
| 187 |
+
}
|
| 188 |
+
|
| 189 |
+
# Next, read the `pyvenv.cfg` file to determine any required value such
|
| 190 |
+
# as `prompt`.
|
| 191 |
+
$pyvenvCfg = Get-PyVenvConfig -ConfigDir $VenvDir
|
| 192 |
+
|
| 193 |
+
# Next, set the prompt from the command line, or the config file, or
|
| 194 |
+
# just use the name of the virtual environment folder.
|
| 195 |
+
if ($Prompt) {
|
| 196 |
+
Write-Verbose "Prompt specified as argument, using '$Prompt'"
|
| 197 |
+
}
|
| 198 |
+
else {
|
| 199 |
+
Write-Verbose "Prompt not specified as argument to script, checking pyvenv.cfg value"
|
| 200 |
+
if ($pyvenvCfg -and $pyvenvCfg['prompt']) {
|
| 201 |
+
Write-Verbose " Setting based on value in pyvenv.cfg='$($pyvenvCfg['prompt'])'"
|
| 202 |
+
$Prompt = $pyvenvCfg['prompt'];
|
| 203 |
+
}
|
| 204 |
+
else {
|
| 205 |
+
Write-Verbose " Setting prompt based on parent's directory's name. (Is the directory name passed to venv module when creating the virtual environment)"
|
| 206 |
+
Write-Verbose " Got leaf-name of $VenvDir='$(Split-Path -Path $venvDir -Leaf)'"
|
| 207 |
+
$Prompt = Split-Path -Path $venvDir -Leaf
|
| 208 |
+
}
|
| 209 |
+
}
|
| 210 |
+
|
| 211 |
+
Write-Verbose "Prompt = '$Prompt'"
|
| 212 |
+
Write-Verbose "VenvDir='$VenvDir'"
|
| 213 |
+
|
| 214 |
+
# Deactivate any currently active virtual environment, but leave the
|
| 215 |
+
# deactivate function in place.
|
| 216 |
+
deactivate -nondestructive
|
| 217 |
+
|
| 218 |
+
# Now set the environment variable VIRTUAL_ENV, used by many tools to determine
|
| 219 |
+
# that there is an activated venv.
|
| 220 |
+
$env:VIRTUAL_ENV = $VenvDir
|
| 221 |
+
|
| 222 |
+
if (-not $Env:VIRTUAL_ENV_DISABLE_PROMPT) {
|
| 223 |
+
|
| 224 |
+
Write-Verbose "Setting prompt to '$Prompt'"
|
| 225 |
+
|
| 226 |
+
# Set the prompt to include the env name
|
| 227 |
+
# Make sure _OLD_VIRTUAL_PROMPT is global
|
| 228 |
+
function global:_OLD_VIRTUAL_PROMPT { "" }
|
| 229 |
+
Copy-Item -Path function:prompt -Destination function:_OLD_VIRTUAL_PROMPT
|
| 230 |
+
New-Variable -Name _PYTHON_VENV_PROMPT_PREFIX -Description "Python virtual environment prompt prefix" -Scope Global -Option ReadOnly -Visibility Public -Value $Prompt
|
| 231 |
+
|
| 232 |
+
function global:prompt {
|
| 233 |
+
Write-Host -NoNewline -ForegroundColor Green "($_PYTHON_VENV_PROMPT_PREFIX) "
|
| 234 |
+
_OLD_VIRTUAL_PROMPT
|
| 235 |
+
}
|
| 236 |
+
$env:VIRTUAL_ENV_PROMPT = $Prompt
|
| 237 |
+
}
|
| 238 |
+
|
| 239 |
+
# Clear PYTHONHOME
|
| 240 |
+
if (Test-Path -Path Env:PYTHONHOME) {
|
| 241 |
+
Copy-Item -Path Env:PYTHONHOME -Destination Env:_OLD_VIRTUAL_PYTHONHOME
|
| 242 |
+
Remove-Item -Path Env:PYTHONHOME
|
| 243 |
+
}
|
| 244 |
+
|
| 245 |
+
# Add the venv to the PATH
|
| 246 |
+
Copy-Item -Path Env:PATH -Destination Env:_OLD_VIRTUAL_PATH
|
| 247 |
+
$Env:PATH = "$VenvExecDir$([System.IO.Path]::PathSeparator)$Env:PATH"
|
| 248 |
+
|
| 249 |
+
# SIG # Begin signature block
|
| 250 |
+
# MIIvIwYJKoZIhvcNAQcCoIIvFDCCLxACAQExDzANBglghkgBZQMEAgEFADB5Bgor
|
| 251 |
+
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
|
| 252 |
+
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCBnL745ElCYk8vk
|
| 253 |
+
# dBtMuQhLeWJ3ZGfzKW4DHCYzAn+QB6CCE8MwggWQMIIDeKADAgECAhAFmxtXno4h
|
| 254 |
+
# MuI5B72nd3VcMA0GCSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQK
|
| 255 |
+
# EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNV
|
| 256 |
+
# BAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBHNDAeFw0xMzA4MDExMjAwMDBaFw0z
|
| 257 |
+
# ODAxMTUxMjAwMDBaMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ
|
| 258 |
+
# bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0
|
| 259 |
+
# IFRydXN0ZWQgUm9vdCBHNDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB
|
| 260 |
+
# AL/mkHNo3rvkXUo8MCIwaTPswqclLskhPfKK2FnC4SmnPVirdprNrnsbhA3EMB/z
|
| 261 |
+
# G6Q4FutWxpdtHauyefLKEdLkX9YFPFIPUh/GnhWlfr6fqVcWWVVyr2iTcMKyunWZ
|
| 262 |
+
# anMylNEQRBAu34LzB4TmdDttceItDBvuINXJIB1jKS3O7F5OyJP4IWGbNOsFxl7s
|
| 263 |
+
# Wxq868nPzaw0QF+xembud8hIqGZXV59UWI4MK7dPpzDZVu7Ke13jrclPXuU15zHL
|
| 264 |
+
# 2pNe3I6PgNq2kZhAkHnDeMe2scS1ahg4AxCN2NQ3pC4FfYj1gj4QkXCrVYJBMtfb
|
| 265 |
+
# BHMqbpEBfCFM1LyuGwN1XXhm2ToxRJozQL8I11pJpMLmqaBn3aQnvKFPObURWBf3
|
| 266 |
+
# JFxGj2T3wWmIdph2PVldQnaHiZdpekjw4KISG2aadMreSx7nDmOu5tTvkpI6nj3c
|
| 267 |
+
# AORFJYm2mkQZK37AlLTSYW3rM9nF30sEAMx9HJXDj/chsrIRt7t/8tWMcCxBYKqx
|
| 268 |
+
# YxhElRp2Yn72gLD76GSmM9GJB+G9t+ZDpBi4pncB4Q+UDCEdslQpJYls5Q5SUUd0
|
| 269 |
+
# viastkF13nqsX40/ybzTQRESW+UQUOsxxcpyFiIJ33xMdT9j7CFfxCBRa2+xq4aL
|
| 270 |
+
# T8LWRV+dIPyhHsXAj6KxfgommfXkaS+YHS312amyHeUbAgMBAAGjQjBAMA8GA1Ud
|
| 271 |
+
# EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBTs1+OC0nFdZEzf
|
| 272 |
+
# Lmc/57qYrhwPTzANBgkqhkiG9w0BAQwFAAOCAgEAu2HZfalsvhfEkRvDoaIAjeNk
|
| 273 |
+
# aA9Wz3eucPn9mkqZucl4XAwMX+TmFClWCzZJXURj4K2clhhmGyMNPXnpbWvWVPjS
|
| 274 |
+
# PMFDQK4dUPVS/JA7u5iZaWvHwaeoaKQn3J35J64whbn2Z006Po9ZOSJTROvIXQPK
|
| 275 |
+
# 7VB6fWIhCoDIc2bRoAVgX+iltKevqPdtNZx8WorWojiZ83iL9E3SIAveBO6Mm0eB
|
| 276 |
+
# cg3AFDLvMFkuruBx8lbkapdvklBtlo1oepqyNhR6BvIkuQkRUNcIsbiJeoQjYUIp
|
| 277 |
+
# 5aPNoiBB19GcZNnqJqGLFNdMGbJQQXE9P01wI4YMStyB0swylIQNCAmXHE/A7msg
|
| 278 |
+
# dDDS4Dk0EIUhFQEI6FUy3nFJ2SgXUE3mvk3RdazQyvtBuEOlqtPDBURPLDab4vri
|
| 279 |
+
# RbgjU2wGb2dVf0a1TD9uKFp5JtKkqGKX0h7i7UqLvBv9R0oN32dmfrJbQdA75PQ7
|
| 280 |
+
# 9ARj6e/CVABRoIoqyc54zNXqhwQYs86vSYiv85KZtrPmYQ/ShQDnUBrkG5WdGaG5
|
| 281 |
+
# nLGbsQAe79APT0JsyQq87kP6OnGlyE0mpTX9iV28hWIdMtKgK1TtmlfB2/oQzxm3
|
| 282 |
+
# i0objwG2J5VT6LaJbVu8aNQj6ItRolb58KaAoNYes7wPD1N1KarqE3fk3oyBIa0H
|
| 283 |
+
# EEcRrYc9B9F1vM/zZn4wggawMIIEmKADAgECAhAIrUCyYNKcTJ9ezam9k67ZMA0G
|
| 284 |
+
# CSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ
|
| 285 |
+
# bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0
|
| 286 |
+
# IFRydXN0ZWQgUm9vdCBHNDAeFw0yMTA0MjkwMDAwMDBaFw0zNjA0MjgyMzU5NTla
|
| 287 |
+
# MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UE
|
| 288 |
+
# AxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEz
|
| 289 |
+
# ODQgMjAyMSBDQTEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDVtC9C
|
| 290 |
+
# 0CiteLdd1TlZG7GIQvUzjOs9gZdwxbvEhSYwn6SOaNhc9es0JAfhS0/TeEP0F9ce
|
| 291 |
+
# 2vnS1WcaUk8OoVf8iJnBkcyBAz5NcCRks43iCH00fUyAVxJrQ5qZ8sU7H/Lvy0da
|
| 292 |
+
# E6ZMswEgJfMQ04uy+wjwiuCdCcBlp/qYgEk1hz1RGeiQIXhFLqGfLOEYwhrMxe6T
|
| 293 |
+
# SXBCMo/7xuoc82VokaJNTIIRSFJo3hC9FFdd6BgTZcV/sk+FLEikVoQ11vkunKoA
|
| 294 |
+
# FdE3/hoGlMJ8yOobMubKwvSnowMOdKWvObarYBLj6Na59zHh3K3kGKDYwSNHR7Oh
|
| 295 |
+
# D26jq22YBoMbt2pnLdK9RBqSEIGPsDsJ18ebMlrC/2pgVItJwZPt4bRc4G/rJvmM
|
| 296 |
+
# 1bL5OBDm6s6R9b7T+2+TYTRcvJNFKIM2KmYoX7BzzosmJQayg9Rc9hUZTO1i4F4z
|
| 297 |
+
# 8ujo7AqnsAMrkbI2eb73rQgedaZlzLvjSFDzd5Ea/ttQokbIYViY9XwCFjyDKK05
|
| 298 |
+
# huzUtw1T0PhH5nUwjewwk3YUpltLXXRhTT8SkXbev1jLchApQfDVxW0mdmgRQRNY
|
| 299 |
+
# mtwmKwH0iU1Z23jPgUo+QEdfyYFQc4UQIyFZYIpkVMHMIRroOBl8ZhzNeDhFMJlP
|
| 300 |
+
# /2NPTLuqDQhTQXxYPUez+rbsjDIJAsxsPAxWEQIDAQABo4IBWTCCAVUwEgYDVR0T
|
| 301 |
+
# AQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUaDfg67Y7+F8Rhvv+YXsIiGX0TkIwHwYD
|
| 302 |
+
# VR0jBBgwFoAU7NfjgtJxXWRM3y5nP+e6mK4cD08wDgYDVR0PAQH/BAQDAgGGMBMG
|
| 303 |
+
# A1UdJQQMMAoGCCsGAQUFBwMDMHcGCCsGAQUFBwEBBGswaTAkBggrBgEFBQcwAYYY
|
| 304 |
+
# aHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEEGCCsGAQUFBzAChjVodHRwOi8vY2Fj
|
| 305 |
+
# ZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNydDBDBgNV
|
| 306 |
+
# HR8EPDA6MDigNqA0hjJodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRU
|
| 307 |
+
# cnVzdGVkUm9vdEc0LmNybDAcBgNVHSAEFTATMAcGBWeBDAEDMAgGBmeBDAEEATAN
|
| 308 |
+
# BgkqhkiG9w0BAQwFAAOCAgEAOiNEPY0Idu6PvDqZ01bgAhql+Eg08yy25nRm95Ry
|
| 309 |
+
# sQDKr2wwJxMSnpBEn0v9nqN8JtU3vDpdSG2V1T9J9Ce7FoFFUP2cvbaF4HZ+N3HL
|
| 310 |
+
# IvdaqpDP9ZNq4+sg0dVQeYiaiorBtr2hSBh+3NiAGhEZGM1hmYFW9snjdufE5Btf
|
| 311 |
+
# Q/g+lP92OT2e1JnPSt0o618moZVYSNUa/tcnP/2Q0XaG3RywYFzzDaju4ImhvTnh
|
| 312 |
+
# OE7abrs2nfvlIVNaw8rpavGiPttDuDPITzgUkpn13c5UbdldAhQfQDN8A+KVssIh
|
| 313 |
+
# dXNSy0bYxDQcoqVLjc1vdjcshT8azibpGL6QB7BDf5WIIIJw8MzK7/0pNVwfiThV
|
| 314 |
+
# 9zeKiwmhywvpMRr/LhlcOXHhvpynCgbWJme3kuZOX956rEnPLqR0kq3bPKSchh/j
|
| 315 |
+
# wVYbKyP/j7XqiHtwa+aguv06P0WmxOgWkVKLQcBIhEuWTatEQOON8BUozu3xGFYH
|
| 316 |
+
# Ki8QxAwIZDwzj64ojDzLj4gLDb879M4ee47vtevLt/B3E+bnKD+sEq6lLyJsQfmC
|
| 317 |
+
# XBVmzGwOysWGw/YmMwwHS6DTBwJqakAwSEs0qFEgu60bhQjiWQ1tygVQK+pKHJ6l
|
| 318 |
+
# /aCnHwZ05/LWUpD9r4VIIflXO7ScA+2GRfS0YW6/aOImYIbqyK+p/pQd52MbOoZW
|
| 319 |
+
# eE4wggd3MIIFX6ADAgECAhAHHxQbizANJfMU6yMM0NHdMA0GCSqGSIb3DQEBCwUA
|
| 320 |
+
# MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UE
|
| 321 |
+
# AxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEz
|
| 322 |
+
# ODQgMjAyMSBDQTEwHhcNMjIwMTE3MDAwMDAwWhcNMjUwMTE1MjM1OTU5WjB8MQsw
|
| 323 |
+
# CQYDVQQGEwJVUzEPMA0GA1UECBMGT3JlZ29uMRIwEAYDVQQHEwlCZWF2ZXJ0b24x
|
| 324 |
+
# IzAhBgNVBAoTGlB5dGhvbiBTb2Z0d2FyZSBGb3VuZGF0aW9uMSMwIQYDVQQDExpQ
|
| 325 |
+
# eXRob24gU29mdHdhcmUgRm91bmRhdGlvbjCCAiIwDQYJKoZIhvcNAQEBBQADggIP
|
| 326 |
+
# ADCCAgoCggIBAKgc0BTT+iKbtK6f2mr9pNMUTcAJxKdsuOiSYgDFfwhjQy89koM7
|
| 327 |
+
# uP+QV/gwx8MzEt3c9tLJvDccVWQ8H7mVsk/K+X+IufBLCgUi0GGAZUegEAeRlSXx
|
| 328 |
+
# xhYScr818ma8EvGIZdiSOhqjYc4KnfgfIS4RLtZSrDFG2tN16yS8skFa3IHyvWdb
|
| 329 |
+
# D9PvZ4iYNAS4pjYDRjT/9uzPZ4Pan+53xZIcDgjiTwOh8VGuppxcia6a7xCyKoOA
|
| 330 |
+
# GjvCyQsj5223v1/Ig7Dp9mGI+nh1E3IwmyTIIuVHyK6Lqu352diDY+iCMpk9Zanm
|
| 331 |
+
# SjmB+GMVs+H/gOiofjjtf6oz0ki3rb7sQ8fTnonIL9dyGTJ0ZFYKeb6BLA66d2GA
|
| 332 |
+
# LwxZhLe5WH4Np9HcyXHACkppsE6ynYjTOd7+jN1PRJahN1oERzTzEiV6nCO1M3U1
|
| 333 |
+
# HbPTGyq52IMFSBM2/07WTJSbOeXjvYR7aUxK9/ZkJiacl2iZI7IWe7JKhHohqKuc
|
| 334 |
+
# eQNyOzxTakLcRkzynvIrk33R9YVqtB4L6wtFxhUjvDnQg16xot2KVPdfyPAWd81w
|
| 335 |
+
# tZADmrUtsZ9qG79x1hBdyOl4vUtVPECuyhCxaw+faVjumapPUnwo8ygflJJ74J+B
|
| 336 |
+
# Yxf6UuD7m8yzsfXWkdv52DjL74TxzuFTLHPyARWCSCAbzn3ZIly+qIqDAgMBAAGj
|
| 337 |
+
# ggIGMIICAjAfBgNVHSMEGDAWgBRoN+Drtjv4XxGG+/5hewiIZfROQjAdBgNVHQ4E
|
| 338 |
+
# FgQUt/1Teh2XDuUj2WW3siYWJgkZHA8wDgYDVR0PAQH/BAQDAgeAMBMGA1UdJQQM
|
| 339 |
+
# MAoGCCsGAQUFBwMDMIG1BgNVHR8Ega0wgaowU6BRoE+GTWh0dHA6Ly9jcmwzLmRp
|
| 340 |
+
# Z2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNENvZGVTaWduaW5nUlNBNDA5NlNI
|
| 341 |
+
# QTM4NDIwMjFDQTEuY3JsMFOgUaBPhk1odHRwOi8vY3JsNC5kaWdpY2VydC5jb20v
|
| 342 |
+
# RGlnaUNlcnRUcnVzdGVkRzRDb2RlU2lnbmluZ1JTQTQwOTZTSEEzODQyMDIxQ0Ex
|
| 343 |
+
# LmNybDA+BgNVHSAENzA1MDMGBmeBDAEEATApMCcGCCsGAQUFBwIBFhtodHRwOi8v
|
| 344 |
+
# d3d3LmRpZ2ljZXJ0LmNvbS9DUFMwgZQGCCsGAQUFBwEBBIGHMIGEMCQGCCsGAQUF
|
| 345 |
+
# BzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wXAYIKwYBBQUHMAKGUGh0dHA6
|
| 346 |
+
# Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNENvZGVTaWdu
|
| 347 |
+
# aW5nUlNBNDA5NlNIQTM4NDIwMjFDQTEuY3J0MAwGA1UdEwEB/wQCMAAwDQYJKoZI
|
| 348 |
+
# hvcNAQELBQADggIBABxv4AeV/5ltkELHSC63fXAFYS5tadcWTiNc2rskrNLrfH1N
|
| 349 |
+
# s0vgSZFoQxYBFKI159E8oQQ1SKbTEubZ/B9kmHPhprHya08+VVzxC88pOEvz68nA
|
| 350 |
+
# 82oEM09584aILqYmj8Pj7h/kmZNzuEL7WiwFa/U1hX+XiWfLIJQsAHBla0i7QRF2
|
| 351 |
+
# de8/VSF0XXFa2kBQ6aiTsiLyKPNbaNtbcucaUdn6vVUS5izWOXM95BSkFSKdE45O
|
| 352 |
+
# q3FForNJXjBvSCpwcP36WklaHL+aHu1upIhCTUkzTHMh8b86WmjRUqbrnvdyR2yd
|
| 353 |
+
# I5l1OqcMBjkpPpIV6wcc+KY/RH2xvVuuoHjlUjwq2bHiNoX+W1scCpnA8YTs2d50
|
| 354 |
+
# jDHUgwUo+ciwpffH0Riq132NFmrH3r67VaN3TuBxjI8SIZM58WEDkbeoriDk3hxU
|
| 355 |
+
# 8ZWV7b8AW6oyVBGfM06UgkfMb58h+tJPrFx8VI/WLq1dTqMfZOm5cuclMnUHs2uq
|
| 356 |
+
# rRNtnV8UfidPBL4ZHkTcClQbCoz0UbLhkiDvIS00Dn+BBcxw/TKqVL4Oaz3bkMSs
|
| 357 |
+
# M46LciTeucHY9ExRVt3zy7i149sd+F4QozPqn7FrSVHXmem3r7bjyHTxOgqxRCVa
|
| 358 |
+
# 18Vtx7P/8bYSBeS+WHCKcliFCecspusCDSlnRUjZwyPdP0VHxaZg2unjHY3rMYIa
|
| 359 |
+
# tjCCGrICAQEwfTBpMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIElu
|
| 360 |
+
# Yy4xQTA/BgNVBAMTOERpZ2lDZXJ0IFRydXN0ZWQgRzQgQ29kZSBTaWduaW5nIFJT
|
| 361 |
+
# QTQwOTYgU0hBMzg0IDIwMjEgQ0ExAhAHHxQbizANJfMU6yMM0NHdMA0GCWCGSAFl
|
| 362 |
+
# AwQCAQUAoIHIMBkGCSqGSIb3DQEJAzEMBgorBgEEAYI3AgEEMBwGCisGAQQBgjcC
|
| 363 |
+
# AQsxDjAMBgorBgEEAYI3AgEVMC8GCSqGSIb3DQEJBDEiBCBnAZ6P7YvTwq0fbF62
|
| 364 |
+
# o7E75R0LxsW5OtyYiFESQckLhjBcBgorBgEEAYI3AgEMMU4wTKBGgEQAQgB1AGkA
|
| 365 |
+
# bAB0ADoAIABSAGUAbABlAGEAcwBlAF8AdgAzAC4AMQAyAC4ANgBfADIAMAAyADQA
|
| 366 |
+
# MAA5ADAANgAuADAAMqECgAAwDQYJKoZIhvcNAQEBBQAEggIAhen5GN03SF9I96DT
|
| 367 |
+
# rrWEsN7FAyx8BHoRf9WFBqoBXpFkBwlE6OWj/rxohuwB/b+3vcBGWaP497ACku4l
|
| 368 |
+
# lgrWCrmYOVMKTjeHtDDkvgmygvGAtWB5drf56553na9RYjTxRqxto5LBMsHtPZy6
|
| 369 |
+
# 1D+touyLSHx+QXzqXO4ssUq7oHtsmjDCKMLdcTuoqNGtpxaIwwlOAK+0DaLLUpkX
|
| 370 |
+
# VRUUzMWBb+2FlmJ2wWtXXs6OtlACm4By2hHmKhd6OYwnHPe6fDVdrhGa0BcDAIIO
|
| 371 |
+
# +elm895ddmfX2KqHWrKpgZ/0DM46pbEiYX4GVwY+kmrK9p8XF7c50c331vPPuImL
|
| 372 |
+
# URRShtCM9F/5e522nQm0NxQ0Pz+thMD+qGBA8WuSoD+RRG+JKOXgM8sMX46goR8P
|
| 373 |
+
# 1IJLeUnEKSOgMNcP0EUeWthrqXRjVgNcazIDgPFpPGMyo4Pp0D8SPvp/RzP3CPVo
|
| 374 |
+
# uVj6r0OnhyoDuDEX4KCyo/+TCSm+2T+hv+cPWQaukovXF1TmahWb/8j1+K1RkCVd
|
| 375 |
+
# UQ5v07AHYoHmJ2gxEgtM9qaVDx4woVVCpUrOhiAP/K1WSRw710oTqECG+4y+g67D
|
| 376 |
+
# P2UuOxxaxhPk0pITFj9pZQcVsrCk5QbW3Yj/I3fISZgjVfYK1IDKzaWQQuBhOuim
|
| 377 |
+
# j2/Tfcg+cLDbY4XEs5vpbKSYsCWhghc/MIIXOwYKKwYBBAGCNwMDATGCFyswghcn
|
| 378 |
+
# BgkqhkiG9w0BBwKgghcYMIIXFAIBAzEPMA0GCWCGSAFlAwQCAQUAMHcGCyqGSIb3
|
| 379 |
+
# DQEJEAEEoGgEZjBkAgEBBglghkgBhv1sBwEwMTANBglghkgBZQMEAgEFAAQgS2eq
|
| 380 |
+
# 9RcYET/J2twNl3zStqvYDUBOrSdHvMcFbSu+C2sCEGHEWhqgAhMA1D+QZOB9TC4Y
|
| 381 |
+
# DzIwMjQwOTA2MjAyNzExWqCCEwkwggbCMIIEqqADAgECAhAFRK/zlJ0IOaa/2z9f
|
| 382 |
+
# 5WEWMA0GCSqGSIb3DQEBCwUAMGMxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdp
|
| 383 |
+
# Q2VydCwgSW5jLjE7MDkGA1UEAxMyRGlnaUNlcnQgVHJ1c3RlZCBHNCBSU0E0MDk2
|
| 384 |
+
# IFNIQTI1NiBUaW1lU3RhbXBpbmcgQ0EwHhcNMjMwNzE0MDAwMDAwWhcNMzQxMDEz
|
| 385 |
+
# MjM1OTU5WjBIMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4x
|
| 386 |
+
# IDAeBgNVBAMTF0RpZ2lDZXJ0IFRpbWVzdGFtcCAyMDIzMIICIjANBgkqhkiG9w0B
|
| 387 |
+
# AQEFAAOCAg8AMIICCgKCAgEAo1NFhx2DjlusPlSzI+DPn9fl0uddoQ4J3C9Io5d6
|
| 388 |
+
# OyqcZ9xiFVjBqZMRp82qsmrdECmKHmJjadNYnDVxvzqX65RQjxwg6seaOy+WZuNp
|
| 389 |
+
# 52n+W8PWKyAcwZeUtKVQgfLPywemMGjKg0La/H8JJJSkghraarrYO8pd3hkYhftF
|
| 390 |
+
# 6g1hbJ3+cV7EBpo88MUueQ8bZlLjyNY+X9pD04T10Mf2SC1eRXWWdf7dEKEbg8G4
|
| 391 |
+
# 5lKVtUfXeCk5a+B4WZfjRCtK1ZXO7wgX6oJkTf8j48qG7rSkIWRw69XloNpjsy7p
|
| 392 |
+
# Be6q9iT1HbybHLK3X9/w7nZ9MZllR1WdSiQvrCuXvp/k/XtzPjLuUjT71Lvr1KAs
|
| 393 |
+
# NJvj3m5kGQc3AZEPHLVRzapMZoOIaGK7vEEbeBlt5NkP4FhB+9ixLOFRr7StFQYU
|
| 394 |
+
# 6mIIE9NpHnxkTZ0P387RXoyqq1AVybPKvNfEO2hEo6U7Qv1zfe7dCv95NBB+plwK
|
| 395 |
+
# WEwAPoVpdceDZNZ1zY8SdlalJPrXxGshuugfNJgvOuprAbD3+yqG7HtSOKmYCaFx
|
| 396 |
+
# smxxrz64b5bV4RAT/mFHCoz+8LbH1cfebCTwv0KCyqBxPZySkwS0aXAnDU+3tTbR
|
| 397 |
+
# yV8IpHCj7ArxES5k4MsiK8rxKBMhSVF+BmbTO77665E42FEHypS34lCh8zrTioPL
|
| 398 |
+
# QHsCAwEAAaOCAYswggGHMA4GA1UdDwEB/wQEAwIHgDAMBgNVHRMBAf8EAjAAMBYG
|
| 399 |
+
# A1UdJQEB/wQMMAoGCCsGAQUFBwMIMCAGA1UdIAQZMBcwCAYGZ4EMAQQCMAsGCWCG
|
| 400 |
+
# SAGG/WwHATAfBgNVHSMEGDAWgBS6FtltTYUvcyl2mi91jGogj57IbzAdBgNVHQ4E
|
| 401 |
+
# FgQUpbbvE+fvzdBkodVWqWUxo97V40kwWgYDVR0fBFMwUTBPoE2gS4ZJaHR0cDov
|
| 402 |
+
# L2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZEc0UlNBNDA5NlNIQTI1
|
| 403 |
+
# NlRpbWVTdGFtcGluZ0NBLmNybDCBkAYIKwYBBQUHAQEEgYMwgYAwJAYIKwYBBQUH
|
| 404 |
+
# MAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBYBggrBgEFBQcwAoZMaHR0cDov
|
| 405 |
+
# L2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZEc0UlNBNDA5NlNI
|
| 406 |
+
# QTI1NlRpbWVTdGFtcGluZ0NBLmNydDANBgkqhkiG9w0BAQsFAAOCAgEAgRrW3qCp
|
| 407 |
+
# tZgXvHCNT4o8aJzYJf/LLOTN6l0ikuyMIgKpuM+AqNnn48XtJoKKcS8Y3U623mzX
|
| 408 |
+
# 4WCcK+3tPUiOuGu6fF29wmE3aEl3o+uQqhLXJ4Xzjh6S2sJAOJ9dyKAuJXglnSoF
|
| 409 |
+
# eoQpmLZXeY/bJlYrsPOnvTcM2Jh2T1a5UsK2nTipgedtQVyMadG5K8TGe8+c+nji
|
| 410 |
+
# kxp2oml101DkRBK+IA2eqUTQ+OVJdwhaIcW0z5iVGlS6ubzBaRm6zxbygzc0brBB
|
| 411 |
+
# Jt3eWpdPM43UjXd9dUWhpVgmagNF3tlQtVCMr1a9TMXhRsUo063nQwBw3syYnhmJ
|
| 412 |
+
# A+rUkTfvTVLzyWAhxFZH7doRS4wyw4jmWOK22z75X7BC1o/jF5HRqsBV44a/rCcs
|
| 413 |
+
# QdCaM0qoNtS5cpZ+l3k4SF/Kwtw9Mt911jZnWon49qfH5U81PAC9vpwqbHkB3NpE
|
| 414 |
+
# 5jreODsHXjlY9HxzMVWggBHLFAx+rrz+pOt5Zapo1iLKO+uagjVXKBbLafIymrLS
|
| 415 |
+
# 2Dq4sUaGa7oX/cR3bBVsrquvczroSUa31X/MtjjA2Owc9bahuEMs305MfR5ocMB3
|
| 416 |
+
# CtQC4Fxguyj/OOVSWtasFyIjTvTs0xf7UGv/B3cfcZdEQcm4RtNsMnxYL2dHZeUb
|
| 417 |
+
# c7aZ+WssBkbvQR7w8F/g29mtkIBEr4AQQYowggauMIIElqADAgECAhAHNje3JFR8
|
| 418 |
+
# 2Ees/ShmKl5bMA0GCSqGSIb3DQEBCwUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQK
|
| 419 |
+
# EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNV
|
| 420 |
+
# BAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBHNDAeFw0yMjAzMjMwMDAwMDBaFw0z
|
| 421 |
+
# NzAzMjIyMzU5NTlaMGMxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwg
|
| 422 |
+
# SW5jLjE7MDkGA1UEAxMyRGlnaUNlcnQgVHJ1c3RlZCBHNCBSU0E0MDk2IFNIQTI1
|
| 423 |
+
# NiBUaW1lU3RhbXBpbmcgQ0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
|
| 424 |
+
# AQDGhjUGSbPBPXJJUVXHJQPE8pE3qZdRodbSg9GeTKJtoLDMg/la9hGhRBVCX6SI
|
| 425 |
+
# 82j6ffOciQt/nR+eDzMfUBMLJnOWbfhXqAJ9/UO0hNoR8XOxs+4rgISKIhjf69o9
|
| 426 |
+
# xBd/qxkrPkLcZ47qUT3w1lbU5ygt69OxtXXnHwZljZQp09nsad/ZkIdGAHvbREGJ
|
| 427 |
+
# 3HxqV3rwN3mfXazL6IRktFLydkf3YYMZ3V+0VAshaG43IbtArF+y3kp9zvU5Emfv
|
| 428 |
+
# DqVjbOSmxR3NNg1c1eYbqMFkdECnwHLFuk4fsbVYTXn+149zk6wsOeKlSNbwsDET
|
| 429 |
+
# qVcplicu9Yemj052FVUmcJgmf6AaRyBD40NjgHt1biclkJg6OBGz9vae5jtb7IHe
|
| 430 |
+
# IhTZgirHkr+g3uM+onP65x9abJTyUpURK1h0QCirc0PO30qhHGs4xSnzyqqWc0Jo
|
| 431 |
+
# n7ZGs506o9UD4L/wojzKQtwYSH8UNM/STKvvmz3+DrhkKvp1KCRB7UK/BZxmSVJQ
|
| 432 |
+
# 9FHzNklNiyDSLFc1eSuo80VgvCONWPfcYd6T/jnA+bIwpUzX6ZhKWD7TA4j+s4/T
|
| 433 |
+
# Xkt2ElGTyYwMO1uKIqjBJgj5FBASA31fI7tk42PgpuE+9sJ0sj8eCXbsq11GdeJg
|
| 434 |
+
# o1gJASgADoRU7s7pXcheMBK9Rp6103a50g5rmQzSM7TNsQIDAQABo4IBXTCCAVkw
|
| 435 |
+
# EgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUuhbZbU2FL3MpdpovdYxqII+e
|
| 436 |
+
# yG8wHwYDVR0jBBgwFoAU7NfjgtJxXWRM3y5nP+e6mK4cD08wDgYDVR0PAQH/BAQD
|
| 437 |
+
# AgGGMBMGA1UdJQQMMAoGCCsGAQUFBwMIMHcGCCsGAQUFBwEBBGswaTAkBggrBgEF
|
| 438 |
+
# BQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEEGCCsGAQUFBzAChjVodHRw
|
| 439 |
+
# Oi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNy
|
| 440 |
+
# dDBDBgNVHR8EPDA6MDigNqA0hjJodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGln
|
| 441 |
+
# aUNlcnRUcnVzdGVkUm9vdEc0LmNybDAgBgNVHSAEGTAXMAgGBmeBDAEEAjALBglg
|
| 442 |
+
# hkgBhv1sBwEwDQYJKoZIhvcNAQELBQADggIBAH1ZjsCTtm+YqUQiAX5m1tghQuGw
|
| 443 |
+
# GC4QTRPPMFPOvxj7x1Bd4ksp+3CKDaopafxpwc8dB+k+YMjYC+VcW9dth/qEICU0
|
| 444 |
+
# MWfNthKWb8RQTGIdDAiCqBa9qVbPFXONASIlzpVpP0d3+3J0FNf/q0+KLHqrhc1D
|
| 445 |
+
# X+1gtqpPkWaeLJ7giqzl/Yy8ZCaHbJK9nXzQcAp876i8dU+6WvepELJd6f8oVInw
|
| 446 |
+
# 1YpxdmXazPByoyP6wCeCRK6ZJxurJB4mwbfeKuv2nrF5mYGjVoarCkXJ38SNoOeY
|
| 447 |
+
# +/umnXKvxMfBwWpx2cYTgAnEtp/Nh4cku0+jSbl3ZpHxcpzpSwJSpzd+k1OsOx0I
|
| 448 |
+
# SQ+UzTl63f8lY5knLD0/a6fxZsNBzU+2QJshIUDQtxMkzdwdeDrknq3lNHGS1yZr
|
| 449 |
+
# 5Dhzq6YBT70/O3itTK37xJV77QpfMzmHQXh6OOmc4d0j/R0o08f56PGYX/sr2H7y
|
| 450 |
+
# Rp11LB4nLCbbbxV7HhmLNriT1ObyF5lZynDwN7+YAN8gFk8n+2BnFqFmut1VwDop
|
| 451 |
+
# hrCYoCvtlUG3OtUVmDG0YgkPCr2B2RP+v6TR81fZvAT6gt4y3wSJ8ADNXcL50CN/
|
| 452 |
+
# AAvkdgIm2fBldkKmKYcJRyvmfxqkhQ/8mJb2VVQrH4D6wPIOK+XW+6kvRBVK5xMO
|
| 453 |
+
# Hds3OBqhK/bt1nz8MIIFjTCCBHWgAwIBAgIQDpsYjvnQLefv21DiCEAYWjANBgkq
|
| 454 |
+
# hkiG9w0BAQwFADBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5j
|
| 455 |
+
# MRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBB
|
| 456 |
+
# c3N1cmVkIElEIFJvb3QgQ0EwHhcNMjIwODAxMDAwMDAwWhcNMzExMTA5MjM1OTU5
|
| 457 |
+
# WjBiMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQL
|
| 458 |
+
# ExB3d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJv
|
| 459 |
+
# b3QgRzQwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC/5pBzaN675F1K
|
| 460 |
+
# PDAiMGkz7MKnJS7JIT3yithZwuEppz1Yq3aaza57G4QNxDAf8xukOBbrVsaXbR2r
|
| 461 |
+
# snnyyhHS5F/WBTxSD1Ifxp4VpX6+n6lXFllVcq9ok3DCsrp1mWpzMpTREEQQLt+C
|
| 462 |
+
# 8weE5nQ7bXHiLQwb7iDVySAdYyktzuxeTsiT+CFhmzTrBcZe7FsavOvJz82sNEBf
|
| 463 |
+
# sXpm7nfISKhmV1efVFiODCu3T6cw2Vbuyntd463JT17lNecxy9qTXtyOj4DatpGY
|
| 464 |
+
# QJB5w3jHtrHEtWoYOAMQjdjUN6QuBX2I9YI+EJFwq1WCQTLX2wRzKm6RAXwhTNS8
|
| 465 |
+
# rhsDdV14Ztk6MUSaM0C/CNdaSaTC5qmgZ92kJ7yhTzm1EVgX9yRcRo9k98FpiHaY
|
| 466 |
+
# dj1ZXUJ2h4mXaXpI8OCiEhtmmnTK3kse5w5jrubU75KSOp493ADkRSWJtppEGSt+
|
| 467 |
+
# wJS00mFt6zPZxd9LBADMfRyVw4/3IbKyEbe7f/LVjHAsQWCqsWMYRJUadmJ+9oCw
|
| 468 |
+
# ++hkpjPRiQfhvbfmQ6QYuKZ3AeEPlAwhHbJUKSWJbOUOUlFHdL4mrLZBdd56rF+N
|
| 469 |
+
# P8m800ERElvlEFDrMcXKchYiCd98THU/Y+whX8QgUWtvsauGi0/C1kVfnSD8oR7F
|
| 470 |
+
# wI+isX4KJpn15GkvmB0t9dmpsh3lGwIDAQABo4IBOjCCATYwDwYDVR0TAQH/BAUw
|
| 471 |
+
# AwEB/zAdBgNVHQ4EFgQU7NfjgtJxXWRM3y5nP+e6mK4cD08wHwYDVR0jBBgwFoAU
|
| 472 |
+
# Reuir/SSy4IxLVGLp6chnfNtyA8wDgYDVR0PAQH/BAQDAgGGMHkGCCsGAQUFBwEB
|
| 473 |
+
# BG0wazAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEMGCCsG
|
| 474 |
+
# AQUFBzAChjdodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRBc3N1
|
| 475 |
+
# cmVkSURSb290Q0EuY3J0MEUGA1UdHwQ+MDwwOqA4oDaGNGh0dHA6Ly9jcmwzLmRp
|
| 476 |
+
# Z2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcmwwEQYDVR0gBAow
|
| 477 |
+
# CDAGBgRVHSAAMA0GCSqGSIb3DQEBDAUAA4IBAQBwoL9DXFXnOF+go3QbPbYW1/e/
|
| 478 |
+
# Vwe9mqyhhyzshV6pGrsi+IcaaVQi7aSId229GhT0E0p6Ly23OO/0/4C5+KH38nLe
|
| 479 |
+
# JLxSA8hO0Cre+i1Wz/n096wwepqLsl7Uz9FDRJtDIeuWcqFItJnLnU+nBgMTdydE
|
| 480 |
+
# 1Od/6Fmo8L8vC6bp8jQ87PcDx4eo0kxAGTVGamlUsLihVo7spNU96LHc/RzY9Hda
|
| 481 |
+
# XFSMb++hUD38dglohJ9vytsgjTVgHAIDyyCwrFigDkBjxZgiwbJZ9VVrzyerbHbO
|
| 482 |
+
# byMt9H5xaiNrIv8SuFQtJ37YOtnwtoeW/VvRXKwYw02fc7cBqZ9Xql4o4rmUMYID
|
| 483 |
+
# djCCA3ICAQEwdzBjMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIElu
|
| 484 |
+
# Yy4xOzA5BgNVBAMTMkRpZ2lDZXJ0IFRydXN0ZWQgRzQgUlNBNDA5NiBTSEEyNTYg
|
| 485 |
+
# VGltZVN0YW1waW5nIENBAhAFRK/zlJ0IOaa/2z9f5WEWMA0GCWCGSAFlAwQCAQUA
|
| 486 |
+
# oIHRMBoGCSqGSIb3DQEJAzENBgsqhkiG9w0BCRABBDAcBgkqhkiG9w0BCQUxDxcN
|
| 487 |
+
# MjQwOTA2MjAyNzExWjArBgsqhkiG9w0BCRACDDEcMBowGDAWBBRm8CsywsLJD4Jd
|
| 488 |
+
# zqqKycZPGZzPQDAvBgkqhkiG9w0BCQQxIgQgXSdFKsIxhS4gvdZFC5i8csELx4EN
|
| 489 |
+
# gje4K7DDRX8dz3AwNwYLKoZIhvcNAQkQAi8xKDAmMCQwIgQg0vbkbe10IszR1EBX
|
| 490 |
+
# aEE2b4KK2lWarjMWr00amtQMeCgwDQYJKoZIhvcNAQEBBQAEggIAYX9aC647tWiS
|
| 491 |
+
# rGwjsC+5s2CosHEwRzUG9YNI58OJgUfCwsfgMkgKWkSi/K7mumf5RHkU+P+HCwoy
|
| 492 |
+
# kvIOZ7viK9fcAkK9zS3eKPUA6mGQS11yEnEhRUZbrrsG1uHQO+gSO2SgyLs8+3vX
|
| 493 |
+
# /8+YEl1IkGbw4/oeLavq79jULQqZ6/00n0E0nFDmbprjFK4wUX4CoIqt8AAWCt4F
|
| 494 |
+
# Az8XwvYxa63A2JQmeDzDAWR4lfNbREQaC3MdnqbnvQIBQUspJsn3t7zxU+ubzCez
|
| 495 |
+
# kCkk+7Tt5FFCP9OJvc/BEv3HcXrTAoZ4VFfAwL9K1DQ4A3hbsvKlwV0OxZlhouMd
|
| 496 |
+
# fGq+R8IGMsy7mGxeHx67nzKIr6Rjd426YsGskp5D3gE9shvH8i3GOTBi2Y9JUnaU
|
| 497 |
+
# /KX+IMzKbvR0Y9echgTb17v3D/+fYzDD/kSGJcuQEIbJEyYsCDBF53xoKd6K0Pgz
|
| 498 |
+
# 2drucT9otwOLUgGfR1N6lRwDtkMHYB25OMIKLYtcfHjQZn+Howq/TVUbp9ohhW1N
|
| 499 |
+
# jim3nJfNvmRe2zN5476SOn86GzzrqxfAMCTtbZeim2ltOHxlnPUE8EJLdRFesKMK
|
| 500 |
+
# 6izgaxptlT+MO0R8jx1VoOn+qbQPbNn2GCOUvh/yFkjwDLtFb/rNdoWMNrSMZDhV
|
| 501 |
+
# mRCM17SwjW6qRmsrC7VSaSAgPsokYM0=
|
| 502 |
+
# SIG # End signature block
|
INSTALLER
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
pip
|
LICENSE
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
METADATA
ADDED
|
@@ -0,0 +1,549 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Metadata-Version: 2.1
|
| 2 |
+
Name: torch
|
| 3 |
+
Version: 2.6.0
|
| 4 |
+
Summary: Tensors and Dynamic neural networks in Python with strong GPU acceleration
|
| 5 |
+
Home-page: https://pytorch.org/
|
| 6 |
+
Download-URL: https://github.com/pytorch/pytorch/tags
|
| 7 |
+
Author: PyTorch Team
|
| 8 |
+
Author-email: [email protected]
|
| 9 |
+
License: BSD-3-Clause
|
| 10 |
+
Keywords: pytorch,machine learning
|
| 11 |
+
Classifier: Development Status :: 5 - Production/Stable
|
| 12 |
+
Classifier: Intended Audience :: Developers
|
| 13 |
+
Classifier: Intended Audience :: Education
|
| 14 |
+
Classifier: Intended Audience :: Science/Research
|
| 15 |
+
Classifier: License :: OSI Approved :: BSD License
|
| 16 |
+
Classifier: Topic :: Scientific/Engineering
|
| 17 |
+
Classifier: Topic :: Scientific/Engineering :: Mathematics
|
| 18 |
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
| 19 |
+
Classifier: Topic :: Software Development
|
| 20 |
+
Classifier: Topic :: Software Development :: Libraries
|
| 21 |
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
| 22 |
+
Classifier: Programming Language :: C++
|
| 23 |
+
Classifier: Programming Language :: Python :: 3
|
| 24 |
+
Classifier: Programming Language :: Python :: 3.9
|
| 25 |
+
Classifier: Programming Language :: Python :: 3.10
|
| 26 |
+
Classifier: Programming Language :: Python :: 3.11
|
| 27 |
+
Classifier: Programming Language :: Python :: 3.12
|
| 28 |
+
Classifier: Programming Language :: Python :: 3.13
|
| 29 |
+
Requires-Python: >=3.9.0
|
| 30 |
+
Description-Content-Type: text/markdown
|
| 31 |
+
License-File: LICENSE
|
| 32 |
+
License-File: NOTICE
|
| 33 |
+
Requires-Dist: filelock
|
| 34 |
+
Requires-Dist: typing-extensions>=4.10.0
|
| 35 |
+
Requires-Dist: networkx
|
| 36 |
+
Requires-Dist: jinja2
|
| 37 |
+
Requires-Dist: fsspec
|
| 38 |
+
Requires-Dist: nvidia-cuda-nvrtc-cu12==12.4.127; platform_system == "Linux" and platform_machine == "x86_64"
|
| 39 |
+
Requires-Dist: nvidia-cuda-runtime-cu12==12.4.127; platform_system == "Linux" and platform_machine == "x86_64"
|
| 40 |
+
Requires-Dist: nvidia-cuda-cupti-cu12==12.4.127; platform_system == "Linux" and platform_machine == "x86_64"
|
| 41 |
+
Requires-Dist: nvidia-cudnn-cu12==9.1.0.70; platform_system == "Linux" and platform_machine == "x86_64"
|
| 42 |
+
Requires-Dist: nvidia-cublas-cu12==12.4.5.8; platform_system == "Linux" and platform_machine == "x86_64"
|
| 43 |
+
Requires-Dist: nvidia-cufft-cu12==11.2.1.3; platform_system == "Linux" and platform_machine == "x86_64"
|
| 44 |
+
Requires-Dist: nvidia-curand-cu12==10.3.5.147; platform_system == "Linux" and platform_machine == "x86_64"
|
| 45 |
+
Requires-Dist: nvidia-cusolver-cu12==11.6.1.9; platform_system == "Linux" and platform_machine == "x86_64"
|
| 46 |
+
Requires-Dist: nvidia-cusparse-cu12==12.3.1.170; platform_system == "Linux" and platform_machine == "x86_64"
|
| 47 |
+
Requires-Dist: nvidia-cusparselt-cu12==0.6.2; platform_system == "Linux" and platform_machine == "x86_64"
|
| 48 |
+
Requires-Dist: nvidia-nccl-cu12==2.21.5; platform_system == "Linux" and platform_machine == "x86_64"
|
| 49 |
+
Requires-Dist: nvidia-nvtx-cu12==12.4.127; platform_system == "Linux" and platform_machine == "x86_64"
|
| 50 |
+
Requires-Dist: nvidia-nvjitlink-cu12==12.4.127; platform_system == "Linux" and platform_machine == "x86_64"
|
| 51 |
+
Requires-Dist: triton==3.2.0; platform_system == "Linux" and platform_machine == "x86_64"
|
| 52 |
+
Requires-Dist: setuptools; python_version >= "3.12"
|
| 53 |
+
Requires-Dist: sympy==1.13.1; python_version >= "3.9"
|
| 54 |
+
Provides-Extra: opt-einsum
|
| 55 |
+
Requires-Dist: opt-einsum>=3.3; extra == "opt-einsum"
|
| 56 |
+
Provides-Extra: optree
|
| 57 |
+
Requires-Dist: optree>=0.13.0; extra == "optree"
|
| 58 |
+
|
| 59 |
+

|
| 60 |
+
|
| 61 |
+
--------------------------------------------------------------------------------
|
| 62 |
+
|
| 63 |
+
PyTorch is a Python package that provides two high-level features:
|
| 64 |
+
- Tensor computation (like NumPy) with strong GPU acceleration
|
| 65 |
+
- Deep neural networks built on a tape-based autograd system
|
| 66 |
+
|
| 67 |
+
You can reuse your favorite Python packages such as NumPy, SciPy, and Cython to extend PyTorch when needed.
|
| 68 |
+
|
| 69 |
+
Our trunk health (Continuous Integration signals) can be found at [hud.pytorch.org](https://hud.pytorch.org/ci/pytorch/pytorch/main).
|
| 70 |
+
|
| 71 |
+
<!-- toc -->
|
| 72 |
+
|
| 73 |
+
- [More About PyTorch](#more-about-pytorch)
|
| 74 |
+
- [A GPU-Ready Tensor Library](#a-gpu-ready-tensor-library)
|
| 75 |
+
- [Dynamic Neural Networks: Tape-Based Autograd](#dynamic-neural-networks-tape-based-autograd)
|
| 76 |
+
- [Python First](#python-first)
|
| 77 |
+
- [Imperative Experiences](#imperative-experiences)
|
| 78 |
+
- [Fast and Lean](#fast-and-lean)
|
| 79 |
+
- [Extensions Without Pain](#extensions-without-pain)
|
| 80 |
+
- [Installation](#installation)
|
| 81 |
+
- [Binaries](#binaries)
|
| 82 |
+
- [NVIDIA Jetson Platforms](#nvidia-jetson-platforms)
|
| 83 |
+
- [From Source](#from-source)
|
| 84 |
+
- [Prerequisites](#prerequisites)
|
| 85 |
+
- [NVIDIA CUDA Support](#nvidia-cuda-support)
|
| 86 |
+
- [AMD ROCm Support](#amd-rocm-support)
|
| 87 |
+
- [Intel GPU Support](#intel-gpu-support)
|
| 88 |
+
- [Get the PyTorch Source](#get-the-pytorch-source)
|
| 89 |
+
- [Install Dependencies](#install-dependencies)
|
| 90 |
+
- [Install PyTorch](#install-pytorch)
|
| 91 |
+
- [Adjust Build Options (Optional)](#adjust-build-options-optional)
|
| 92 |
+
- [Docker Image](#docker-image)
|
| 93 |
+
- [Using pre-built images](#using-pre-built-images)
|
| 94 |
+
- [Building the image yourself](#building-the-image-yourself)
|
| 95 |
+
- [Building the Documentation](#building-the-documentation)
|
| 96 |
+
- [Previous Versions](#previous-versions)
|
| 97 |
+
- [Getting Started](#getting-started)
|
| 98 |
+
- [Resources](#resources)
|
| 99 |
+
- [Communication](#communication)
|
| 100 |
+
- [Releases and Contributing](#releases-and-contributing)
|
| 101 |
+
- [The Team](#the-team)
|
| 102 |
+
- [License](#license)
|
| 103 |
+
|
| 104 |
+
<!-- tocstop -->
|
| 105 |
+
|
| 106 |
+
## More About PyTorch
|
| 107 |
+
|
| 108 |
+
[Learn the basics of PyTorch](https://pytorch.org/tutorials/beginner/basics/intro.html)
|
| 109 |
+
|
| 110 |
+
At a granular level, PyTorch is a library that consists of the following components:
|
| 111 |
+
|
| 112 |
+
| Component | Description |
|
| 113 |
+
| ---- | --- |
|
| 114 |
+
| [**torch**](https://pytorch.org/docs/stable/torch.html) | A Tensor library like NumPy, with strong GPU support |
|
| 115 |
+
| [**torch.autograd**](https://pytorch.org/docs/stable/autograd.html) | A tape-based automatic differentiation library that supports all differentiable Tensor operations in torch |
|
| 116 |
+
| [**torch.jit**](https://pytorch.org/docs/stable/jit.html) | A compilation stack (TorchScript) to create serializable and optimizable models from PyTorch code |
|
| 117 |
+
| [**torch.nn**](https://pytorch.org/docs/stable/nn.html) | A neural networks library deeply integrated with autograd designed for maximum flexibility |
|
| 118 |
+
| [**torch.multiprocessing**](https://pytorch.org/docs/stable/multiprocessing.html) | Python multiprocessing, but with magical memory sharing of torch Tensors across processes. Useful for data loading and Hogwild training |
|
| 119 |
+
| [**torch.utils**](https://pytorch.org/docs/stable/data.html) | DataLoader and other utility functions for convenience |
|
| 120 |
+
|
| 121 |
+
Usually, PyTorch is used either as:
|
| 122 |
+
|
| 123 |
+
- A replacement for NumPy to use the power of GPUs.
|
| 124 |
+
- A deep learning research platform that provides maximum flexibility and speed.
|
| 125 |
+
|
| 126 |
+
Elaborating Further:
|
| 127 |
+
|
| 128 |
+
### A GPU-Ready Tensor Library
|
| 129 |
+
|
| 130 |
+
If you use NumPy, then you have used Tensors (a.k.a. ndarray).
|
| 131 |
+
|
| 132 |
+

|
| 133 |
+
|
| 134 |
+
PyTorch provides Tensors that can live either on the CPU or the GPU and accelerates the
|
| 135 |
+
computation by a huge amount.
|
| 136 |
+
|
| 137 |
+
We provide a wide variety of tensor routines to accelerate and fit your scientific computation needs
|
| 138 |
+
such as slicing, indexing, mathematical operations, linear algebra, reductions.
|
| 139 |
+
And they are fast!
|
| 140 |
+
|
| 141 |
+
### Dynamic Neural Networks: Tape-Based Autograd
|
| 142 |
+
|
| 143 |
+
PyTorch has a unique way of building neural networks: using and replaying a tape recorder.
|
| 144 |
+
|
| 145 |
+
Most frameworks such as TensorFlow, Theano, Caffe, and CNTK have a static view of the world.
|
| 146 |
+
One has to build a neural network and reuse the same structure again and again.
|
| 147 |
+
Changing the way the network behaves means that one has to start from scratch.
|
| 148 |
+
|
| 149 |
+
With PyTorch, we use a technique called reverse-mode auto-differentiation, which allows you to
|
| 150 |
+
change the way your network behaves arbitrarily with zero lag or overhead. Our inspiration comes
|
| 151 |
+
from several research papers on this topic, as well as current and past work such as
|
| 152 |
+
[torch-autograd](https://github.com/twitter/torch-autograd),
|
| 153 |
+
[autograd](https://github.com/HIPS/autograd),
|
| 154 |
+
[Chainer](https://chainer.org), etc.
|
| 155 |
+
|
| 156 |
+
While this technique is not unique to PyTorch, it's one of the fastest implementations of it to date.
|
| 157 |
+
You get the best of speed and flexibility for your crazy research.
|
| 158 |
+
|
| 159 |
+

|
| 160 |
+
|
| 161 |
+
### Python First
|
| 162 |
+
|
| 163 |
+
PyTorch is not a Python binding into a monolithic C++ framework.
|
| 164 |
+
It is built to be deeply integrated into Python.
|
| 165 |
+
You can use it naturally like you would use [NumPy](https://www.numpy.org/) / [SciPy](https://www.scipy.org/) / [scikit-learn](https://scikit-learn.org) etc.
|
| 166 |
+
You can write your new neural network layers in Python itself, using your favorite libraries
|
| 167 |
+
and use packages such as [Cython](https://cython.org/) and [Numba](http://numba.pydata.org/).
|
| 168 |
+
Our goal is to not reinvent the wheel where appropriate.
|
| 169 |
+
|
| 170 |
+
### Imperative Experiences
|
| 171 |
+
|
| 172 |
+
PyTorch is designed to be intuitive, linear in thought, and easy to use.
|
| 173 |
+
When you execute a line of code, it gets executed. There isn't an asynchronous view of the world.
|
| 174 |
+
When you drop into a debugger or receive error messages and stack traces, understanding them is straightforward.
|
| 175 |
+
The stack trace points to exactly where your code was defined.
|
| 176 |
+
We hope you never spend hours debugging your code because of bad stack traces or asynchronous and opaque execution engines.
|
| 177 |
+
|
| 178 |
+
### Fast and Lean
|
| 179 |
+
|
| 180 |
+
PyTorch has minimal framework overhead. We integrate acceleration libraries
|
| 181 |
+
such as [Intel MKL](https://software.intel.com/mkl) and NVIDIA ([cuDNN](https://developer.nvidia.com/cudnn), [NCCL](https://developer.nvidia.com/nccl)) to maximize speed.
|
| 182 |
+
At the core, its CPU and GPU Tensor and neural network backends
|
| 183 |
+
are mature and have been tested for years.
|
| 184 |
+
|
| 185 |
+
Hence, PyTorch is quite fast — whether you run small or large neural networks.
|
| 186 |
+
|
| 187 |
+
The memory usage in PyTorch is extremely efficient compared to Torch or some of the alternatives.
|
| 188 |
+
We've written custom memory allocators for the GPU to make sure that
|
| 189 |
+
your deep learning models are maximally memory efficient.
|
| 190 |
+
This enables you to train bigger deep learning models than before.
|
| 191 |
+
|
| 192 |
+
### Extensions Without Pain
|
| 193 |
+
|
| 194 |
+
Writing new neural network modules, or interfacing with PyTorch's Tensor API was designed to be straightforward
|
| 195 |
+
and with minimal abstractions.
|
| 196 |
+
|
| 197 |
+
You can write new neural network layers in Python using the torch API
|
| 198 |
+
[or your favorite NumPy-based libraries such as SciPy](https://pytorch.org/tutorials/advanced/numpy_extensions_tutorial.html).
|
| 199 |
+
|
| 200 |
+
If you want to write your layers in C/C++, we provide a convenient extension API that is efficient and with minimal boilerplate.
|
| 201 |
+
No wrapper code needs to be written. You can see [a tutorial here](https://pytorch.org/tutorials/advanced/cpp_extension.html) and [an example here](https://github.com/pytorch/extension-cpp).
|
| 202 |
+
|
| 203 |
+
|
| 204 |
+
## Installation
|
| 205 |
+
|
| 206 |
+
### Binaries
|
| 207 |
+
Commands to install binaries via Conda or pip wheels are on our website: [https://pytorch.org/get-started/locally/](https://pytorch.org/get-started/locally/)
|
| 208 |
+
|
| 209 |
+
|
| 210 |
+
#### NVIDIA Jetson Platforms
|
| 211 |
+
|
| 212 |
+
Python wheels for NVIDIA's Jetson Nano, Jetson TX1/TX2, Jetson Xavier NX/AGX, and Jetson AGX Orin are provided [here](https://forums.developer.nvidia.com/t/pytorch-for-jetson-version-1-10-now-available/72048) and the L4T container is published [here](https://catalog.ngc.nvidia.com/orgs/nvidia/containers/l4t-pytorch)
|
| 213 |
+
|
| 214 |
+
They require JetPack 4.2 and above, and [@dusty-nv](https://github.com/dusty-nv) and [@ptrblck](https://github.com/ptrblck) are maintaining them.
|
| 215 |
+
|
| 216 |
+
|
| 217 |
+
### From Source
|
| 218 |
+
|
| 219 |
+
#### Prerequisites
|
| 220 |
+
If you are installing from source, you will need:
|
| 221 |
+
- Python 3.9 or later
|
| 222 |
+
- A compiler that fully supports C++17, such as clang or gcc (gcc 9.4.0 or newer is required, on Linux)
|
| 223 |
+
- Visual Studio or Visual Studio Build Tool (Windows only)
|
| 224 |
+
|
| 225 |
+
\* PyTorch CI uses Visual C++ BuildTools, which come with Visual Studio Enterprise,
|
| 226 |
+
Professional, or Community Editions. You can also install the build tools from
|
| 227 |
+
https://visualstudio.microsoft.com/visual-cpp-build-tools/. The build tools *do not*
|
| 228 |
+
come with Visual Studio Code by default.
|
| 229 |
+
|
| 230 |
+
\* We highly recommend installing an [Anaconda](https://www.anaconda.com/download) environment. You will get a high-quality BLAS library (MKL) and you get controlled dependency versions regardless of your Linux distro.
|
| 231 |
+
|
| 232 |
+
An example of environment setup is shown below:
|
| 233 |
+
|
| 234 |
+
* Linux:
|
| 235 |
+
|
| 236 |
+
```bash
|
| 237 |
+
$ source <CONDA_INSTALL_DIR>/bin/activate
|
| 238 |
+
$ conda create -y -n <CONDA_NAME>
|
| 239 |
+
$ conda activate <CONDA_NAME>
|
| 240 |
+
```
|
| 241 |
+
|
| 242 |
+
* Windows:
|
| 243 |
+
|
| 244 |
+
```bash
|
| 245 |
+
$ source <CONDA_INSTALL_DIR>\Scripts\activate.bat
|
| 246 |
+
$ conda create -y -n <CONDA_NAME>
|
| 247 |
+
$ conda activate <CONDA_NAME>
|
| 248 |
+
$ call "C:\Program Files\Microsoft Visual Studio\<VERSION>\Community\VC\Auxiliary\Build\vcvarsall.bat" x64
|
| 249 |
+
```
|
| 250 |
+
|
| 251 |
+
##### NVIDIA CUDA Support
|
| 252 |
+
If you want to compile with CUDA support, [select a supported version of CUDA from our support matrix](https://pytorch.org/get-started/locally/), then install the following:
|
| 253 |
+
- [NVIDIA CUDA](https://developer.nvidia.com/cuda-downloads)
|
| 254 |
+
- [NVIDIA cuDNN](https://developer.nvidia.com/cudnn) v8.5 or above
|
| 255 |
+
- [Compiler](https://gist.github.com/ax3l/9489132) compatible with CUDA
|
| 256 |
+
|
| 257 |
+
Note: You could refer to the [cuDNN Support Matrix](https://docs.nvidia.com/deeplearning/cudnn/reference/support-matrix.html) for cuDNN versions with the various supported CUDA, CUDA driver and NVIDIA hardware
|
| 258 |
+
|
| 259 |
+
If you want to disable CUDA support, export the environment variable `USE_CUDA=0`.
|
| 260 |
+
Other potentially useful environment variables may be found in `setup.py`.
|
| 261 |
+
|
| 262 |
+
If you are building for NVIDIA's Jetson platforms (Jetson Nano, TX1, TX2, AGX Xavier), Instructions to install PyTorch for Jetson Nano are [available here](https://devtalk.nvidia.com/default/topic/1049071/jetson-nano/pytorch-for-jetson-nano/)
|
| 263 |
+
|
| 264 |
+
##### AMD ROCm Support
|
| 265 |
+
If you want to compile with ROCm support, install
|
| 266 |
+
- [AMD ROCm](https://rocm.docs.amd.com/en/latest/deploy/linux/quick_start.html) 4.0 and above installation
|
| 267 |
+
- ROCm is currently supported only for Linux systems.
|
| 268 |
+
|
| 269 |
+
By default the build system expects ROCm to be installed in `/opt/rocm`. If ROCm is installed in a different directory, the `ROCM_PATH` environment variable must be set to the ROCm installation directory. The build system automatically detects the AMD GPU architecture. Optionally, the AMD GPU architecture can be explicitly set with the `PYTORCH_ROCM_ARCH` environment variable [AMD GPU architecture](https://rocm.docs.amd.com/projects/install-on-linux/en/latest/reference/system-requirements.html#supported-gpus)
|
| 270 |
+
|
| 271 |
+
If you want to disable ROCm support, export the environment variable `USE_ROCM=0`.
|
| 272 |
+
Other potentially useful environment variables may be found in `setup.py`.
|
| 273 |
+
|
| 274 |
+
##### Intel GPU Support
|
| 275 |
+
If you want to compile with Intel GPU support, follow these
|
| 276 |
+
- [PyTorch Prerequisites for Intel GPUs](https://www.intel.com/content/www/us/en/developer/articles/tool/pytorch-prerequisites-for-intel-gpus.html) instructions.
|
| 277 |
+
- Intel GPU is supported for Linux and Windows.
|
| 278 |
+
|
| 279 |
+
If you want to disable Intel GPU support, export the environment variable `USE_XPU=0`.
|
| 280 |
+
Other potentially useful environment variables may be found in `setup.py`.
|
| 281 |
+
|
| 282 |
+
#### Get the PyTorch Source
|
| 283 |
+
```bash
|
| 284 |
+
git clone --recursive https://github.com/pytorch/pytorch
|
| 285 |
+
cd pytorch
|
| 286 |
+
# if you are updating an existing checkout
|
| 287 |
+
git submodule sync
|
| 288 |
+
git submodule update --init --recursive
|
| 289 |
+
```
|
| 290 |
+
|
| 291 |
+
#### Install Dependencies
|
| 292 |
+
|
| 293 |
+
**Common**
|
| 294 |
+
|
| 295 |
+
```bash
|
| 296 |
+
conda install cmake ninja
|
| 297 |
+
# Run this command from the PyTorch directory after cloning the source code using the “Get the PyTorch Source“ section below
|
| 298 |
+
pip install -r requirements.txt
|
| 299 |
+
```
|
| 300 |
+
|
| 301 |
+
**On Linux**
|
| 302 |
+
|
| 303 |
+
```bash
|
| 304 |
+
pip install mkl-static mkl-include
|
| 305 |
+
# CUDA only: Add LAPACK support for the GPU if needed
|
| 306 |
+
conda install -c pytorch magma-cuda121 # or the magma-cuda* that matches your CUDA version from https://anaconda.org/pytorch/repo
|
| 307 |
+
|
| 308 |
+
# (optional) If using torch.compile with inductor/triton, install the matching version of triton
|
| 309 |
+
# Run from the pytorch directory after cloning
|
| 310 |
+
# For Intel GPU support, please explicitly `export USE_XPU=1` before running command.
|
| 311 |
+
make triton
|
| 312 |
+
```
|
| 313 |
+
|
| 314 |
+
**On MacOS**
|
| 315 |
+
|
| 316 |
+
```bash
|
| 317 |
+
# Add this package on intel x86 processor machines only
|
| 318 |
+
pip install mkl-static mkl-include
|
| 319 |
+
# Add these packages if torch.distributed is needed
|
| 320 |
+
conda install pkg-config libuv
|
| 321 |
+
```
|
| 322 |
+
|
| 323 |
+
**On Windows**
|
| 324 |
+
|
| 325 |
+
```bash
|
| 326 |
+
pip install mkl-static mkl-include
|
| 327 |
+
# Add these packages if torch.distributed is needed.
|
| 328 |
+
# Distributed package support on Windows is a prototype feature and is subject to changes.
|
| 329 |
+
conda install -c conda-forge libuv=1.39
|
| 330 |
+
```
|
| 331 |
+
|
| 332 |
+
#### Install PyTorch
|
| 333 |
+
**On Linux**
|
| 334 |
+
|
| 335 |
+
If you would like to compile PyTorch with [new C++ ABI](https://gcc.gnu.org/onlinedocs/libstdc++/manual/using_dual_abi.html) enabled, then first run this command:
|
| 336 |
+
```bash
|
| 337 |
+
export _GLIBCXX_USE_CXX11_ABI=1
|
| 338 |
+
```
|
| 339 |
+
|
| 340 |
+
Please **note** that starting from PyTorch 2.5, the PyTorch build with XPU supports both new and old C++ ABIs. Previously, XPU only supported the new C++ ABI. If you want to compile with Intel GPU support, please follow [Intel GPU Support](#intel-gpu-support).
|
| 341 |
+
|
| 342 |
+
If you're compiling for AMD ROCm then first run this command:
|
| 343 |
+
```bash
|
| 344 |
+
# Only run this if you're compiling for ROCm
|
| 345 |
+
python tools/amd_build/build_amd.py
|
| 346 |
+
```
|
| 347 |
+
|
| 348 |
+
Install PyTorch
|
| 349 |
+
```bash
|
| 350 |
+
export CMAKE_PREFIX_PATH="${CONDA_PREFIX:-'$(dirname $(which conda))/../'}:${CMAKE_PREFIX_PATH}"
|
| 351 |
+
python setup.py develop
|
| 352 |
+
```
|
| 353 |
+
|
| 354 |
+
**On macOS**
|
| 355 |
+
|
| 356 |
+
```bash
|
| 357 |
+
python3 setup.py develop
|
| 358 |
+
```
|
| 359 |
+
|
| 360 |
+
**On Windows**
|
| 361 |
+
|
| 362 |
+
If you want to build legacy python code, please refer to [Building on legacy code and CUDA](https://github.com/pytorch/pytorch/blob/main/CONTRIBUTING.md#building-on-legacy-code-and-cuda)
|
| 363 |
+
|
| 364 |
+
**CPU-only builds**
|
| 365 |
+
|
| 366 |
+
In this mode PyTorch computations will run on your CPU, not your GPU
|
| 367 |
+
|
| 368 |
+
```cmd
|
| 369 |
+
python setup.py develop
|
| 370 |
+
```
|
| 371 |
+
|
| 372 |
+
Note on OpenMP: The desired OpenMP implementation is Intel OpenMP (iomp). In order to link against iomp, you'll need to manually download the library and set up the building environment by tweaking `CMAKE_INCLUDE_PATH` and `LIB`. The instruction [here](https://github.com/pytorch/pytorch/blob/main/docs/source/notes/windows.rst#building-from-source) is an example for setting up both MKL and Intel OpenMP. Without these configurations for CMake, Microsoft Visual C OpenMP runtime (vcomp) will be used.
|
| 373 |
+
|
| 374 |
+
**CUDA based build**
|
| 375 |
+
|
| 376 |
+
In this mode PyTorch computations will leverage your GPU via CUDA for faster number crunching
|
| 377 |
+
|
| 378 |
+
[NVTX](https://docs.nvidia.com/gameworks/content/gameworkslibrary/nvtx/nvidia_tools_extension_library_nvtx.htm) is needed to build Pytorch with CUDA.
|
| 379 |
+
NVTX is a part of CUDA distributive, where it is called "Nsight Compute". To install it onto an already installed CUDA run CUDA installation once again and check the corresponding checkbox.
|
| 380 |
+
Make sure that CUDA with Nsight Compute is installed after Visual Studio.
|
| 381 |
+
|
| 382 |
+
Currently, VS 2017 / 2019, and Ninja are supported as the generator of CMake. If `ninja.exe` is detected in `PATH`, then Ninja will be used as the default generator, otherwise, it will use VS 2017 / 2019.
|
| 383 |
+
<br/> If Ninja is selected as the generator, the latest MSVC will get selected as the underlying toolchain.
|
| 384 |
+
|
| 385 |
+
Additional libraries such as
|
| 386 |
+
[Magma](https://developer.nvidia.com/magma), [oneDNN, a.k.a. MKLDNN or DNNL](https://github.com/oneapi-src/oneDNN), and [Sccache](https://github.com/mozilla/sccache) are often needed. Please refer to the [installation-helper](https://github.com/pytorch/pytorch/tree/main/.ci/pytorch/win-test-helpers/installation-helpers) to install them.
|
| 387 |
+
|
| 388 |
+
You can refer to the [build_pytorch.bat](https://github.com/pytorch/pytorch/blob/main/.ci/pytorch/win-test-helpers/build_pytorch.bat) script for some other environment variables configurations
|
| 389 |
+
|
| 390 |
+
|
| 391 |
+
```cmd
|
| 392 |
+
cmd
|
| 393 |
+
|
| 394 |
+
:: Set the environment variables after you have downloaded and unzipped the mkl package,
|
| 395 |
+
:: else CMake would throw an error as `Could NOT find OpenMP`.
|
| 396 |
+
set CMAKE_INCLUDE_PATH={Your directory}\mkl\include
|
| 397 |
+
set LIB={Your directory}\mkl\lib;%LIB%
|
| 398 |
+
|
| 399 |
+
:: Read the content in the previous section carefully before you proceed.
|
| 400 |
+
:: [Optional] If you want to override the underlying toolset used by Ninja and Visual Studio with CUDA, please run the following script block.
|
| 401 |
+
:: "Visual Studio 2019 Developer Command Prompt" will be run automatically.
|
| 402 |
+
:: Make sure you have CMake >= 3.12 before you do this when you use the Visual Studio generator.
|
| 403 |
+
set CMAKE_GENERATOR_TOOLSET_VERSION=14.27
|
| 404 |
+
set DISTUTILS_USE_SDK=1
|
| 405 |
+
for /f "usebackq tokens=*" %i in (`"%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" -version [15^,17^) -products * -latest -property installationPath`) do call "%i\VC\Auxiliary\Build\vcvarsall.bat" x64 -vcvars_ver=%CMAKE_GENERATOR_TOOLSET_VERSION%
|
| 406 |
+
|
| 407 |
+
:: [Optional] If you want to override the CUDA host compiler
|
| 408 |
+
set CUDAHOSTCXX=C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.27.29110\bin\HostX64\x64\cl.exe
|
| 409 |
+
|
| 410 |
+
python setup.py develop
|
| 411 |
+
|
| 412 |
+
```
|
| 413 |
+
|
| 414 |
+
##### Adjust Build Options (Optional)
|
| 415 |
+
|
| 416 |
+
You can adjust the configuration of cmake variables optionally (without building first), by doing
|
| 417 |
+
the following. For example, adjusting the pre-detected directories for CuDNN or BLAS can be done
|
| 418 |
+
with such a step.
|
| 419 |
+
|
| 420 |
+
On Linux
|
| 421 |
+
```bash
|
| 422 |
+
export CMAKE_PREFIX_PATH="${CONDA_PREFIX:-'$(dirname $(which conda))/../'}:${CMAKE_PREFIX_PATH}"
|
| 423 |
+
python setup.py build --cmake-only
|
| 424 |
+
ccmake build # or cmake-gui build
|
| 425 |
+
```
|
| 426 |
+
|
| 427 |
+
On macOS
|
| 428 |
+
```bash
|
| 429 |
+
export CMAKE_PREFIX_PATH="${CONDA_PREFIX:-'$(dirname $(which conda))/../'}:${CMAKE_PREFIX_PATH}"
|
| 430 |
+
MACOSX_DEPLOYMENT_TARGET=10.9 CC=clang CXX=clang++ python setup.py build --cmake-only
|
| 431 |
+
ccmake build # or cmake-gui build
|
| 432 |
+
```
|
| 433 |
+
|
| 434 |
+
### Docker Image
|
| 435 |
+
|
| 436 |
+
#### Using pre-built images
|
| 437 |
+
|
| 438 |
+
You can also pull a pre-built docker image from Docker Hub and run with docker v19.03+
|
| 439 |
+
|
| 440 |
+
```bash
|
| 441 |
+
docker run --gpus all --rm -ti --ipc=host pytorch/pytorch:latest
|
| 442 |
+
```
|
| 443 |
+
|
| 444 |
+
Please note that PyTorch uses shared memory to share data between processes, so if torch multiprocessing is used (e.g.
|
| 445 |
+
for multithreaded data loaders) the default shared memory segment size that container runs with is not enough, and you
|
| 446 |
+
should increase shared memory size either with `--ipc=host` or `--shm-size` command line options to `nvidia-docker run`.
|
| 447 |
+
|
| 448 |
+
#### Building the image yourself
|
| 449 |
+
|
| 450 |
+
**NOTE:** Must be built with a docker version > 18.06
|
| 451 |
+
|
| 452 |
+
The `Dockerfile` is supplied to build images with CUDA 11.1 support and cuDNN v8.
|
| 453 |
+
You can pass `PYTHON_VERSION=x.y` make variable to specify which Python version is to be used by Miniconda, or leave it
|
| 454 |
+
unset to use the default.
|
| 455 |
+
|
| 456 |
+
```bash
|
| 457 |
+
make -f docker.Makefile
|
| 458 |
+
# images are tagged as docker.io/${your_docker_username}/pytorch
|
| 459 |
+
```
|
| 460 |
+
|
| 461 |
+
You can also pass the `CMAKE_VARS="..."` environment variable to specify additional CMake variables to be passed to CMake during the build.
|
| 462 |
+
See [setup.py](./setup.py) for the list of available variables.
|
| 463 |
+
|
| 464 |
+
```bash
|
| 465 |
+
make -f docker.Makefile
|
| 466 |
+
```
|
| 467 |
+
|
| 468 |
+
### Building the Documentation
|
| 469 |
+
|
| 470 |
+
To build documentation in various formats, you will need [Sphinx](http://www.sphinx-doc.org) and the
|
| 471 |
+
readthedocs theme.
|
| 472 |
+
|
| 473 |
+
```bash
|
| 474 |
+
cd docs/
|
| 475 |
+
pip install -r requirements.txt
|
| 476 |
+
make html
|
| 477 |
+
make serve
|
| 478 |
+
```
|
| 479 |
+
|
| 480 |
+
Run `make` to get a list of all available output formats.
|
| 481 |
+
|
| 482 |
+
If you get a katex error run `npm install katex`. If it persists, try
|
| 483 |
+
`npm install -g katex`
|
| 484 |
+
|
| 485 |
+
> Note: if you installed `nodejs` with a different package manager (e.g.,
|
| 486 |
+
`conda`) then `npm` will probably install a version of `katex` that is not
|
| 487 |
+
compatible with your version of `nodejs` and doc builds will fail.
|
| 488 |
+
A combination of versions that is known to work is `[email protected]` and
|
| 489 |
+
`[email protected]`. To install the latter with `npm` you can run
|
| 490 |
+
```npm install -g [email protected]```
|
| 491 |
+
|
| 492 |
+
### Previous Versions
|
| 493 |
+
|
| 494 |
+
Installation instructions and binaries for previous PyTorch versions may be found
|
| 495 |
+
on [our website](https://pytorch.org/previous-versions).
|
| 496 |
+
|
| 497 |
+
|
| 498 |
+
## Getting Started
|
| 499 |
+
|
| 500 |
+
Three-pointers to get you started:
|
| 501 |
+
- [Tutorials: get you started with understanding and using PyTorch](https://pytorch.org/tutorials/)
|
| 502 |
+
- [Examples: easy to understand PyTorch code across all domains](https://github.com/pytorch/examples)
|
| 503 |
+
- [The API Reference](https://pytorch.org/docs/)
|
| 504 |
+
- [Glossary](https://github.com/pytorch/pytorch/blob/main/GLOSSARY.md)
|
| 505 |
+
|
| 506 |
+
## Resources
|
| 507 |
+
|
| 508 |
+
* [PyTorch.org](https://pytorch.org/)
|
| 509 |
+
* [PyTorch Tutorials](https://pytorch.org/tutorials/)
|
| 510 |
+
* [PyTorch Examples](https://github.com/pytorch/examples)
|
| 511 |
+
* [PyTorch Models](https://pytorch.org/hub/)
|
| 512 |
+
* [Intro to Deep Learning with PyTorch from Udacity](https://www.udacity.com/course/deep-learning-pytorch--ud188)
|
| 513 |
+
* [Intro to Machine Learning with PyTorch from Udacity](https://www.udacity.com/course/intro-to-machine-learning-nanodegree--nd229)
|
| 514 |
+
* [Deep Neural Networks with PyTorch from Coursera](https://www.coursera.org/learn/deep-neural-networks-with-pytorch)
|
| 515 |
+
* [PyTorch Twitter](https://twitter.com/PyTorch)
|
| 516 |
+
* [PyTorch Blog](https://pytorch.org/blog/)
|
| 517 |
+
* [PyTorch YouTube](https://www.youtube.com/channel/UCWXI5YeOsh03QvJ59PMaXFw)
|
| 518 |
+
|
| 519 |
+
## Communication
|
| 520 |
+
* Forums: Discuss implementations, research, etc. https://discuss.pytorch.org
|
| 521 |
+
* GitHub Issues: Bug reports, feature requests, install issues, RFCs, thoughts, etc.
|
| 522 |
+
* Slack: The [PyTorch Slack](https://pytorch.slack.com/) hosts a primary audience of moderate to experienced PyTorch users and developers for general chat, online discussions, collaboration, etc. If you are a beginner looking for help, the primary medium is [PyTorch Forums](https://discuss.pytorch.org). If you need a slack invite, please fill this form: https://goo.gl/forms/PP1AGvNHpSaJP8to1
|
| 523 |
+
* Newsletter: No-noise, a one-way email newsletter with important announcements about PyTorch. You can sign-up here: https://eepurl.com/cbG0rv
|
| 524 |
+
* Facebook Page: Important announcements about PyTorch. https://www.facebook.com/pytorch
|
| 525 |
+
* For brand guidelines, please visit our website at [pytorch.org](https://pytorch.org/)
|
| 526 |
+
|
| 527 |
+
## Releases and Contributing
|
| 528 |
+
|
| 529 |
+
Typically, PyTorch has three minor releases a year. Please let us know if you encounter a bug by [filing an issue](https://github.com/pytorch/pytorch/issues).
|
| 530 |
+
|
| 531 |
+
We appreciate all contributions. If you are planning to contribute back bug-fixes, please do so without any further discussion.
|
| 532 |
+
|
| 533 |
+
If you plan to contribute new features, utility functions, or extensions to the core, please first open an issue and discuss the feature with us.
|
| 534 |
+
Sending a PR without discussion might end up resulting in a rejected PR because we might be taking the core in a different direction than you might be aware of.
|
| 535 |
+
|
| 536 |
+
To learn more about making a contribution to Pytorch, please see our [Contribution page](CONTRIBUTING.md). For more information about PyTorch releases, see [Release page](RELEASE.md).
|
| 537 |
+
|
| 538 |
+
## The Team
|
| 539 |
+
|
| 540 |
+
PyTorch is a community-driven project with several skillful engineers and researchers contributing to it.
|
| 541 |
+
|
| 542 |
+
PyTorch is currently maintained by [Soumith Chintala](http://soumith.ch), [Gregory Chanan](https://github.com/gchanan), [Dmytro Dzhulgakov](https://github.com/dzhulgakov), [Edward Yang](https://github.com/ezyang), and [Nikita Shulga](https://github.com/malfet) with major contributions coming from hundreds of talented individuals in various forms and means.
|
| 543 |
+
A non-exhaustive but growing list needs to mention: [Trevor Killeen](https://github.com/killeent), [Sasank Chilamkurthy](https://github.com/chsasank), [Sergey Zagoruyko](https://github.com/szagoruyko), [Adam Lerer](https://github.com/adamlerer), [Francisco Massa](https://github.com/fmassa), [Alykhan Tejani](https://github.com/alykhantejani), [Luca Antiga](https://github.com/lantiga), [Alban Desmaison](https://github.com/albanD), [Andreas Koepf](https://github.com/andreaskoepf), [James Bradbury](https://github.com/jamesb93), [Zeming Lin](https://github.com/ebetica), [Yuandong Tian](https://github.com/yuandong-tian), [Guillaume Lample](https://github.com/glample), [Marat Dukhan](https://github.com/Maratyszcza), [Natalia Gimelshein](https://github.com/ngimel), [Christian Sarofeen](https://github.com/csarofeen), [Martin Raison](https://github.com/martinraison), [Edward Yang](https://github.com/ezyang), [Zachary Devito](https://github.com/zdevito).
|
| 544 |
+
|
| 545 |
+
Note: This project is unrelated to [hughperkins/pytorch](https://github.com/hughperkins/pytorch) with the same name. Hugh is a valuable contributor to the Torch community and has helped with many things Torch and PyTorch.
|
| 546 |
+
|
| 547 |
+
## License
|
| 548 |
+
|
| 549 |
+
PyTorch has a BSD-style license, as found in the [LICENSE](LICENSE) file.
|
NOTICE
ADDED
|
@@ -0,0 +1,456 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
=======================================================================
|
| 2 |
+
Software under third_party
|
| 3 |
+
=======================================================================
|
| 4 |
+
Software libraries under third_party are provided as github submodule
|
| 5 |
+
links, and their content is not part of the Caffe2 codebase. Their
|
| 6 |
+
licences can be found under the respective software repositories.
|
| 7 |
+
|
| 8 |
+
=======================================================================
|
| 9 |
+
Earlier BSD License
|
| 10 |
+
=======================================================================
|
| 11 |
+
Early development of Caffe2 in 2015 and early 2016 is licensed under the
|
| 12 |
+
BSD license. The license is attached below:
|
| 13 |
+
|
| 14 |
+
All contributions by Facebook:
|
| 15 |
+
Copyright (c) 2016 Facebook Inc.
|
| 16 |
+
|
| 17 |
+
All contributions by Google:
|
| 18 |
+
Copyright (c) 2015 Google Inc.
|
| 19 |
+
All rights reserved.
|
| 20 |
+
|
| 21 |
+
All contributions by Yangqing Jia:
|
| 22 |
+
Copyright (c) 2015 Yangqing Jia
|
| 23 |
+
All rights reserved.
|
| 24 |
+
|
| 25 |
+
All contributions by Kakao Brain:
|
| 26 |
+
Copyright 2019-2020 Kakao Brain
|
| 27 |
+
|
| 28 |
+
All other contributions:
|
| 29 |
+
Copyright(c) 2015, 2016 the respective contributors
|
| 30 |
+
All rights reserved.
|
| 31 |
+
|
| 32 |
+
Redistribution and use in source and binary forms, with or without
|
| 33 |
+
modification, are permitted provided that the following conditions are met:
|
| 34 |
+
|
| 35 |
+
1. Redistributions of source code must retain the above copyright notice, this
|
| 36 |
+
list of conditions and the following disclaimer.
|
| 37 |
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
| 38 |
+
this list of conditions and the following disclaimer in the documentation
|
| 39 |
+
and/or other materials provided with the distribution.
|
| 40 |
+
|
| 41 |
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
| 42 |
+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
| 43 |
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
| 44 |
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
| 45 |
+
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
| 46 |
+
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
| 47 |
+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
| 48 |
+
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
| 49 |
+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
| 50 |
+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
| 51 |
+
|
| 52 |
+
|
| 53 |
+
=======================================================================
|
| 54 |
+
Caffe's BSD License
|
| 55 |
+
=======================================================================
|
| 56 |
+
Some parts of the caffe2 code is derived from the original Caffe code, which is
|
| 57 |
+
created by Yangqing Jia and is now a BSD-licensed open-source project. The Caffe
|
| 58 |
+
license is as follows:
|
| 59 |
+
|
| 60 |
+
COPYRIGHT
|
| 61 |
+
|
| 62 |
+
All contributions by the University of California:
|
| 63 |
+
Copyright (c) 2014, The Regents of the University of California (Regents)
|
| 64 |
+
All rights reserved.
|
| 65 |
+
|
| 66 |
+
All other contributions:
|
| 67 |
+
Copyright (c) 2014, the respective contributors
|
| 68 |
+
All rights reserved.
|
| 69 |
+
|
| 70 |
+
Caffe uses a shared copyright model: each contributor holds copyright over
|
| 71 |
+
their contributions to Caffe. The project versioning records all such
|
| 72 |
+
contribution and copyright details. If a contributor wants to further mark
|
| 73 |
+
their specific copyright on a particular contribution, they should indicate
|
| 74 |
+
their copyright solely in the commit message of the change when it is
|
| 75 |
+
committed.
|
| 76 |
+
|
| 77 |
+
LICENSE
|
| 78 |
+
|
| 79 |
+
Redistribution and use in source and binary forms, with or without
|
| 80 |
+
modification, are permitted provided that the following conditions are met:
|
| 81 |
+
|
| 82 |
+
1. Redistributions of source code must retain the above copyright notice, this
|
| 83 |
+
list of conditions and the following disclaimer.
|
| 84 |
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
| 85 |
+
this list of conditions and the following disclaimer in the documentation
|
| 86 |
+
and/or other materials provided with the distribution.
|
| 87 |
+
|
| 88 |
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
| 89 |
+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
| 90 |
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
| 91 |
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
| 92 |
+
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
| 93 |
+
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
| 94 |
+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
| 95 |
+
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
| 96 |
+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
| 97 |
+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
| 98 |
+
|
| 99 |
+
CONTRIBUTION AGREEMENT
|
| 100 |
+
|
| 101 |
+
By contributing to the BVLC/caffe repository through pull-request, comment,
|
| 102 |
+
or otherwise, the contributor releases their content to the
|
| 103 |
+
license and copyright terms herein.
|
| 104 |
+
|
| 105 |
+
=======================================================================
|
| 106 |
+
Caffe2's Apache License
|
| 107 |
+
=======================================================================
|
| 108 |
+
|
| 109 |
+
This repo contains Caffe2 code, which was previously licensed under
|
| 110 |
+
Apache License Version 2.0:
|
| 111 |
+
|
| 112 |
+
Apache License
|
| 113 |
+
Version 2.0, January 2004
|
| 114 |
+
http://www.apache.org/licenses/
|
| 115 |
+
|
| 116 |
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
| 117 |
+
|
| 118 |
+
1. Definitions.
|
| 119 |
+
|
| 120 |
+
"License" shall mean the terms and conditions for use, reproduction,
|
| 121 |
+
and distribution as defined by Sections 1 through 9 of this document.
|
| 122 |
+
|
| 123 |
+
"Licensor" shall mean the copyright owner or entity authorized by
|
| 124 |
+
the copyright owner that is granting the License.
|
| 125 |
+
|
| 126 |
+
"Legal Entity" shall mean the union of the acting entity and all
|
| 127 |
+
other entities that control, are controlled by, or are under common
|
| 128 |
+
control with that entity. For the purposes of this definition,
|
| 129 |
+
"control" means (i) the power, direct or indirect, to cause the
|
| 130 |
+
direction or management of such entity, whether by contract or
|
| 131 |
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
| 132 |
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
| 133 |
+
|
| 134 |
+
"You" (or "Your") shall mean an individual or Legal Entity
|
| 135 |
+
exercising permissions granted by this License.
|
| 136 |
+
|
| 137 |
+
"Source" form shall mean the preferred form for making modifications,
|
| 138 |
+
including but not limited to software source code, documentation
|
| 139 |
+
source, and configuration files.
|
| 140 |
+
|
| 141 |
+
"Object" form shall mean any form resulting from mechanical
|
| 142 |
+
transformation or translation of a Source form, including but
|
| 143 |
+
not limited to compiled object code, generated documentation,
|
| 144 |
+
and conversions to other media types.
|
| 145 |
+
|
| 146 |
+
"Work" shall mean the work of authorship, whether in Source or
|
| 147 |
+
Object form, made available under the License, as indicated by a
|
| 148 |
+
copyright notice that is included in or attached to the work
|
| 149 |
+
(an example is provided in the Appendix below).
|
| 150 |
+
|
| 151 |
+
"Derivative Works" shall mean any work, whether in Source or Object
|
| 152 |
+
form, that is based on (or derived from) the Work and for which the
|
| 153 |
+
editorial revisions, annotations, elaborations, or other modifications
|
| 154 |
+
represent, as a whole, an original work of authorship. For the purposes
|
| 155 |
+
of this License, Derivative Works shall not include works that remain
|
| 156 |
+
separable from, or merely link (or bind by name) to the interfaces of,
|
| 157 |
+
the Work and Derivative Works thereof.
|
| 158 |
+
|
| 159 |
+
"Contribution" shall mean any work of authorship, including
|
| 160 |
+
the original version of the Work and any modifications or additions
|
| 161 |
+
to that Work or Derivative Works thereof, that is intentionally
|
| 162 |
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
| 163 |
+
or by an individual or Legal Entity authorized to submit on behalf of
|
| 164 |
+
the copyright owner. For the purposes of this definition, "submitted"
|
| 165 |
+
means any form of electronic, verbal, or written communication sent
|
| 166 |
+
to the Licensor or its representatives, including but not limited to
|
| 167 |
+
communication on electronic mailing lists, source code control systems,
|
| 168 |
+
and issue tracking systems that are managed by, or on behalf of, the
|
| 169 |
+
Licensor for the purpose of discussing and improving the Work, but
|
| 170 |
+
excluding communication that is conspicuously marked or otherwise
|
| 171 |
+
designated in writing by the copyright owner as "Not a Contribution."
|
| 172 |
+
|
| 173 |
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
| 174 |
+
on behalf of whom a Contribution has been received by Licensor and
|
| 175 |
+
subsequently incorporated within the Work.
|
| 176 |
+
|
| 177 |
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
| 178 |
+
this License, each Contributor hereby grants to You a perpetual,
|
| 179 |
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
| 180 |
+
copyright license to reproduce, prepare Derivative Works of,
|
| 181 |
+
publicly display, publicly perform, sublicense, and distribute the
|
| 182 |
+
Work and such Derivative Works in Source or Object form.
|
| 183 |
+
|
| 184 |
+
3. Grant of Patent License. Subject to the terms and conditions of
|
| 185 |
+
this License, each Contributor hereby grants to You a perpetual,
|
| 186 |
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
| 187 |
+
(except as stated in this section) patent license to make, have made,
|
| 188 |
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
| 189 |
+
where such license applies only to those patent claims licensable
|
| 190 |
+
by such Contributor that are necessarily infringed by their
|
| 191 |
+
Contribution(s) alone or by combination of their Contribution(s)
|
| 192 |
+
with the Work to which such Contribution(s) was submitted. If You
|
| 193 |
+
institute patent litigation against any entity (including a
|
| 194 |
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
| 195 |
+
or a Contribution incorporated within the Work constitutes direct
|
| 196 |
+
or contributory patent infringement, then any patent licenses
|
| 197 |
+
granted to You under this License for that Work shall terminate
|
| 198 |
+
as of the date such litigation is filed.
|
| 199 |
+
|
| 200 |
+
4. Redistribution. You may reproduce and distribute copies of the
|
| 201 |
+
Work or Derivative Works thereof in any medium, with or without
|
| 202 |
+
modifications, and in Source or Object form, provided that You
|
| 203 |
+
meet the following conditions:
|
| 204 |
+
|
| 205 |
+
(a) You must give any other recipients of the Work or
|
| 206 |
+
Derivative Works a copy of this License; and
|
| 207 |
+
|
| 208 |
+
(b) You must cause any modified files to carry prominent notices
|
| 209 |
+
stating that You changed the files; and
|
| 210 |
+
|
| 211 |
+
(c) You must retain, in the Source form of any Derivative Works
|
| 212 |
+
that You distribute, all copyright, patent, trademark, and
|
| 213 |
+
attribution notices from the Source form of the Work,
|
| 214 |
+
excluding those notices that do not pertain to any part of
|
| 215 |
+
the Derivative Works; and
|
| 216 |
+
|
| 217 |
+
(d) If the Work includes a "NOTICE" text file as part of its
|
| 218 |
+
distribution, then any Derivative Works that You distribute must
|
| 219 |
+
include a readable copy of the attribution notices contained
|
| 220 |
+
within such NOTICE file, excluding those notices that do not
|
| 221 |
+
pertain to any part of the Derivative Works, in at least one
|
| 222 |
+
of the following places: within a NOTICE text file distributed
|
| 223 |
+
as part of the Derivative Works; within the Source form or
|
| 224 |
+
documentation, if provided along with the Derivative Works; or,
|
| 225 |
+
within a display generated by the Derivative Works, if and
|
| 226 |
+
wherever such third-party notices normally appear. The contents
|
| 227 |
+
of the NOTICE file are for informational purposes only and
|
| 228 |
+
do not modify the License. You may add Your own attribution
|
| 229 |
+
notices within Derivative Works that You distribute, alongside
|
| 230 |
+
or as an addendum to the NOTICE text from the Work, provided
|
| 231 |
+
that such additional attribution notices cannot be construed
|
| 232 |
+
as modifying the License.
|
| 233 |
+
|
| 234 |
+
You may add Your own copyright statement to Your modifications and
|
| 235 |
+
may provide additional or different license terms and conditions
|
| 236 |
+
for use, reproduction, or distribution of Your modifications, or
|
| 237 |
+
for any such Derivative Works as a whole, provided Your use,
|
| 238 |
+
reproduction, and distribution of the Work otherwise complies with
|
| 239 |
+
the conditions stated in this License.
|
| 240 |
+
|
| 241 |
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
| 242 |
+
any Contribution intentionally submitted for inclusion in the Work
|
| 243 |
+
by You to the Licensor shall be under the terms and conditions of
|
| 244 |
+
this License, without any additional terms or conditions.
|
| 245 |
+
Notwithstanding the above, nothing herein shall supersede or modify
|
| 246 |
+
the terms of any separate license agreement you may have executed
|
| 247 |
+
with Licensor regarding such Contributions.
|
| 248 |
+
|
| 249 |
+
6. Trademarks. This License does not grant permission to use the trade
|
| 250 |
+
names, trademarks, service marks, or product names of the Licensor,
|
| 251 |
+
except as required for reasonable and customary use in describing the
|
| 252 |
+
origin of the Work and reproducing the content of the NOTICE file.
|
| 253 |
+
|
| 254 |
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
| 255 |
+
agreed to in writing, Licensor provides the Work (and each
|
| 256 |
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
| 257 |
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
| 258 |
+
implied, including, without limitation, any warranties or conditions
|
| 259 |
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
| 260 |
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
| 261 |
+
appropriateness of using or redistributing the Work and assume any
|
| 262 |
+
risks associated with Your exercise of permissions under this License.
|
| 263 |
+
|
| 264 |
+
8. Limitation of Liability. In no event and under no legal theory,
|
| 265 |
+
whether in tort (including negligence), contract, or otherwise,
|
| 266 |
+
unless required by applicable law (such as deliberate and grossly
|
| 267 |
+
negligent acts) or agreed to in writing, shall any Contributor be
|
| 268 |
+
liable to You for damages, including any direct, indirect, special,
|
| 269 |
+
incidental, or consequential damages of any character arising as a
|
| 270 |
+
result of this License or out of the use or inability to use the
|
| 271 |
+
Work (including but not limited to damages for loss of goodwill,
|
| 272 |
+
work stoppage, computer failure or malfunction, or any and all
|
| 273 |
+
other commercial damages or losses), even if such Contributor
|
| 274 |
+
has been advised of the possibility of such damages.
|
| 275 |
+
|
| 276 |
+
9. Accepting Warranty or Additional Liability. While redistributing
|
| 277 |
+
the Work or Derivative Works thereof, You may choose to offer,
|
| 278 |
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
| 279 |
+
or other liability obligations and/or rights consistent with this
|
| 280 |
+
License. However, in accepting such obligations, You may act only
|
| 281 |
+
on Your own behalf and on Your sole responsibility, not on behalf
|
| 282 |
+
of any other Contributor, and only if You agree to indemnify,
|
| 283 |
+
defend, and hold each Contributor harmless for any liability
|
| 284 |
+
incurred by, or claims asserted against, such Contributor by reason
|
| 285 |
+
of your accepting any such warranty or additional liability.
|
| 286 |
+
|
| 287 |
+
=======================================================================
|
| 288 |
+
Cephes's 3-Clause BSD License
|
| 289 |
+
=======================================================================
|
| 290 |
+
|
| 291 |
+
Code derived from implementations in the Cephes Math Library should mention
|
| 292 |
+
its derivation and reference the following license:
|
| 293 |
+
|
| 294 |
+
3-Clause BSD License for the Cephes Math Library
|
| 295 |
+
Copyright (c) 2018, Steven Moshier
|
| 296 |
+
All rights reserved.
|
| 297 |
+
|
| 298 |
+
Redistribution and use in source and binary forms, with or without
|
| 299 |
+
modification, are permitted provided that the following conditions are met:
|
| 300 |
+
|
| 301 |
+
* Redistributions of source code must retain the above copyright
|
| 302 |
+
notice, this list of conditions and the following disclaimer.
|
| 303 |
+
|
| 304 |
+
* Redistributions in binary form must reproduce the above copyright
|
| 305 |
+
notice, this list of conditions and the following disclaimer in the
|
| 306 |
+
documentation and/or other materials provided with the distribution.
|
| 307 |
+
|
| 308 |
+
* Neither the name of the nor the
|
| 309 |
+
names of its contributors may be used to endorse or promote products
|
| 310 |
+
derived from this software without specific prior written permission.
|
| 311 |
+
|
| 312 |
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
| 313 |
+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
| 314 |
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
| 315 |
+
DISCLAIMED. IN NO EVENT SHALL Steven Moshier BE LIABLE FOR ANY
|
| 316 |
+
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
| 317 |
+
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
| 318 |
+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
| 319 |
+
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
| 320 |
+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
| 321 |
+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
| 322 |
+
|
| 323 |
+
|
| 324 |
+
=======================================================================
|
| 325 |
+
SciPy's 3-Clause BSD License
|
| 326 |
+
=======================================================================
|
| 327 |
+
|
| 328 |
+
Code derived from implementations in SciPy should mention its derivation
|
| 329 |
+
and reference the following license:
|
| 330 |
+
|
| 331 |
+
Copyright (c) 2001-2002 Enthought, Inc. 2003-2019, SciPy Developers.
|
| 332 |
+
All rights reserved.
|
| 333 |
+
|
| 334 |
+
Redistribution and use in source and binary forms, with or without
|
| 335 |
+
modification, are permitted provided that the following conditions
|
| 336 |
+
are met:
|
| 337 |
+
|
| 338 |
+
1. Redistributions of source code must retain the above copyright
|
| 339 |
+
notice, this list of conditions and the following disclaimer.
|
| 340 |
+
|
| 341 |
+
2. Redistributions in binary form must reproduce the above
|
| 342 |
+
copyright notice, this list of conditions and the following
|
| 343 |
+
disclaimer in the documentation and/or other materials provided
|
| 344 |
+
with the distribution.
|
| 345 |
+
|
| 346 |
+
3. Neither the name of the copyright holder nor the names of its
|
| 347 |
+
contributors may be used to endorse or promote products derived
|
| 348 |
+
from this software without specific prior written permission.
|
| 349 |
+
|
| 350 |
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
| 351 |
+
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
| 352 |
+
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
| 353 |
+
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
| 354 |
+
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
| 355 |
+
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
| 356 |
+
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
| 357 |
+
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
| 358 |
+
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
| 359 |
+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
| 360 |
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
| 361 |
+
|
| 362 |
+
=======================================================================
|
| 363 |
+
Boost's 1.0 Software License
|
| 364 |
+
=======================================================================
|
| 365 |
+
|
| 366 |
+
Code derived from implementations in Boost 1.0 should mention its
|
| 367 |
+
derivation and reference the following license:
|
| 368 |
+
|
| 369 |
+
Boost Software License - Version 1.0 - August 17th, 2003
|
| 370 |
+
|
| 371 |
+
Permission is hereby granted, free of charge, to any person or organization
|
| 372 |
+
obtaining a copy of the software and accompanying documentation covered by
|
| 373 |
+
this license (the "Software") to use, reproduce, display, distribute,
|
| 374 |
+
execute, and transmit the Software, and to prepare derivative works of the
|
| 375 |
+
Software, and to permit third-parties to whom the Software is furnished to
|
| 376 |
+
do so, all subject to the following:
|
| 377 |
+
|
| 378 |
+
The copyright notices in the Software and this entire statement, including
|
| 379 |
+
the above license grant, this restriction and the following disclaimer,
|
| 380 |
+
must be included in all copies of the Software, in whole or in part, and
|
| 381 |
+
all derivative works of the Software, unless such copies or derivative
|
| 382 |
+
works are solely in the form of machine-executable object code generated by
|
| 383 |
+
a source language processor.
|
| 384 |
+
|
| 385 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
| 386 |
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
| 387 |
+
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
| 388 |
+
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
| 389 |
+
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
| 390 |
+
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
| 391 |
+
DEALINGS IN THE SOFTWARE.
|
| 392 |
+
|
| 393 |
+
END OF TERMS AND CONDITIONS
|
| 394 |
+
|
| 395 |
+
APPENDIX: How to apply the Apache License to your work.
|
| 396 |
+
|
| 397 |
+
To apply the Apache License to your work, attach the following
|
| 398 |
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
| 399 |
+
replaced with your own identifying information. (Don't include
|
| 400 |
+
the brackets!) The text should be enclosed in the appropriate
|
| 401 |
+
comment syntax for the file format. We also recommend that a
|
| 402 |
+
file or class name and description of purpose be included on the
|
| 403 |
+
same "printed page" as the copyright notice for easier
|
| 404 |
+
identification within third-party archives.
|
| 405 |
+
|
| 406 |
+
Copyright [yyyy] [name of copyright owner]
|
| 407 |
+
|
| 408 |
+
Licensed under the Apache License, Version 2.0 (the "License");
|
| 409 |
+
you may not use this file except in compliance with the License.
|
| 410 |
+
You may obtain a copy of the License at
|
| 411 |
+
|
| 412 |
+
http://www.apache.org/licenses/LICENSE-2.0
|
| 413 |
+
|
| 414 |
+
Unless required by applicable law or agreed to in writing, software
|
| 415 |
+
distributed under the License is distributed on an "AS IS" BASIS,
|
| 416 |
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 417 |
+
See the License for the specific language governing permissions and
|
| 418 |
+
limitations under the License.
|
| 419 |
+
|
| 420 |
+
=======================================================================
|
| 421 |
+
PILLOW-SIMD Software License
|
| 422 |
+
=======================================================================
|
| 423 |
+
|
| 424 |
+
Code derived from implementations in PILLOW-SIMD should mention its derivation
|
| 425 |
+
and reference the following license:
|
| 426 |
+
|
| 427 |
+
The Python Imaging Library (PIL) is
|
| 428 |
+
|
| 429 |
+
Copyright © 1997-2011 by Secret Labs AB
|
| 430 |
+
Copyright © 1995-2011 by Fredrik Lundh
|
| 431 |
+
|
| 432 |
+
Pillow is the friendly PIL fork. It is
|
| 433 |
+
|
| 434 |
+
Copyright © 2010-2022 by Alex Clark and contributors
|
| 435 |
+
|
| 436 |
+
Like PIL, Pillow is licensed under the open source HPND License:
|
| 437 |
+
|
| 438 |
+
By obtaining, using, and/or copying this software and/or its associated
|
| 439 |
+
documentation, you agree that you have read, understood, and will comply
|
| 440 |
+
with the following terms and conditions:
|
| 441 |
+
|
| 442 |
+
Permission to use, copy, modify, and distribute this software and its
|
| 443 |
+
associated documentation for any purpose and without fee is hereby granted,
|
| 444 |
+
provided that the above copyright notice appears in all copies, and that
|
| 445 |
+
both that copyright notice and this permission notice appear in supporting
|
| 446 |
+
documentation, and that the name of Secret Labs AB or the author not be
|
| 447 |
+
used in advertising or publicity pertaining to distribution of the software
|
| 448 |
+
without specific, written prior permission.
|
| 449 |
+
|
| 450 |
+
SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
|
| 451 |
+
SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
|
| 452 |
+
IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR BE LIABLE FOR ANY SPECIAL,
|
| 453 |
+
INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
| 454 |
+
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
| 455 |
+
OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
| 456 |
+
PERFORMANCE OF THIS SOFTWARE.
|
RECORD
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
REQUESTED
ADDED
|
File without changes
|
WHEEL
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Wheel-Version: 1.0
|
| 2 |
+
Generator: setuptools (72.1.0)
|
| 3 |
+
Root-Is-Purelib: false
|
| 4 |
+
Tag: cp312-cp312-win_amd64
|
| 5 |
+
|
__init__.py
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""torchgen
|
| 2 |
+
|
| 3 |
+
This module contains codegeneration utilities for PyTorch. It is used to
|
| 4 |
+
build PyTorch from source, but may also be used for out-of-tree projects
|
| 5 |
+
that extend PyTorch.
|
| 6 |
+
|
| 7 |
+
Note well that we provide no BC guarantees for torchgen. If you're interested
|
| 8 |
+
in using torchgen and want the PyTorch team to be aware, please reach out
|
| 9 |
+
on GitHub.
|
| 10 |
+
"""
|
activate
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# This file must be used with "source bin/activate" *from bash*
|
| 2 |
+
# You cannot run it directly
|
| 3 |
+
|
| 4 |
+
deactivate () {
|
| 5 |
+
# reset old environment variables
|
| 6 |
+
if [ -n "${_OLD_VIRTUAL_PATH:-}" ] ; then
|
| 7 |
+
PATH="${_OLD_VIRTUAL_PATH:-}"
|
| 8 |
+
export PATH
|
| 9 |
+
unset _OLD_VIRTUAL_PATH
|
| 10 |
+
fi
|
| 11 |
+
if [ -n "${_OLD_VIRTUAL_PYTHONHOME:-}" ] ; then
|
| 12 |
+
PYTHONHOME="${_OLD_VIRTUAL_PYTHONHOME:-}"
|
| 13 |
+
export PYTHONHOME
|
| 14 |
+
unset _OLD_VIRTUAL_PYTHONHOME
|
| 15 |
+
fi
|
| 16 |
+
|
| 17 |
+
# Call hash to forget past commands. Without forgetting
|
| 18 |
+
# past commands the $PATH changes we made may not be respected
|
| 19 |
+
hash -r 2> /dev/null
|
| 20 |
+
|
| 21 |
+
if [ -n "${_OLD_VIRTUAL_PS1:-}" ] ; then
|
| 22 |
+
PS1="${_OLD_VIRTUAL_PS1:-}"
|
| 23 |
+
export PS1
|
| 24 |
+
unset _OLD_VIRTUAL_PS1
|
| 25 |
+
fi
|
| 26 |
+
|
| 27 |
+
unset VIRTUAL_ENV
|
| 28 |
+
unset VIRTUAL_ENV_PROMPT
|
| 29 |
+
if [ ! "${1:-}" = "nondestructive" ] ; then
|
| 30 |
+
# Self destruct!
|
| 31 |
+
unset -f deactivate
|
| 32 |
+
fi
|
| 33 |
+
}
|
| 34 |
+
|
| 35 |
+
# unset irrelevant variables
|
| 36 |
+
deactivate nondestructive
|
| 37 |
+
|
| 38 |
+
# on Windows, a path can contain colons and backslashes and has to be converted:
|
| 39 |
+
if [ "${OSTYPE:-}" = "cygwin" ] || [ "${OSTYPE:-}" = "msys" ] ; then
|
| 40 |
+
# transform D:\path\to\venv to /d/path/to/venv on MSYS
|
| 41 |
+
# and to /cygdrive/d/path/to/venv on Cygwin
|
| 42 |
+
export VIRTUAL_ENV=$(cygpath "C:\Users\migue\Downloads\Codigo\AI\HuggingModelAI\IAservicies\myenv")
|
| 43 |
+
else
|
| 44 |
+
# use the path as-is
|
| 45 |
+
export VIRTUAL_ENV="C:\Users\migue\Downloads\Codigo\AI\HuggingModelAI\IAservicies\myenv"
|
| 46 |
+
fi
|
| 47 |
+
|
| 48 |
+
_OLD_VIRTUAL_PATH="$PATH"
|
| 49 |
+
PATH="$VIRTUAL_ENV/Scripts:$PATH"
|
| 50 |
+
export PATH
|
| 51 |
+
|
| 52 |
+
# unset PYTHONHOME if set
|
| 53 |
+
# this will fail if PYTHONHOME is set to the empty string (which is bad anyway)
|
| 54 |
+
# could use `if (set -u; : $PYTHONHOME) ;` in bash
|
| 55 |
+
if [ -n "${PYTHONHOME:-}" ] ; then
|
| 56 |
+
_OLD_VIRTUAL_PYTHONHOME="${PYTHONHOME:-}"
|
| 57 |
+
unset PYTHONHOME
|
| 58 |
+
fi
|
| 59 |
+
|
| 60 |
+
if [ -z "${VIRTUAL_ENV_DISABLE_PROMPT:-}" ] ; then
|
| 61 |
+
_OLD_VIRTUAL_PS1="${PS1:-}"
|
| 62 |
+
PS1="(myenv) ${PS1:-}"
|
| 63 |
+
export PS1
|
| 64 |
+
VIRTUAL_ENV_PROMPT="(myenv) "
|
| 65 |
+
export VIRTUAL_ENV_PROMPT
|
| 66 |
+
fi
|
| 67 |
+
|
| 68 |
+
# Call hash to forget past commands. Without forgetting
|
| 69 |
+
# past commands the $PATH changes we made may not be respected
|
| 70 |
+
hash -r 2> /dev/null
|
activate.bat
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
@echo off
|
| 2 |
+
|
| 3 |
+
rem This file is UTF-8 encoded, so we need to update the current code page while executing it
|
| 4 |
+
for /f "tokens=2 delims=:." %%a in ('"%SystemRoot%\System32\chcp.com"') do (
|
| 5 |
+
set _OLD_CODEPAGE=%%a
|
| 6 |
+
)
|
| 7 |
+
if defined _OLD_CODEPAGE (
|
| 8 |
+
"%SystemRoot%\System32\chcp.com" 65001 > nul
|
| 9 |
+
)
|
| 10 |
+
|
| 11 |
+
set VIRTUAL_ENV=C:\Users\migue\Downloads\Codigo\AI\HuggingModelAI\IAservicies\myenv
|
| 12 |
+
|
| 13 |
+
if not defined PROMPT set PROMPT=$P$G
|
| 14 |
+
|
| 15 |
+
if defined _OLD_VIRTUAL_PROMPT set PROMPT=%_OLD_VIRTUAL_PROMPT%
|
| 16 |
+
if defined _OLD_VIRTUAL_PYTHONHOME set PYTHONHOME=%_OLD_VIRTUAL_PYTHONHOME%
|
| 17 |
+
|
| 18 |
+
set _OLD_VIRTUAL_PROMPT=%PROMPT%
|
| 19 |
+
set PROMPT=(myenv) %PROMPT%
|
| 20 |
+
|
| 21 |
+
if defined PYTHONHOME set _OLD_VIRTUAL_PYTHONHOME=%PYTHONHOME%
|
| 22 |
+
set PYTHONHOME=
|
| 23 |
+
|
| 24 |
+
if defined _OLD_VIRTUAL_PATH set PATH=%_OLD_VIRTUAL_PATH%
|
| 25 |
+
if not defined _OLD_VIRTUAL_PATH set _OLD_VIRTUAL_PATH=%PATH%
|
| 26 |
+
|
| 27 |
+
set PATH=%VIRTUAL_ENV%\Scripts;%PATH%
|
| 28 |
+
set VIRTUAL_ENV_PROMPT=(myenv)
|
| 29 |
+
|
| 30 |
+
:END
|
| 31 |
+
if defined _OLD_CODEPAGE (
|
| 32 |
+
"%SystemRoot%\System32\chcp.com" %_OLD_CODEPAGE% > nul
|
| 33 |
+
set _OLD_CODEPAGE=
|
| 34 |
+
)
|
code_template.py
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
import re
|
| 4 |
+
from typing import TYPE_CHECKING
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
if TYPE_CHECKING:
|
| 8 |
+
from collections.abc import Mapping, Sequence
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
# match $identifier or ${identifier} and replace with value in env
|
| 12 |
+
# If this identifier is at the beginning of whitespace on a line
|
| 13 |
+
# and its value is a list then it is treated as
|
| 14 |
+
# block substitution by indenting to that depth and putting each element
|
| 15 |
+
# of the list on its own line
|
| 16 |
+
# if the identifier is on a line starting with non-whitespace and a list
|
| 17 |
+
# then it is comma separated ${,foo} will insert a comma before the list
|
| 18 |
+
# if this list is not empty and ${foo,} will insert one after.
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
class CodeTemplate:
|
| 22 |
+
substitution_str = r"(^[^\n\S]*)?\$([^\d\W]\w*|\{,?[^\d\W]\w*\,?})"
|
| 23 |
+
substitution = re.compile(substitution_str, re.MULTILINE)
|
| 24 |
+
|
| 25 |
+
pattern: str
|
| 26 |
+
filename: str
|
| 27 |
+
|
| 28 |
+
@staticmethod
|
| 29 |
+
def from_file(filename: str) -> CodeTemplate:
|
| 30 |
+
with open(filename) as f:
|
| 31 |
+
return CodeTemplate(f.read(), filename)
|
| 32 |
+
|
| 33 |
+
def __init__(self, pattern: str, filename: str = "") -> None:
|
| 34 |
+
self.pattern = pattern
|
| 35 |
+
self.filename = filename
|
| 36 |
+
|
| 37 |
+
def substitute(
|
| 38 |
+
self, env: Mapping[str, object] | None = None, **kwargs: object
|
| 39 |
+
) -> str:
|
| 40 |
+
if env is None:
|
| 41 |
+
env = {}
|
| 42 |
+
|
| 43 |
+
def lookup(v: str) -> object:
|
| 44 |
+
assert env is not None
|
| 45 |
+
return kwargs[v] if v in kwargs else env[v]
|
| 46 |
+
|
| 47 |
+
def indent_lines(indent: str, v: Sequence[object]) -> str:
|
| 48 |
+
return "".join(
|
| 49 |
+
[indent + l + "\n" for e in v for l in str(e).splitlines()]
|
| 50 |
+
).rstrip()
|
| 51 |
+
|
| 52 |
+
def replace(match: re.Match[str]) -> str:
|
| 53 |
+
indent = match.group(1)
|
| 54 |
+
key = match.group(2)
|
| 55 |
+
comma_before = ""
|
| 56 |
+
comma_after = ""
|
| 57 |
+
if key[0] == "{":
|
| 58 |
+
key = key[1:-1]
|
| 59 |
+
if key[0] == ",":
|
| 60 |
+
comma_before = ", "
|
| 61 |
+
key = key[1:]
|
| 62 |
+
if key[-1] == ",":
|
| 63 |
+
comma_after = ", "
|
| 64 |
+
key = key[:-1]
|
| 65 |
+
v = lookup(key)
|
| 66 |
+
if indent is not None:
|
| 67 |
+
if not isinstance(v, list):
|
| 68 |
+
v = [v]
|
| 69 |
+
return indent_lines(indent, v)
|
| 70 |
+
elif isinstance(v, list):
|
| 71 |
+
middle = ", ".join([str(x) for x in v])
|
| 72 |
+
if len(v) == 0:
|
| 73 |
+
return middle
|
| 74 |
+
return comma_before + middle + comma_after
|
| 75 |
+
else:
|
| 76 |
+
return str(v)
|
| 77 |
+
|
| 78 |
+
return self.substitution.sub(replace, self.pattern)
|
| 79 |
+
|
| 80 |
+
|
| 81 |
+
if __name__ == "__main__":
|
| 82 |
+
c = CodeTemplate(
|
| 83 |
+
"""\
|
| 84 |
+
int foo($args) {
|
| 85 |
+
|
| 86 |
+
$bar
|
| 87 |
+
$bar
|
| 88 |
+
$a+$b
|
| 89 |
+
}
|
| 90 |
+
int commatest(int a${,stuff})
|
| 91 |
+
int notest(int a${,empty,})
|
| 92 |
+
"""
|
| 93 |
+
)
|
| 94 |
+
print(
|
| 95 |
+
c.substitute(
|
| 96 |
+
args=["hi", 8],
|
| 97 |
+
bar=["what", 7],
|
| 98 |
+
a=3,
|
| 99 |
+
b=4,
|
| 100 |
+
stuff=["things...", "others"],
|
| 101 |
+
empty=[],
|
| 102 |
+
)
|
| 103 |
+
)
|
context.py
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
import contextlib
|
| 4 |
+
import functools
|
| 5 |
+
from typing import Any, Callable, Optional, TYPE_CHECKING, TypeVar, Union
|
| 6 |
+
|
| 7 |
+
import torchgen.local as local
|
| 8 |
+
from torchgen.model import (
|
| 9 |
+
BackendIndex,
|
| 10 |
+
DispatchKey,
|
| 11 |
+
NativeFunction,
|
| 12 |
+
NativeFunctionsGroup,
|
| 13 |
+
NativeFunctionsViewGroup,
|
| 14 |
+
)
|
| 15 |
+
from torchgen.utils import context, S, T
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
if TYPE_CHECKING:
|
| 19 |
+
from collections.abc import Iterator
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
# Helper functions for defining generators on things in the model
|
| 23 |
+
|
| 24 |
+
F = TypeVar(
|
| 25 |
+
"F",
|
| 26 |
+
NativeFunction,
|
| 27 |
+
NativeFunctionsGroup,
|
| 28 |
+
NativeFunctionsViewGroup,
|
| 29 |
+
Union[NativeFunction, NativeFunctionsGroup],
|
| 30 |
+
Union[NativeFunction, NativeFunctionsViewGroup],
|
| 31 |
+
)
|
| 32 |
+
|
| 33 |
+
F2 = TypeVar(
|
| 34 |
+
"F2",
|
| 35 |
+
NativeFunction,
|
| 36 |
+
NativeFunctionsGroup,
|
| 37 |
+
Optional[NativeFunction],
|
| 38 |
+
bool,
|
| 39 |
+
str,
|
| 40 |
+
)
|
| 41 |
+
|
| 42 |
+
F3 = TypeVar("F3", tuple[NativeFunction, Any], list[NativeFunction])
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
@contextlib.contextmanager
|
| 46 |
+
def native_function_manager(
|
| 47 |
+
g: NativeFunctionsGroup | NativeFunctionsViewGroup | NativeFunction,
|
| 48 |
+
) -> Iterator[None]:
|
| 49 |
+
if isinstance(g, NativeFunctionsGroup):
|
| 50 |
+
# By default, we associate all errors with structured native functions
|
| 51 |
+
# with the out variant. In some cases, it might be better to have
|
| 52 |
+
# a more specific place to hang things; if so, use
|
| 53 |
+
# native_function_manager again on the inside
|
| 54 |
+
f = g.out
|
| 55 |
+
elif isinstance(g, NativeFunctionsViewGroup):
|
| 56 |
+
# We associate errors with the view operator
|
| 57 |
+
f = g.view
|
| 58 |
+
else:
|
| 59 |
+
f = g
|
| 60 |
+
with context(lambda: f"in native_functions.yaml line {f.loc}:\n {f.func}"):
|
| 61 |
+
with local.parametrize(
|
| 62 |
+
use_const_ref_for_mutable_tensors=f.use_const_ref_for_mutable_tensors,
|
| 63 |
+
use_ilistref_for_tensor_lists=f.part_of_structured_group,
|
| 64 |
+
):
|
| 65 |
+
yield
|
| 66 |
+
|
| 67 |
+
|
| 68 |
+
# Given a function that operates on NativeFunction, wrap it into a new function
|
| 69 |
+
# that sets some appropriate context managers for that native function.
|
| 70 |
+
# YOU MUST WRAP FUNCTIONS IN THIS for calls to api modules to be sound
|
| 71 |
+
# (you will get an error if we try to access the local variables without having
|
| 72 |
+
# set them).
|
| 73 |
+
def with_native_function(func: Callable[[F], T]) -> Callable[[F], T]:
|
| 74 |
+
@functools.wraps(func)
|
| 75 |
+
def wrapper(f: F) -> T:
|
| 76 |
+
with native_function_manager(f):
|
| 77 |
+
return func(f)
|
| 78 |
+
|
| 79 |
+
return wrapper
|
| 80 |
+
|
| 81 |
+
|
| 82 |
+
def with_native_function_and(func: Callable[[F, F2], T]) -> Callable[[F, F2], T]:
|
| 83 |
+
@functools.wraps(func)
|
| 84 |
+
def wrapper(f: F, f2: F2) -> T:
|
| 85 |
+
# The first native_function is assumed to be the one with the appropriate context.
|
| 86 |
+
with native_function_manager(f):
|
| 87 |
+
return func(f, f2)
|
| 88 |
+
|
| 89 |
+
return wrapper
|
| 90 |
+
|
| 91 |
+
|
| 92 |
+
def method_with_native_function(func: Callable[[S, F], T]) -> Callable[[S, F], T]:
|
| 93 |
+
@functools.wraps(func)
|
| 94 |
+
def wrapper(slf: S, f: F) -> T:
|
| 95 |
+
with native_function_manager(f):
|
| 96 |
+
return func(slf, f)
|
| 97 |
+
|
| 98 |
+
return wrapper
|
| 99 |
+
|
| 100 |
+
|
| 101 |
+
def method_with_nested_native_function(
|
| 102 |
+
func: Callable[[S, F3], T],
|
| 103 |
+
) -> Callable[[S, F3], T]:
|
| 104 |
+
@functools.wraps(func)
|
| 105 |
+
def wrapper(slf: S, f: F3) -> T:
|
| 106 |
+
with native_function_manager(f[0]):
|
| 107 |
+
return func(slf, f)
|
| 108 |
+
|
| 109 |
+
return wrapper
|
| 110 |
+
|
| 111 |
+
|
| 112 |
+
# Convenience decorator for functions that explicitly take in a BackendIndex,
|
| 113 |
+
# instead of indirectly taking one in as a closure
|
| 114 |
+
def with_native_function_and_index(
|
| 115 |
+
func: Callable[[F, BackendIndex], T],
|
| 116 |
+
) -> Callable[[F, BackendIndex], T]:
|
| 117 |
+
@functools.wraps(func)
|
| 118 |
+
def wrapper(f: F, backend_index: BackendIndex) -> T:
|
| 119 |
+
with native_function_manager(f):
|
| 120 |
+
return func(f, backend_index)
|
| 121 |
+
|
| 122 |
+
return wrapper
|
| 123 |
+
|
| 124 |
+
|
| 125 |
+
# Convenience decorator for functions that explicitly take in a Dict of BackendIndices
|
| 126 |
+
def with_native_function_and_indices(
|
| 127 |
+
func: Callable[[F, dict[DispatchKey, BackendIndex]], T],
|
| 128 |
+
) -> Callable[[F, dict[DispatchKey, BackendIndex]], T]:
|
| 129 |
+
@functools.wraps(func)
|
| 130 |
+
def wrapper(f: F, backend_indices: dict[DispatchKey, BackendIndex]) -> T:
|
| 131 |
+
with native_function_manager(f):
|
| 132 |
+
return func(f, backend_indices)
|
| 133 |
+
|
| 134 |
+
return wrapper
|
datasets-cli.exe
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:8ecc3f2798b65a5b9ee125a1bf86f62d05a56e9210e70dddff9a6895fae506a5
|
| 3 |
+
size 108451
|
deactivate.bat
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
@echo off
|
| 2 |
+
|
| 3 |
+
if defined _OLD_VIRTUAL_PROMPT (
|
| 4 |
+
set "PROMPT=%_OLD_VIRTUAL_PROMPT%"
|
| 5 |
+
)
|
| 6 |
+
set _OLD_VIRTUAL_PROMPT=
|
| 7 |
+
|
| 8 |
+
if defined _OLD_VIRTUAL_PYTHONHOME (
|
| 9 |
+
set "PYTHONHOME=%_OLD_VIRTUAL_PYTHONHOME%"
|
| 10 |
+
set _OLD_VIRTUAL_PYTHONHOME=
|
| 11 |
+
)
|
| 12 |
+
|
| 13 |
+
if defined _OLD_VIRTUAL_PATH (
|
| 14 |
+
set "PATH=%_OLD_VIRTUAL_PATH%"
|
| 15 |
+
)
|
| 16 |
+
|
| 17 |
+
set _OLD_VIRTUAL_PATH=
|
| 18 |
+
|
| 19 |
+
set VIRTUAL_ENV=
|
| 20 |
+
set VIRTUAL_ENV_PROMPT=
|
| 21 |
+
|
| 22 |
+
:END
|
distutils-precedence.pth
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:2638ce9e2500e572a5e0de7faed6661eb569d1b696fcba07b0dd223da5f5d224
|
| 3 |
+
size 151
|
entry_points.txt
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[console_scripts]
|
| 2 |
+
torchfrtrace = tools.flight_recorder.fr_trace:main
|
| 3 |
+
torchrun = torch.distributed.run:main
|
| 4 |
+
|
| 5 |
+
[torchrun.logs_specs]
|
| 6 |
+
default = torch.distributed.elastic.multiprocessing:DefaultLogsSpecs
|
f2py.exe
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:5463826983b536494b9bf027c1fab304632aefe10c03e89f0145a7543cb69d16
|
| 3 |
+
size 108440
|
gen.py
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
gen_aoti_c_shim.py
ADDED
|
@@ -0,0 +1,508 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
import textwrap
|
| 4 |
+
from dataclasses import dataclass
|
| 5 |
+
from typing import TYPE_CHECKING
|
| 6 |
+
|
| 7 |
+
from torchgen.api.types import DispatcherSignature
|
| 8 |
+
from torchgen.api.types.signatures import CppSignature, CppSignatureGroup
|
| 9 |
+
from torchgen.context import method_with_native_function
|
| 10 |
+
from torchgen.model import (
|
| 11 |
+
Argument,
|
| 12 |
+
BackendIndex,
|
| 13 |
+
BaseTy,
|
| 14 |
+
BaseType,
|
| 15 |
+
DispatchKey,
|
| 16 |
+
FunctionSchema,
|
| 17 |
+
ListType,
|
| 18 |
+
NativeFunction,
|
| 19 |
+
NativeFunctionsGroup,
|
| 20 |
+
OperatorName,
|
| 21 |
+
OptionalType,
|
| 22 |
+
Type,
|
| 23 |
+
)
|
| 24 |
+
from torchgen.utils import mapMaybe
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
if TYPE_CHECKING:
|
| 28 |
+
from collections.abc import Sequence
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
base_type_to_c_type = {
|
| 32 |
+
BaseTy.Tensor: "AtenTensorHandle",
|
| 33 |
+
BaseTy.bool: "int32_t", # Use int to pass bool
|
| 34 |
+
BaseTy.int: "int64_t",
|
| 35 |
+
BaseTy.SymInt: "int64_t", # Inductor-generated code won't see a SymInt
|
| 36 |
+
BaseTy.Scalar: "double", # Use double to pass both integer and floating point
|
| 37 |
+
BaseTy.float: "double", # TODO: how about other floating point types?
|
| 38 |
+
BaseTy.str: "const char*",
|
| 39 |
+
BaseTy.DeviceIndex: "int32_t",
|
| 40 |
+
BaseTy.Layout: "int32_t", # Represent enum as int
|
| 41 |
+
BaseTy.MemoryFormat: "int32_t", # Represent enum as int
|
| 42 |
+
BaseTy.ScalarType: "int32_t", # Represent enum as int
|
| 43 |
+
BaseTy.Generator: "AtenGeneratorHandle",
|
| 44 |
+
}
|
| 45 |
+
|
| 46 |
+
base_type_to_aten_type = {
|
| 47 |
+
BaseTy.Tensor: "at::Tensor",
|
| 48 |
+
BaseTy.bool: "bool",
|
| 49 |
+
BaseTy.int: "int64_t",
|
| 50 |
+
BaseTy.SymInt: "c10::SymInt",
|
| 51 |
+
BaseTy.Scalar: "c10::Scalar",
|
| 52 |
+
BaseTy.float: "double",
|
| 53 |
+
BaseTy.str: "c10::string_view",
|
| 54 |
+
BaseTy.DeviceIndex: "c10::DeviceIndex",
|
| 55 |
+
BaseTy.Layout: "c10::Layout",
|
| 56 |
+
BaseTy.MemoryFormat: "c10::MemoryFormat",
|
| 57 |
+
BaseTy.ScalarType: "c10::ScalarType",
|
| 58 |
+
BaseTy.Generator: "at::Generator",
|
| 59 |
+
}
|
| 60 |
+
|
| 61 |
+
base_type_to_callsite_expr = {
|
| 62 |
+
BaseTy.Tensor: "*tensor_handle_to_tensor_pointer",
|
| 63 |
+
BaseTy.bool: "",
|
| 64 |
+
BaseTy.int: "",
|
| 65 |
+
BaseTy.SymInt: "",
|
| 66 |
+
BaseTy.Scalar: "",
|
| 67 |
+
BaseTy.float: "",
|
| 68 |
+
BaseTy.str: "",
|
| 69 |
+
BaseTy.DeviceIndex: "static_cast<c10::DeviceIndex>",
|
| 70 |
+
BaseTy.Layout: "static_cast<c10::Layout>",
|
| 71 |
+
BaseTy.MemoryFormat: "static_cast<c10::MemoryFormat>",
|
| 72 |
+
BaseTy.ScalarType: "static_cast<c10::ScalarType>",
|
| 73 |
+
BaseTy.Generator: "*generator_handle_to_generator_pointer",
|
| 74 |
+
}
|
| 75 |
+
|
| 76 |
+
|
| 77 |
+
# convert args to C types, names in declarations, and expressions in function bodies
|
| 78 |
+
def convert_arg_type_and_name( # type: ignore[return]
|
| 79 |
+
typ: Type,
|
| 80 |
+
name: str,
|
| 81 |
+
) -> tuple[list[str], list[str], list[str], list[str]]:
|
| 82 |
+
if isinstance(typ, BaseType):
|
| 83 |
+
if typ.name in base_type_to_c_type:
|
| 84 |
+
return (
|
| 85 |
+
[base_type_to_c_type[typ.name]],
|
| 86 |
+
[name],
|
| 87 |
+
[base_type_to_aten_type[typ.name]],
|
| 88 |
+
[
|
| 89 |
+
f"{base_type_to_callsite_expr[typ.name]}({name})"
|
| 90 |
+
if base_type_to_callsite_expr[typ.name]
|
| 91 |
+
else name
|
| 92 |
+
],
|
| 93 |
+
)
|
| 94 |
+
elif typ.name == BaseTy.Device:
|
| 95 |
+
return (
|
| 96 |
+
["int32_t", "int32_t"],
|
| 97 |
+
[name, name + "_index_"],
|
| 98 |
+
["c10::Device"],
|
| 99 |
+
[
|
| 100 |
+
f"c10::Device(static_cast<c10::DeviceType>({name}), static_cast<c10::DeviceIndex>({name}_index_))"
|
| 101 |
+
],
|
| 102 |
+
)
|
| 103 |
+
else:
|
| 104 |
+
# TODO: BaseTy.Dimname, etc.
|
| 105 |
+
raise NotImplementedError(f"TODO: add support for arg type {repr(typ)}")
|
| 106 |
+
elif isinstance(typ, OptionalType):
|
| 107 |
+
c_types, names, aten_types, callsite_exprs = convert_arg_type_and_name(
|
| 108 |
+
typ.elem, name
|
| 109 |
+
)
|
| 110 |
+
j = 0 # index for names
|
| 111 |
+
new_aten_types = []
|
| 112 |
+
new_callsite_exprs = []
|
| 113 |
+
for aten_type in aten_types:
|
| 114 |
+
# Use pointer to denote optional type
|
| 115 |
+
c_types[j] = c_types[j] + "*"
|
| 116 |
+
if aten_type.startswith("c10::ArrayRef<"):
|
| 117 |
+
# ArrayRef is passed as pointer + size, but no need to add "*" to the size argument
|
| 118 |
+
new_aten_types.append(f"::std::optional<{aten_type}>")
|
| 119 |
+
base_type = aten_type[len("c10::ArrayRef<") : -1]
|
| 120 |
+
new_callsite_exprs.append(
|
| 121 |
+
f"pointer_to_optional_list<{base_type}>({names[j]}, {names[j + 1]})"
|
| 122 |
+
)
|
| 123 |
+
j += 2
|
| 124 |
+
elif aten_type == "c10::Device":
|
| 125 |
+
# Device is passed as device_type + device_index
|
| 126 |
+
new_aten_types.append("::std::optional<c10::Device>")
|
| 127 |
+
new_callsite_exprs.append(
|
| 128 |
+
f"pointer_to_optional_device({names[j]}, {names[j + 1]})"
|
| 129 |
+
)
|
| 130 |
+
j += 2
|
| 131 |
+
else:
|
| 132 |
+
new_aten_types.append(f"::std::optional<{aten_type}>")
|
| 133 |
+
new_callsite_exprs.append(
|
| 134 |
+
f"pointer_to_optional<{aten_type}>({names[j]})"
|
| 135 |
+
)
|
| 136 |
+
j += 1
|
| 137 |
+
|
| 138 |
+
return (
|
| 139 |
+
c_types,
|
| 140 |
+
names,
|
| 141 |
+
new_aten_types,
|
| 142 |
+
new_callsite_exprs,
|
| 143 |
+
)
|
| 144 |
+
elif isinstance(typ, ListType):
|
| 145 |
+
# Need to explictly pass the list as pointer + length
|
| 146 |
+
c_types, names, aten_types, _ = convert_arg_type_and_name(typ.elem, name)
|
| 147 |
+
assert len(c_types) == 1, "ListType with unsupported element type " + repr(typ)
|
| 148 |
+
|
| 149 |
+
# The list content should never be modified
|
| 150 |
+
c_types[0] = f"const {c_types[0]}*"
|
| 151 |
+
c_types.append("int64_t")
|
| 152 |
+
name = names[0]
|
| 153 |
+
names.append(name + "_len_")
|
| 154 |
+
|
| 155 |
+
atype = aten_types[0]
|
| 156 |
+
callsite_exprs = []
|
| 157 |
+
if atype == "bool":
|
| 158 |
+
# no converter from std::vector<bool> to c10::ArrayRef<bool>
|
| 159 |
+
# construct std::array<bool, N> instead
|
| 160 |
+
assert typ.size is not None
|
| 161 |
+
callsite_exprs.append(f"pointer_to_list<{typ.size}>({name})")
|
| 162 |
+
elif atype == "::std::optional<at::Tensor>":
|
| 163 |
+
# convert from std::vector<::std::optional<at::Tensor>> to c10::List<::std::optional<at::Tensor>>
|
| 164 |
+
callsite_exprs.append(
|
| 165 |
+
f"c10::List<{atype}>(c10::ArrayRef<{atype}>(pointer_to_list<{atype}>({name}, {name}_len_)))"
|
| 166 |
+
)
|
| 167 |
+
else:
|
| 168 |
+
callsite_exprs.append(f"pointer_to_list<{atype}>({name}, {name}_len_)")
|
| 169 |
+
|
| 170 |
+
aten_types = [f"c10::ArrayRef<{t}>" for t in aten_types]
|
| 171 |
+
return (
|
| 172 |
+
c_types,
|
| 173 |
+
names,
|
| 174 |
+
aten_types,
|
| 175 |
+
callsite_exprs,
|
| 176 |
+
)
|
| 177 |
+
|
| 178 |
+
|
| 179 |
+
def zip_type_and_name(types: list[str], names: list[str]) -> list[str]:
|
| 180 |
+
return [typ + " " + name for typ, name in zip(types, names)]
|
| 181 |
+
|
| 182 |
+
|
| 183 |
+
# Generate argument declarations and callsite expressions
|
| 184 |
+
def gen_arguments(flat_arguments: Sequence[Argument]) -> tuple[list[str], list[str]]:
|
| 185 |
+
types = []
|
| 186 |
+
new_names = []
|
| 187 |
+
callsite_exprs = []
|
| 188 |
+
for arg in flat_arguments:
|
| 189 |
+
new_types, names, _, new_callsite_exprs = convert_arg_type_and_name(
|
| 190 |
+
arg.type, arg.name
|
| 191 |
+
)
|
| 192 |
+
types.extend(new_types)
|
| 193 |
+
new_names.extend(names)
|
| 194 |
+
callsite_exprs.extend(new_callsite_exprs)
|
| 195 |
+
return zip_type_and_name(types, new_names), callsite_exprs
|
| 196 |
+
|
| 197 |
+
|
| 198 |
+
# Return values are passed out as pointer arguments because all the C shim functions
|
| 199 |
+
# are expected to return AOTITorchError.
|
| 200 |
+
# Generate returns as declarations and callsite expressions
|
| 201 |
+
def gen_returns(schema: FunctionSchema) -> tuple[list[str], list[str]]:
|
| 202 |
+
types = []
|
| 203 |
+
names = []
|
| 204 |
+
for idx, ret in enumerate(schema.returns):
|
| 205 |
+
names.append(f"ret{idx}")
|
| 206 |
+
if isinstance(ret.type, BaseType) and ret.type.name in base_type_to_c_type:
|
| 207 |
+
types.append(base_type_to_c_type[ret.type.name] + "*")
|
| 208 |
+
else:
|
| 209 |
+
raise NotImplementedError(
|
| 210 |
+
f"TODO: add support for return type {repr(ret.type)}"
|
| 211 |
+
)
|
| 212 |
+
|
| 213 |
+
def convert_return(typ: BaseType, val: str) -> str:
|
| 214 |
+
if typ.name == BaseTy.Tensor:
|
| 215 |
+
return f"new_tensor_handle(std::move({val}));"
|
| 216 |
+
elif typ.name == BaseTy.SymInt:
|
| 217 |
+
return f"{val}.expect_int()"
|
| 218 |
+
elif typ.name == BaseTy.Scalar:
|
| 219 |
+
return f"{val}.toDouble()"
|
| 220 |
+
else:
|
| 221 |
+
return val
|
| 222 |
+
|
| 223 |
+
ret_pointer_can_be_null = False
|
| 224 |
+
unambiguous_name = schema.name.unambiguous_name()
|
| 225 |
+
for name in [
|
| 226 |
+
"_scaled_dot_product_flash_attention",
|
| 227 |
+
"_scaled_dot_product_efficient_attention",
|
| 228 |
+
"_scaled_dot_product_cudnn_attention",
|
| 229 |
+
"convolution_backward",
|
| 230 |
+
]:
|
| 231 |
+
if name in unambiguous_name:
|
| 232 |
+
ret_pointer_can_be_null = True
|
| 233 |
+
break
|
| 234 |
+
|
| 235 |
+
callsite_exprs: list[str] = []
|
| 236 |
+
for idx, ret in enumerate(schema.returns):
|
| 237 |
+
tmp = "tmp_result" if len(names) == 1 else f"std::get<{idx}>(tmp_result)"
|
| 238 |
+
assert isinstance(ret.type, BaseType)
|
| 239 |
+
rval = convert_return(ret.type, tmp)
|
| 240 |
+
if ret_pointer_can_be_null:
|
| 241 |
+
callsite_exprs.append(f"if ({names[idx]}) {{ *{names[idx]} = {rval}; }}")
|
| 242 |
+
else:
|
| 243 |
+
callsite_exprs.append(f"*{names[idx]} = {rval};")
|
| 244 |
+
|
| 245 |
+
return zip_type_and_name(types, names), callsite_exprs
|
| 246 |
+
|
| 247 |
+
|
| 248 |
+
# gen.py generates header first and then src, so caching the result here to avoid duplicate work
|
| 249 |
+
declaration_definition_cache: dict[tuple[str, str, str], tuple[str, str]] = {}
|
| 250 |
+
|
| 251 |
+
|
| 252 |
+
def gen_declaration_and_definition(
|
| 253 |
+
schema: FunctionSchema, device: str, backend_call: str
|
| 254 |
+
) -> tuple[str, str]:
|
| 255 |
+
func_name = schema.name.unambiguous_name()
|
| 256 |
+
|
| 257 |
+
global declaration_definition_cache
|
| 258 |
+
if (func_name, device, backend_call) in declaration_definition_cache:
|
| 259 |
+
return declaration_definition_cache[(func_name, device, backend_call)]
|
| 260 |
+
|
| 261 |
+
if schema.is_out_fn():
|
| 262 |
+
# out_variant has out arguments in the front, and it's ok to ignore return values
|
| 263 |
+
# because C shim functions only return AOTITorchError
|
| 264 |
+
args, callsite_exprs = gen_arguments(
|
| 265 |
+
[*schema.arguments.out, *schema.arguments.flat_non_out]
|
| 266 |
+
)
|
| 267 |
+
ret_assignments: list[str] = []
|
| 268 |
+
else:
|
| 269 |
+
args, callsite_exprs = gen_arguments(schema.arguments.flat_all)
|
| 270 |
+
# ignore return values for inplace ops
|
| 271 |
+
ret_declarations, ret_assignments = (
|
| 272 |
+
([], []) if schema.name.name.inplace else gen_returns(schema)
|
| 273 |
+
)
|
| 274 |
+
args.extend(ret_declarations)
|
| 275 |
+
|
| 276 |
+
declaration = f"AOTITorchError aoti_torch_{device}_{func_name}({', '.join(args)})"
|
| 277 |
+
|
| 278 |
+
tmp_result = "auto tmp_result = " if ret_assignments else ""
|
| 279 |
+
ret_assignments_str = "\n" + "\n".join(ret_assignments) if ret_assignments else ""
|
| 280 |
+
definition = f"""
|
| 281 |
+
{declaration} {{
|
| 282 |
+
AOTI_TORCH_CONVERT_EXCEPTION_TO_ERROR_CODE({{
|
| 283 |
+
{tmp_result}{backend_call}(
|
| 284 |
+
{textwrap.indent(', '.join(callsite_exprs), " ")}
|
| 285 |
+
);{textwrap.indent(ret_assignments_str, " ")}
|
| 286 |
+
}});
|
| 287 |
+
}}
|
| 288 |
+
"""
|
| 289 |
+
declaration_definition_cache[(func_name, device, backend_call)] = (
|
| 290 |
+
declaration,
|
| 291 |
+
definition,
|
| 292 |
+
)
|
| 293 |
+
return declaration, definition
|
| 294 |
+
|
| 295 |
+
|
| 296 |
+
def gen_static_dispatch_backend_call_signature(
|
| 297 |
+
sig: CppSignature | DispatcherSignature,
|
| 298 |
+
f: NativeFunction,
|
| 299 |
+
) -> CppSignature:
|
| 300 |
+
sig = DispatcherSignature.from_schema(f.func)
|
| 301 |
+
cpp_sigs = CppSignatureGroup.from_native_function(
|
| 302 |
+
f, method=False, fallback_binding=False
|
| 303 |
+
)
|
| 304 |
+
if sig.symint and f.func.has_symint():
|
| 305 |
+
cpp_sig = cpp_sigs.symint_signature
|
| 306 |
+
else:
|
| 307 |
+
cpp_sig = cpp_sigs.signature
|
| 308 |
+
assert cpp_sig is not None
|
| 309 |
+
return cpp_sig
|
| 310 |
+
|
| 311 |
+
|
| 312 |
+
def gen_static_dispatch_backend_call(
|
| 313 |
+
f: NativeFunction,
|
| 314 |
+
backend_index: BackendIndex,
|
| 315 |
+
) -> str:
|
| 316 |
+
sig = DispatcherSignature.from_schema(f.func)
|
| 317 |
+
cpp_sig = gen_static_dispatch_backend_call_signature(sig, f)
|
| 318 |
+
return f"at::{backend_index.dispatch_key.lower()}::{cpp_sig.name()}"
|
| 319 |
+
|
| 320 |
+
|
| 321 |
+
def get_backend_index_for_aoti(
|
| 322 |
+
func: NativeFunction,
|
| 323 |
+
func_group_mapping: dict[OperatorName, NativeFunctionsGroup],
|
| 324 |
+
dispatch_key: DispatchKey,
|
| 325 |
+
backend_indices: dict[DispatchKey, BackendIndex],
|
| 326 |
+
extend_aoti_c_shim: bool,
|
| 327 |
+
) -> BackendIndex | None:
|
| 328 |
+
backend_index = None
|
| 329 |
+
if backend_indices[dispatch_key].has_kernel(func) or (
|
| 330 |
+
func.structured_delegate is not None
|
| 331 |
+
and func.structured_delegate in func_group_mapping
|
| 332 |
+
and backend_indices[dispatch_key].has_kernel(
|
| 333 |
+
func_group_mapping[func.structured_delegate]
|
| 334 |
+
)
|
| 335 |
+
):
|
| 336 |
+
backend_index = backend_indices[dispatch_key]
|
| 337 |
+
else:
|
| 338 |
+
# for the extend out-of-tree kernels, we don't need to
|
| 339 |
+
# duplicatly create C shim wrappers for other dispatch keys
|
| 340 |
+
if extend_aoti_c_shim:
|
| 341 |
+
return backend_index
|
| 342 |
+
|
| 343 |
+
elif backend_indices[DispatchKey.CompositeExplicitAutograd].has_kernel(func):
|
| 344 |
+
# We need to create C shim wrappers for CompositeExplicitAutograd kernels
|
| 345 |
+
backend_index = backend_indices[DispatchKey.CompositeExplicitAutograd]
|
| 346 |
+
elif backend_indices[
|
| 347 |
+
DispatchKey.CompositeExplicitAutogradNonFunctional
|
| 348 |
+
].has_kernel(func):
|
| 349 |
+
# We need to create C shim wrappers for CompositeExplicitAutogradNonFunctional kernels
|
| 350 |
+
backend_index = backend_indices[
|
| 351 |
+
DispatchKey.CompositeExplicitAutogradNonFunctional
|
| 352 |
+
]
|
| 353 |
+
elif backend_indices[DispatchKey.CompositeImplicitAutograd].has_kernel(func):
|
| 354 |
+
backend_index = backend_indices[DispatchKey.CompositeImplicitAutograd]
|
| 355 |
+
|
| 356 |
+
return backend_index
|
| 357 |
+
|
| 358 |
+
|
| 359 |
+
def get_header_for_aoti(
|
| 360 |
+
func: NativeFunction,
|
| 361 |
+
func_group_mapping: dict[OperatorName, NativeFunctionsGroup],
|
| 362 |
+
dispatch_key: DispatchKey,
|
| 363 |
+
backend_indices: dict[DispatchKey, BackendIndex],
|
| 364 |
+
extend_aoti_c_shim: bool,
|
| 365 |
+
) -> str | None:
|
| 366 |
+
backend_index = get_backend_index_for_aoti(
|
| 367 |
+
func, func_group_mapping, dispatch_key, backend_indices, extend_aoti_c_shim
|
| 368 |
+
)
|
| 369 |
+
return (
|
| 370 |
+
None
|
| 371 |
+
if backend_index is None
|
| 372 |
+
else f"#include <ATen/ops/{func.root_name}_{backend_index.dispatch_key.lower()}_dispatch.h>"
|
| 373 |
+
)
|
| 374 |
+
|
| 375 |
+
|
| 376 |
+
def get_fallback_op_name(func: NativeFunction) -> str:
|
| 377 |
+
return (
|
| 378 |
+
f"{func.namespace}.{func.func.name.name}.{func.func.name.overload_name}"
|
| 379 |
+
if func.func.name.overload_name
|
| 380 |
+
else f"{func.namespace}.{func.func.name.name}.default"
|
| 381 |
+
)
|
| 382 |
+
|
| 383 |
+
|
| 384 |
+
def gen_c_shim(
|
| 385 |
+
func: NativeFunction,
|
| 386 |
+
func_group_mapping: dict[OperatorName, NativeFunctionsGroup],
|
| 387 |
+
dispatch_key: DispatchKey,
|
| 388 |
+
backend_indices: dict[DispatchKey, BackendIndex],
|
| 389 |
+
header: bool,
|
| 390 |
+
extend_aoti_c_shim: bool,
|
| 391 |
+
) -> str | None:
|
| 392 |
+
backend_index = get_backend_index_for_aoti(
|
| 393 |
+
func, func_group_mapping, dispatch_key, backend_indices, extend_aoti_c_shim
|
| 394 |
+
)
|
| 395 |
+
if backend_index is None:
|
| 396 |
+
return None
|
| 397 |
+
|
| 398 |
+
schema = func.func
|
| 399 |
+
device = dispatch_key.lower()
|
| 400 |
+
backend_call = gen_static_dispatch_backend_call(
|
| 401 |
+
func,
|
| 402 |
+
backend_index,
|
| 403 |
+
)
|
| 404 |
+
|
| 405 |
+
try:
|
| 406 |
+
if header:
|
| 407 |
+
declaration, _ = gen_declaration_and_definition(
|
| 408 |
+
schema, device, backend_call
|
| 409 |
+
)
|
| 410 |
+
return f"AOTI_TORCH_EXPORT {declaration};"
|
| 411 |
+
else:
|
| 412 |
+
_, definition = gen_declaration_and_definition(schema, device, backend_call)
|
| 413 |
+
return definition
|
| 414 |
+
|
| 415 |
+
except NotImplementedError:
|
| 416 |
+
return None
|
| 417 |
+
|
| 418 |
+
|
| 419 |
+
@dataclass(frozen=True)
|
| 420 |
+
class ShimGenerator:
|
| 421 |
+
func_group_mapping: dict[OperatorName, NativeFunctionsGroup]
|
| 422 |
+
dispatch_key: DispatchKey
|
| 423 |
+
backend_indices: dict[DispatchKey, BackendIndex]
|
| 424 |
+
header: bool # True to generate .h and False to generate .cpp
|
| 425 |
+
extend_aoti_c_shim: bool
|
| 426 |
+
|
| 427 |
+
@method_with_native_function
|
| 428 |
+
def __call__(
|
| 429 |
+
self,
|
| 430 |
+
func: NativeFunction,
|
| 431 |
+
) -> str | None:
|
| 432 |
+
result = gen_c_shim(
|
| 433 |
+
func,
|
| 434 |
+
self.func_group_mapping,
|
| 435 |
+
self.dispatch_key,
|
| 436 |
+
self.backend_indices,
|
| 437 |
+
self.header,
|
| 438 |
+
self.extend_aoti_c_shim,
|
| 439 |
+
)
|
| 440 |
+
return result
|
| 441 |
+
|
| 442 |
+
|
| 443 |
+
def gen_aoti_c_shim(
|
| 444 |
+
native_functions: Sequence[NativeFunction],
|
| 445 |
+
func_group_mapping: dict[OperatorName, NativeFunctionsGroup],
|
| 446 |
+
dispatch_key: DispatchKey,
|
| 447 |
+
backend_indices: dict[DispatchKey, BackendIndex],
|
| 448 |
+
header: bool,
|
| 449 |
+
extend_aoti_c_shim: bool,
|
| 450 |
+
includes: str = "",
|
| 451 |
+
) -> str:
|
| 452 |
+
body = "\n".join(
|
| 453 |
+
list(
|
| 454 |
+
mapMaybe(
|
| 455 |
+
ShimGenerator(
|
| 456 |
+
func_group_mapping,
|
| 457 |
+
dispatch_key,
|
| 458 |
+
backend_indices,
|
| 459 |
+
header,
|
| 460 |
+
extend_aoti_c_shim,
|
| 461 |
+
),
|
| 462 |
+
native_functions,
|
| 463 |
+
)
|
| 464 |
+
)
|
| 465 |
+
)
|
| 466 |
+
device = dispatch_key.lower()
|
| 467 |
+
warning = """
|
| 468 |
+
// WARNING: THIS FILE IS AUTOGENERATED BY torchgen. DO NOT MODIFY BY HAND.
|
| 469 |
+
// See https://github.com/pytorch/pytorch/blob/7e86a7c0155295539996e0cf422883571126073e/torchgen/gen.py#L2424-L2436 for details"""
|
| 470 |
+
|
| 471 |
+
if header:
|
| 472 |
+
return f"""
|
| 473 |
+
{warning}
|
| 474 |
+
|
| 475 |
+
#pragma once
|
| 476 |
+
|
| 477 |
+
#include <torch/csrc/inductor/aoti_torch/c/shim.h>
|
| 478 |
+
|
| 479 |
+
#ifdef __cplusplus
|
| 480 |
+
extern "C" {{
|
| 481 |
+
#endif
|
| 482 |
+
|
| 483 |
+
{body}
|
| 484 |
+
|
| 485 |
+
#ifdef __cplusplus
|
| 486 |
+
}} // extern "C"
|
| 487 |
+
#endif
|
| 488 |
+
"""
|
| 489 |
+
|
| 490 |
+
else:
|
| 491 |
+
return f"""
|
| 492 |
+
{warning}
|
| 493 |
+
|
| 494 |
+
#include <torch/csrc/inductor/aoti_torch/generated/{"extend/" if extend_aoti_c_shim else ""}c_shim_{device}.h>
|
| 495 |
+
#include <torch/csrc/inductor/aoti_torch/utils.h>
|
| 496 |
+
|
| 497 |
+
#ifndef AT_PER_OPERATOR_HEADERS
|
| 498 |
+
#include <ATen/{str(dispatch_key)}Functions.h>
|
| 499 |
+
#include <ATen/CompositeExplicitAutogradFunctions.h>
|
| 500 |
+
#include <ATen/CompositeExplicitAutogradNonFunctionalFunctions.h>
|
| 501 |
+
#include <ATen/CompositeImplicitAutogradFunctions.h>
|
| 502 |
+
#else
|
| 503 |
+
{includes}
|
| 504 |
+
#endif
|
| 505 |
+
|
| 506 |
+
using namespace torch::aot_inductor;
|
| 507 |
+
|
| 508 |
+
{body}"""
|
gen_backend_stubs.py
ADDED
|
@@ -0,0 +1,615 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
import argparse
|
| 4 |
+
import os
|
| 5 |
+
import re
|
| 6 |
+
from collections import Counter, defaultdict, namedtuple
|
| 7 |
+
from pathlib import Path
|
| 8 |
+
from typing import TYPE_CHECKING
|
| 9 |
+
|
| 10 |
+
import yaml
|
| 11 |
+
|
| 12 |
+
import torchgen.api.dispatcher as dispatcher
|
| 13 |
+
import torchgen.dest as dest
|
| 14 |
+
from torchgen.api.types import DispatcherSignature
|
| 15 |
+
from torchgen.code_template import CodeTemplate
|
| 16 |
+
from torchgen.context import native_function_manager
|
| 17 |
+
from torchgen.gen import get_grouped_native_functions, parse_native_yaml
|
| 18 |
+
from torchgen.model import (
|
| 19 |
+
BackendIndex,
|
| 20 |
+
BackendMetadata,
|
| 21 |
+
DispatchKey,
|
| 22 |
+
NativeFunction,
|
| 23 |
+
NativeFunctionsGroup,
|
| 24 |
+
OperatorName,
|
| 25 |
+
)
|
| 26 |
+
from torchgen.selective_build.selector import SelectiveBuilder
|
| 27 |
+
from torchgen.utils import concatMap, context, FileManager, NamespaceHelper, Target
|
| 28 |
+
from torchgen.yaml_utils import YamlLoader
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
if TYPE_CHECKING:
|
| 32 |
+
from collections.abc import Sequence
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
# Parses the external backend's yaml, and adds a new BackendIndex for the backend's dispatch key.
|
| 36 |
+
# Returns a Tuple of (backend_key, autograd_key, cpp_namespace, updated BackendIndex mapping)
|
| 37 |
+
ParsedExternalYaml = namedtuple(
|
| 38 |
+
"ParsedExternalYaml",
|
| 39 |
+
["backend_key", "autograd_key", "class_name", "cpp_namespace", "backend_indices"],
|
| 40 |
+
)
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
def parse_backend_yaml(
|
| 44 |
+
backend_yaml_path: str,
|
| 45 |
+
grouped_native_functions: Sequence[NativeFunction | NativeFunctionsGroup],
|
| 46 |
+
backend_indices: dict[DispatchKey, BackendIndex],
|
| 47 |
+
) -> ParsedExternalYaml:
|
| 48 |
+
native_functions_map: dict[OperatorName, NativeFunction] = {
|
| 49 |
+
f.func.name: f
|
| 50 |
+
for f in concatMap(
|
| 51 |
+
lambda f: [f] if isinstance(f, NativeFunction) else list(f.functions()),
|
| 52 |
+
grouped_native_functions,
|
| 53 |
+
)
|
| 54 |
+
}
|
| 55 |
+
|
| 56 |
+
with open(backend_yaml_path) as f:
|
| 57 |
+
yaml_values = yaml.load(f, Loader=YamlLoader)
|
| 58 |
+
assert isinstance(yaml_values, dict)
|
| 59 |
+
|
| 60 |
+
valid_keys = [
|
| 61 |
+
"backend",
|
| 62 |
+
"class_name",
|
| 63 |
+
"cpp_namespace",
|
| 64 |
+
"extra_headers",
|
| 65 |
+
"supported",
|
| 66 |
+
"autograd",
|
| 67 |
+
"full_codegen",
|
| 68 |
+
"non_native",
|
| 69 |
+
"ir_gen",
|
| 70 |
+
"symint",
|
| 71 |
+
]
|
| 72 |
+
|
| 73 |
+
backend = yaml_values.pop("backend", None)
|
| 74 |
+
assert backend is not None, 'You must provide a value for "backend"'
|
| 75 |
+
|
| 76 |
+
class_name = yaml_values.pop("class_name", None)
|
| 77 |
+
|
| 78 |
+
cpp_namespace = yaml_values.pop("cpp_namespace", None)
|
| 79 |
+
assert cpp_namespace is not None, 'You must provide a value for "cpp_namespace"'
|
| 80 |
+
|
| 81 |
+
# Mostly just defaulting to false to stick with LazyTensor convention.
|
| 82 |
+
use_out_as_primary = yaml_values.pop("use_out_as_primary", False)
|
| 83 |
+
assert isinstance(
|
| 84 |
+
use_out_as_primary, bool
|
| 85 |
+
), f"You must provide either True or False for use_out_as_primary. Provided: {use_out_as_primary}"
|
| 86 |
+
|
| 87 |
+
use_device_guard = yaml_values.pop("device_guard", False)
|
| 88 |
+
assert isinstance(
|
| 89 |
+
use_device_guard, bool
|
| 90 |
+
), f"You must provide either True or False for device_guard. Provided: {use_device_guard}"
|
| 91 |
+
|
| 92 |
+
supported = yaml_values.pop("supported", [])
|
| 93 |
+
if supported is None:
|
| 94 |
+
supported = [] # Allow an empty list of supported ops
|
| 95 |
+
assert isinstance(
|
| 96 |
+
supported, list
|
| 97 |
+
), f'expected "supported" to be a list, but got: {supported} (of type {type(supported)})'
|
| 98 |
+
|
| 99 |
+
symint = yaml_values.pop("symint", [])
|
| 100 |
+
if symint is None:
|
| 101 |
+
symint = [] # Allow an empty list of symint ops
|
| 102 |
+
assert isinstance(
|
| 103 |
+
symint, list
|
| 104 |
+
), f'expected "symint" to be a list, but got: {supported} (of type {type(supported)})'
|
| 105 |
+
symint_set = set(symint)
|
| 106 |
+
|
| 107 |
+
supported_autograd = yaml_values.pop("autograd", [])
|
| 108 |
+
assert isinstance(
|
| 109 |
+
supported_autograd, list
|
| 110 |
+
), f'expected "autograd" to be a list, but got: {supported_autograd}'
|
| 111 |
+
|
| 112 |
+
# full_codegen is ignored by parse_backend_yaml, and re-parsed in gen_lazy_tensor.py
|
| 113 |
+
full_codegen = yaml_values.pop("full_codegen", [])
|
| 114 |
+
supported.extend(full_codegen)
|
| 115 |
+
|
| 116 |
+
# non_native is ignored by parse_backend_yaml, and re-parsed in gen_lazy_tensor.py
|
| 117 |
+
yaml_values.pop("non_native", {})
|
| 118 |
+
|
| 119 |
+
# ir_gen is ignored by parse_backend_yaml, and re-parsed in gen_lazy_tensor.py
|
| 120 |
+
yaml_values.pop("ir_gen", {})
|
| 121 |
+
|
| 122 |
+
assert (
|
| 123 |
+
len(yaml_values.keys()) == 0
|
| 124 |
+
), f'{backend_yaml_path} contains unexpected keys: {", ".join(yaml_values.keys())}. \
|
| 125 |
+
Only the following keys are supported: {", ".join(valid_keys)}'
|
| 126 |
+
|
| 127 |
+
def create_backend_index(
|
| 128 |
+
backend_ops: list[str],
|
| 129 |
+
symint_ops: set[str],
|
| 130 |
+
dispatch_key: DispatchKey,
|
| 131 |
+
*,
|
| 132 |
+
use_out_as_primary: bool,
|
| 133 |
+
use_device_guard: bool,
|
| 134 |
+
) -> BackendIndex:
|
| 135 |
+
metadata: dict[OperatorName, BackendMetadata] = {}
|
| 136 |
+
for op in backend_ops:
|
| 137 |
+
op_name = OperatorName.parse(op)
|
| 138 |
+
assert (
|
| 139 |
+
op_name in native_functions_map
|
| 140 |
+
), f"Found an invalid operator name: {op_name}"
|
| 141 |
+
# See Note [External Backends Follow Dispatcher API]
|
| 142 |
+
kernel_name = dispatcher.name(native_functions_map[op_name].func)
|
| 143 |
+
if op in symint_ops:
|
| 144 |
+
kernel_name += "_symint"
|
| 145 |
+
# TODO: allow structured external backends later.
|
| 146 |
+
m = BackendMetadata(
|
| 147 |
+
kernel=kernel_name, structured=False, cpp_namespace=cpp_namespace
|
| 148 |
+
)
|
| 149 |
+
metadata[op_name] = m
|
| 150 |
+
return BackendIndex(
|
| 151 |
+
dispatch_key=dispatch_key,
|
| 152 |
+
use_out_as_primary=use_out_as_primary,
|
| 153 |
+
external=True,
|
| 154 |
+
device_guard=use_device_guard,
|
| 155 |
+
index=metadata,
|
| 156 |
+
)
|
| 157 |
+
|
| 158 |
+
backend_key: DispatchKey | None = None
|
| 159 |
+
if len(supported) > 0:
|
| 160 |
+
with context(
|
| 161 |
+
lambda: f'The provided value for "backend" must be a valid DispatchKey, but got {backend}.'
|
| 162 |
+
):
|
| 163 |
+
backend_key = DispatchKey.parse(backend)
|
| 164 |
+
|
| 165 |
+
backend_idx = create_backend_index(
|
| 166 |
+
supported,
|
| 167 |
+
symint_set,
|
| 168 |
+
backend_key,
|
| 169 |
+
use_out_as_primary=use_out_as_primary,
|
| 170 |
+
use_device_guard=use_device_guard,
|
| 171 |
+
)
|
| 172 |
+
assert backend_key not in backend_indices
|
| 173 |
+
backend_indices[backend_key] = backend_idx
|
| 174 |
+
|
| 175 |
+
autograd_key: DispatchKey | None = None
|
| 176 |
+
if len(supported_autograd) > 0:
|
| 177 |
+
with context(
|
| 178 |
+
lambda: f'The "autograd" key was specified, which indicates that you would like to override \
|
| 179 |
+
the behavior of autograd for some operators on your backend. However "Autograd{backend}" is not a valid DispatchKey.'
|
| 180 |
+
):
|
| 181 |
+
autograd_key = DispatchKey.parse(f"Autograd{backend}")
|
| 182 |
+
|
| 183 |
+
autograd_idx = create_backend_index(
|
| 184 |
+
supported_autograd,
|
| 185 |
+
symint_set,
|
| 186 |
+
autograd_key,
|
| 187 |
+
use_out_as_primary=use_out_as_primary,
|
| 188 |
+
use_device_guard=use_device_guard,
|
| 189 |
+
)
|
| 190 |
+
assert autograd_key not in backend_indices
|
| 191 |
+
backend_indices[autograd_key] = autograd_idx
|
| 192 |
+
|
| 193 |
+
for g in grouped_native_functions:
|
| 194 |
+
if isinstance(g, NativeFunction):
|
| 195 |
+
forward_kernels = (
|
| 196 |
+
[]
|
| 197 |
+
if backend_key is None
|
| 198 |
+
else [
|
| 199 |
+
m
|
| 200 |
+
for m in [backend_indices[backend_key].get_kernel(g)]
|
| 201 |
+
if m is not None
|
| 202 |
+
]
|
| 203 |
+
)
|
| 204 |
+
backward_kernels = (
|
| 205 |
+
[]
|
| 206 |
+
if autograd_key is None
|
| 207 |
+
else [
|
| 208 |
+
m
|
| 209 |
+
for m in [backend_indices[autograd_key].get_kernel(g)]
|
| 210 |
+
if m is not None
|
| 211 |
+
]
|
| 212 |
+
)
|
| 213 |
+
else:
|
| 214 |
+
forward_kernels = (
|
| 215 |
+
[]
|
| 216 |
+
if backend_key is None
|
| 217 |
+
else [
|
| 218 |
+
m
|
| 219 |
+
for m in [
|
| 220 |
+
backend_indices[backend_key].get_kernel(f)
|
| 221 |
+
for f in g.functions()
|
| 222 |
+
]
|
| 223 |
+
if m is not None
|
| 224 |
+
]
|
| 225 |
+
)
|
| 226 |
+
backward_kernels = (
|
| 227 |
+
[]
|
| 228 |
+
if autograd_key is None
|
| 229 |
+
else [
|
| 230 |
+
m
|
| 231 |
+
for m in [
|
| 232 |
+
backend_indices[autograd_key].get_kernel(f)
|
| 233 |
+
for f in g.functions()
|
| 234 |
+
]
|
| 235 |
+
if m is not None
|
| 236 |
+
]
|
| 237 |
+
)
|
| 238 |
+
|
| 239 |
+
forward_kernels = [f for f in forward_kernels if f is not None]
|
| 240 |
+
backward_kernels = [f for f in backward_kernels if f is not None]
|
| 241 |
+
assert (
|
| 242 |
+
len(forward_kernels) == 0 or len(backward_kernels) == 0
|
| 243 |
+
), f'Currently, all variants of an op must either be registered to a backend key, or to a backend\'s \
|
| 244 |
+
autograd key. They cannot be mix and matched. If this is something you need, feel free to create an issue! \
|
| 245 |
+
{forward_kernels[0].kernel} is listed under "supported", but {backward_kernels[0].kernel} is listed under "autograd".'
|
| 246 |
+
|
| 247 |
+
return ParsedExternalYaml(
|
| 248 |
+
backend_key, autograd_key, class_name, cpp_namespace, backend_indices
|
| 249 |
+
)
|
| 250 |
+
|
| 251 |
+
|
| 252 |
+
def error_on_missing_kernels(
|
| 253 |
+
native_functions: Sequence[NativeFunction],
|
| 254 |
+
backend_indices: dict[DispatchKey, BackendIndex],
|
| 255 |
+
backend_key: DispatchKey,
|
| 256 |
+
autograd_key: DispatchKey | None,
|
| 257 |
+
class_name: str,
|
| 258 |
+
kernel_defn_file_path: str,
|
| 259 |
+
full_codegen: list[OperatorName] | None = None,
|
| 260 |
+
) -> None:
|
| 261 |
+
try:
|
| 262 |
+
with open(kernel_defn_file_path) as f:
|
| 263 |
+
backend_defns = f.read()
|
| 264 |
+
except OSError as e:
|
| 265 |
+
raise AssertionError(
|
| 266 |
+
f"Unable to read from the specified impl_path file: {kernel_defn_file_path}"
|
| 267 |
+
) from e
|
| 268 |
+
|
| 269 |
+
if full_codegen is None:
|
| 270 |
+
full_codegen = []
|
| 271 |
+
|
| 272 |
+
indices = [backend_indices[backend_key].index] + (
|
| 273 |
+
[] if autograd_key is None else [backend_indices[autograd_key].index]
|
| 274 |
+
)
|
| 275 |
+
# Quick mapping from each OperatorName used by the external backend
|
| 276 |
+
# to its backend kernel name
|
| 277 |
+
expected_backend_op_names: dict[OperatorName, str] = dict(
|
| 278 |
+
list(
|
| 279 |
+
concatMap(
|
| 280 |
+
lambda index: [
|
| 281 |
+
(op_name, metadata.kernel) for op_name, metadata in index.items()
|
| 282 |
+
],
|
| 283 |
+
indices,
|
| 284 |
+
)
|
| 285 |
+
)
|
| 286 |
+
)
|
| 287 |
+
expected_backend_native_funcs: list[NativeFunction] = [
|
| 288 |
+
f
|
| 289 |
+
for f in native_functions
|
| 290 |
+
if f.func.name in expected_backend_op_names.keys()
|
| 291 |
+
and f.func.name not in full_codegen
|
| 292 |
+
]
|
| 293 |
+
expected_backend_kernel_name_counts: dict[str, list[NativeFunction]] = defaultdict(
|
| 294 |
+
list
|
| 295 |
+
)
|
| 296 |
+
for native_f in expected_backend_native_funcs:
|
| 297 |
+
expected_backend_kernel_name_counts[
|
| 298 |
+
expected_backend_op_names[native_f.func.name]
|
| 299 |
+
].append(native_f)
|
| 300 |
+
|
| 301 |
+
# This just looks for lines containing "foo(", and assumes that the kernel foo has been implemented.
|
| 302 |
+
# It might cause false negatives (we won't catch all cases), but that's ok - if we catch a missing kernel
|
| 303 |
+
# here, then we get a nicer error message. If we miss it, you get a linker error.
|
| 304 |
+
kernel_defn_regex = rf"(.*){class_name}::\s*([\w\d]*)\("
|
| 305 |
+
actual_backend_kernel_name_counts = Counter(
|
| 306 |
+
# A bit unwieldy (this could probably be moved into regex),
|
| 307 |
+
# but we don't want to include kernel names that come from function calls,
|
| 308 |
+
# like "return torch_xla::XLANativeFunctions::empty_strided_symint(...)".
|
| 309 |
+
# Easy check is to ignore any lines with colons before the class name.
|
| 310 |
+
[
|
| 311 |
+
y
|
| 312 |
+
for (x, y) in re.findall(kernel_defn_regex, backend_defns)
|
| 313 |
+
if not x.endswith(":")
|
| 314 |
+
]
|
| 315 |
+
)
|
| 316 |
+
|
| 317 |
+
missing_kernels_err_msg = ""
|
| 318 |
+
for expected_name, funcs in expected_backend_kernel_name_counts.items():
|
| 319 |
+
expected_overload_count = len(funcs)
|
| 320 |
+
actual_overload_count = actual_backend_kernel_name_counts[expected_name]
|
| 321 |
+
if expected_overload_count != actual_overload_count:
|
| 322 |
+
|
| 323 |
+
def create_decl(f: NativeFunction) -> str:
|
| 324 |
+
with native_function_manager(f):
|
| 325 |
+
return DispatcherSignature.from_schema(f.func).decl()
|
| 326 |
+
|
| 327 |
+
expected_schemas_str = "\n".join([create_decl(f) for f in funcs])
|
| 328 |
+
missing_kernels_err_msg += f"""
|
| 329 |
+
{class_name} is missing a kernel definition for {expected_name}. We found {actual_overload_count} kernel(s) with that name,
|
| 330 |
+
but expected {expected_overload_count} kernel(s). The expected function schemas for the missing operator are:
|
| 331 |
+
{expected_schemas_str}
|
| 332 |
+
|
| 333 |
+
"""
|
| 334 |
+
assert missing_kernels_err_msg == "", missing_kernels_err_msg
|
| 335 |
+
|
| 336 |
+
|
| 337 |
+
def main() -> None:
|
| 338 |
+
parser = argparse.ArgumentParser(description="Generate backend stub files")
|
| 339 |
+
parser.add_argument(
|
| 340 |
+
"-s",
|
| 341 |
+
"--source-yaml",
|
| 342 |
+
"--source_yaml",
|
| 343 |
+
help="path to source yaml file containing operator external definitions",
|
| 344 |
+
)
|
| 345 |
+
parser.add_argument("-o", "--output-dir", "--output_dir", help="output directory")
|
| 346 |
+
parser.add_argument(
|
| 347 |
+
"--dry-run", "--dry_run", type=bool, default=False, help="output directory"
|
| 348 |
+
)
|
| 349 |
+
parser.add_argument(
|
| 350 |
+
"--impl-path",
|
| 351 |
+
"--impl_path",
|
| 352 |
+
type=str,
|
| 353 |
+
default=None,
|
| 354 |
+
help="path to the source C++ file containing kernel definitions",
|
| 355 |
+
)
|
| 356 |
+
options = parser.parse_args()
|
| 357 |
+
|
| 358 |
+
run(options.source_yaml, options.output_dir, options.dry_run, options.impl_path)
|
| 359 |
+
|
| 360 |
+
|
| 361 |
+
def gen_dispatchkey_nativefunc_headers(
|
| 362 |
+
fm: FileManager,
|
| 363 |
+
class_name: str,
|
| 364 |
+
cpp_namespace: str,
|
| 365 |
+
backend_indices: dict[DispatchKey, BackendIndex],
|
| 366 |
+
grouped_native_functions: Sequence[NativeFunction | NativeFunctionsGroup],
|
| 367 |
+
backend_dispatch_key: DispatchKey,
|
| 368 |
+
autograd_dispatch_key: DispatchKey | None,
|
| 369 |
+
backend_name: str = "",
|
| 370 |
+
) -> None:
|
| 371 |
+
assert class_name is not None
|
| 372 |
+
generated_comment = (
|
| 373 |
+
"Autogenerated file by gen_backend_stubs.py. Do not edit directly!"
|
| 374 |
+
)
|
| 375 |
+
|
| 376 |
+
# Convert to a set first to remove duplicate kernel names.
|
| 377 |
+
# Backends are allowed to repeat kernel names; only generate the declaration once!
|
| 378 |
+
# Sort for deterministic output.
|
| 379 |
+
backend_declarations = sorted(
|
| 380 |
+
set(
|
| 381 |
+
concatMap(
|
| 382 |
+
lambda f: dest.compute_native_function_declaration(
|
| 383 |
+
f, backend_indices[backend_dispatch_key]
|
| 384 |
+
),
|
| 385 |
+
grouped_native_functions,
|
| 386 |
+
)
|
| 387 |
+
)
|
| 388 |
+
)
|
| 389 |
+
autograd_declarations = sorted(
|
| 390 |
+
set(
|
| 391 |
+
concatMap(
|
| 392 |
+
lambda f: []
|
| 393 |
+
if autograd_dispatch_key is None
|
| 394 |
+
else dest.compute_native_function_declaration(
|
| 395 |
+
f, backend_indices[autograd_dispatch_key]
|
| 396 |
+
),
|
| 397 |
+
grouped_native_functions,
|
| 398 |
+
)
|
| 399 |
+
)
|
| 400 |
+
)
|
| 401 |
+
|
| 402 |
+
ns_helper = NamespaceHelper(cpp_namespace)
|
| 403 |
+
fm.write_with_template(
|
| 404 |
+
f"{backend_dispatch_key}NativeFunctions.h",
|
| 405 |
+
"DispatchKeyNativeFunctions.h",
|
| 406 |
+
lambda: {
|
| 407 |
+
"generated_comment": generated_comment,
|
| 408 |
+
"namespace_prologue": ns_helper.prologue,
|
| 409 |
+
"class_name": class_name,
|
| 410 |
+
"namespace_epilogue": ns_helper.epilogue,
|
| 411 |
+
"dispatch_declarations": backend_declarations + autograd_declarations,
|
| 412 |
+
"BackendName": backend_name,
|
| 413 |
+
"DispatchKey": backend_dispatch_key,
|
| 414 |
+
},
|
| 415 |
+
)
|
| 416 |
+
|
| 417 |
+
|
| 418 |
+
def gen_dispatcher_registrations(
|
| 419 |
+
fm: FileManager,
|
| 420 |
+
output_dir: str,
|
| 421 |
+
class_name: str,
|
| 422 |
+
backend_indices: dict[DispatchKey, BackendIndex],
|
| 423 |
+
grouped_native_functions: Sequence[NativeFunction | NativeFunctionsGroup],
|
| 424 |
+
backend_dispatch_key: DispatchKey,
|
| 425 |
+
dispatch_key: DispatchKey,
|
| 426 |
+
selector: SelectiveBuilder,
|
| 427 |
+
# build_in_tree is true for lazy TS backend and affects include paths, not used for external backends
|
| 428 |
+
build_in_tree: bool = False,
|
| 429 |
+
per_operator_headers: bool = False,
|
| 430 |
+
backend_name: str = "",
|
| 431 |
+
eager_registration: bool = True,
|
| 432 |
+
) -> None:
|
| 433 |
+
headers = [
|
| 434 |
+
f"{output_dir}/{backend_dispatch_key}NativeFunctions.h",
|
| 435 |
+
]
|
| 436 |
+
if build_in_tree:
|
| 437 |
+
external_backend_headers_str = "\n".join(f"#include <{h}>" for h in headers)
|
| 438 |
+
else:
|
| 439 |
+
external_backend_headers_str = "\n".join(f'#include "{h}"' for h in headers)
|
| 440 |
+
|
| 441 |
+
assert class_name is not None
|
| 442 |
+
backend_index = backend_indices[dispatch_key]
|
| 443 |
+
|
| 444 |
+
dispatch_registrations_body = list(
|
| 445 |
+
concatMap(
|
| 446 |
+
dest.RegisterDispatchKey(
|
| 447 |
+
backend_index,
|
| 448 |
+
Target.REGISTRATION,
|
| 449 |
+
selector,
|
| 450 |
+
rocm=False,
|
| 451 |
+
symint=True,
|
| 452 |
+
class_method_name=f"{class_name}",
|
| 453 |
+
skip_dispatcher_op_registration=False,
|
| 454 |
+
),
|
| 455 |
+
grouped_native_functions,
|
| 456 |
+
)
|
| 457 |
+
)
|
| 458 |
+
newline = "\n"
|
| 459 |
+
ns_helper = NamespaceHelper(namespace_str="at")
|
| 460 |
+
deferred_dispatch_registrations = ""
|
| 461 |
+
static_init_dispatch_registrations = ""
|
| 462 |
+
if eager_registration:
|
| 463 |
+
static_template = CodeTemplate(
|
| 464 |
+
"""\
|
| 465 |
+
TORCH_LIBRARY_IMPL(aten, $dispatch_key, m) {
|
| 466 |
+
$dispatch_registrations_body
|
| 467 |
+
}"""
|
| 468 |
+
)
|
| 469 |
+
static_init_dispatch_registrations = static_template.substitute(
|
| 470 |
+
dispatch_key=dispatch_key,
|
| 471 |
+
dispatch_registrations_body=dispatch_registrations_body,
|
| 472 |
+
)
|
| 473 |
+
else:
|
| 474 |
+
deferred_template = CodeTemplate(
|
| 475 |
+
"""\
|
| 476 |
+
TORCH_API void Register${backend_name}${dispatch_key}NativeFunctions();
|
| 477 |
+
TORCH_API void Register${backend_name}${dispatch_key}NativeFunctions() {
|
| 478 |
+
static auto m = MAKE_TORCH_LIBRARY_IMPL(aten, $dispatch_key);
|
| 479 |
+
$dispatch_registrations_body
|
| 480 |
+
}"""
|
| 481 |
+
)
|
| 482 |
+
deferred_dispatch_registrations = deferred_template.substitute(
|
| 483 |
+
backend_name=backend_name,
|
| 484 |
+
dispatch_key=dispatch_key,
|
| 485 |
+
dispatch_registrations_body=dispatch_registrations_body,
|
| 486 |
+
)
|
| 487 |
+
|
| 488 |
+
fm.write_with_template(
|
| 489 |
+
f"Register{dispatch_key}.cpp",
|
| 490 |
+
"RegisterDispatchKey.cpp",
|
| 491 |
+
lambda: {
|
| 492 |
+
"extra_cuda_headers": "",
|
| 493 |
+
"external_backend_headers": external_backend_headers_str,
|
| 494 |
+
"ops_headers": "#include <ATen/Functions.h>"
|
| 495 |
+
if not per_operator_headers
|
| 496 |
+
else "",
|
| 497 |
+
"DispatchKey": dispatch_key,
|
| 498 |
+
"dispatch_namespace": dispatch_key.lower(),
|
| 499 |
+
"dispatch_headers": dest.gen_registration_headers(
|
| 500 |
+
backend_index, per_operator_headers=per_operator_headers, rocm=False
|
| 501 |
+
),
|
| 502 |
+
"dispatch_definitions": fm.substitute_with_template(
|
| 503 |
+
"RegisterDispatchDefinitions.ini",
|
| 504 |
+
lambda: {
|
| 505 |
+
"ns_prologue": ns_helper.prologue,
|
| 506 |
+
"ns_epilogue": ns_helper.epilogue,
|
| 507 |
+
"static_init_dispatch_registrations": static_init_dispatch_registrations,
|
| 508 |
+
"deferred_dispatch_registrations": deferred_dispatch_registrations,
|
| 509 |
+
"dispatch_helpers": dest.gen_registration_helpers(backend_index),
|
| 510 |
+
"dispatch_namespace": dispatch_key.lower(),
|
| 511 |
+
"dispatch_namespaced_definitions": "",
|
| 512 |
+
"dispatch_anonymous_definitions": list(
|
| 513 |
+
concatMap(
|
| 514 |
+
dest.RegisterDispatchKey(
|
| 515 |
+
backend_index,
|
| 516 |
+
Target.ANONYMOUS_DEFINITION,
|
| 517 |
+
selector,
|
| 518 |
+
rocm=False,
|
| 519 |
+
symint=True,
|
| 520 |
+
class_method_name=f"{class_name}",
|
| 521 |
+
skip_dispatcher_op_registration=False,
|
| 522 |
+
),
|
| 523 |
+
grouped_native_functions,
|
| 524 |
+
)
|
| 525 |
+
),
|
| 526 |
+
},
|
| 527 |
+
).split(newline),
|
| 528 |
+
},
|
| 529 |
+
)
|
| 530 |
+
|
| 531 |
+
|
| 532 |
+
def run(
|
| 533 |
+
source_yaml: str, output_dir: str, dry_run: bool, impl_path: str | None = None
|
| 534 |
+
) -> None:
|
| 535 |
+
# Assumes that this file lives at PYTORCH_ROOT/torchgen/gen_backend_stubs.py
|
| 536 |
+
pytorch_root = Path(__file__).parent.parent.absolute()
|
| 537 |
+
template_dir = os.path.join(pytorch_root, "aten/src/ATen/templates")
|
| 538 |
+
|
| 539 |
+
def make_file_manager(install_dir: str) -> FileManager:
|
| 540 |
+
return FileManager(
|
| 541 |
+
install_dir=install_dir, template_dir=template_dir, dry_run=dry_run
|
| 542 |
+
)
|
| 543 |
+
|
| 544 |
+
fm = make_file_manager(output_dir)
|
| 545 |
+
|
| 546 |
+
native_yaml_path = os.path.join(
|
| 547 |
+
pytorch_root, "aten/src/ATen/native/native_functions.yaml"
|
| 548 |
+
)
|
| 549 |
+
tags_yaml_path = os.path.join(pytorch_root, "aten/src/ATen/native/tags.yaml")
|
| 550 |
+
parsed_yaml = parse_native_yaml(native_yaml_path, tags_yaml_path)
|
| 551 |
+
native_functions, backend_indices = (
|
| 552 |
+
parsed_yaml.native_functions,
|
| 553 |
+
parsed_yaml.backend_indices,
|
| 554 |
+
)
|
| 555 |
+
grouped_native_functions = get_grouped_native_functions(native_functions)
|
| 556 |
+
parsed_backend_yaml = parse_backend_yaml(
|
| 557 |
+
source_yaml, grouped_native_functions, backend_indices
|
| 558 |
+
)
|
| 559 |
+
backend_key = parsed_backend_yaml.backend_key
|
| 560 |
+
autograd_key = parsed_backend_yaml.autograd_key
|
| 561 |
+
cpp_namespace = parsed_backend_yaml.cpp_namespace
|
| 562 |
+
class_name = parsed_backend_yaml.class_name
|
| 563 |
+
backend_indices = parsed_backend_yaml.backend_indices
|
| 564 |
+
|
| 565 |
+
selector = SelectiveBuilder.get_nop_selector()
|
| 566 |
+
|
| 567 |
+
if backend_key is None:
|
| 568 |
+
# This could be useful if a backend wants to quickly set up a noop yaml file but doesn't have any kernels ready yet.
|
| 569 |
+
return
|
| 570 |
+
|
| 571 |
+
if class_name is None:
|
| 572 |
+
# class_name is an optional argument to backend yaml file.
|
| 573 |
+
# if specified it allows an external backend to override
|
| 574 |
+
# the name of the class that all generated kernel definitions live under.
|
| 575 |
+
# if not specified, its value is given as native_function_class_name.
|
| 576 |
+
class_name = backend_indices[backend_key].native_function_class_name()
|
| 577 |
+
assert class_name is not None
|
| 578 |
+
|
| 579 |
+
if impl_path is not None:
|
| 580 |
+
error_on_missing_kernels(
|
| 581 |
+
native_functions,
|
| 582 |
+
backend_indices,
|
| 583 |
+
backend_key,
|
| 584 |
+
autograd_key,
|
| 585 |
+
class_name,
|
| 586 |
+
impl_path,
|
| 587 |
+
)
|
| 588 |
+
|
| 589 |
+
gen_dispatchkey_nativefunc_headers(
|
| 590 |
+
fm,
|
| 591 |
+
class_name,
|
| 592 |
+
cpp_namespace,
|
| 593 |
+
backend_indices,
|
| 594 |
+
grouped_native_functions,
|
| 595 |
+
backend_key,
|
| 596 |
+
autograd_key,
|
| 597 |
+
)
|
| 598 |
+
|
| 599 |
+
for dispatch_key in (
|
| 600 |
+
[backend_key] if autograd_key is None else [backend_key, autograd_key]
|
| 601 |
+
):
|
| 602 |
+
gen_dispatcher_registrations(
|
| 603 |
+
fm,
|
| 604 |
+
output_dir,
|
| 605 |
+
class_name,
|
| 606 |
+
backend_indices,
|
| 607 |
+
grouped_native_functions,
|
| 608 |
+
backend_key,
|
| 609 |
+
dispatch_key,
|
| 610 |
+
selector,
|
| 611 |
+
)
|
| 612 |
+
|
| 613 |
+
|
| 614 |
+
if __name__ == "__main__":
|
| 615 |
+
main()
|
gen_executorch.py
ADDED
|
@@ -0,0 +1,1000 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
import argparse
|
| 4 |
+
import os
|
| 5 |
+
from collections import defaultdict
|
| 6 |
+
from dataclasses import dataclass
|
| 7 |
+
from pathlib import Path
|
| 8 |
+
from typing import Any, Callable, TextIO, TYPE_CHECKING
|
| 9 |
+
|
| 10 |
+
import yaml
|
| 11 |
+
|
| 12 |
+
# Parse native_functions.yaml into a sequence of NativeFunctions and Backend Indices.
|
| 13 |
+
from torchgen import dest
|
| 14 |
+
from torchgen.api import cpp as aten_cpp
|
| 15 |
+
from torchgen.api.types import CppSignature, CppSignatureGroup, CType, NamedCType
|
| 16 |
+
from torchgen.context import (
|
| 17 |
+
method_with_native_function,
|
| 18 |
+
method_with_nested_native_function,
|
| 19 |
+
with_native_function_and_index,
|
| 20 |
+
)
|
| 21 |
+
from torchgen.executorch.api import et_cpp
|
| 22 |
+
from torchgen.executorch.api.custom_ops import (
|
| 23 |
+
ComputeNativeFunctionStub,
|
| 24 |
+
gen_custom_ops_registration,
|
| 25 |
+
)
|
| 26 |
+
from torchgen.executorch.api.types import contextArg, ExecutorchCppSignature
|
| 27 |
+
from torchgen.executorch.api.unboxing import Unboxing
|
| 28 |
+
from torchgen.executorch.model import ETKernelIndex, ETKernelKey, ETParsedYaml
|
| 29 |
+
from torchgen.executorch.parse import ET_FIELDS, parse_et_yaml, parse_et_yaml_struct
|
| 30 |
+
from torchgen.gen import (
|
| 31 |
+
get_custom_build_selector,
|
| 32 |
+
get_native_function_declarations,
|
| 33 |
+
get_native_function_declarations_from_ns_grouped_kernels,
|
| 34 |
+
get_native_function_schema_registrations,
|
| 35 |
+
LineLoader,
|
| 36 |
+
parse_native_yaml,
|
| 37 |
+
)
|
| 38 |
+
from torchgen.model import (
|
| 39 |
+
BackendIndex,
|
| 40 |
+
BackendMetadata,
|
| 41 |
+
DEFAULT_KERNEL_NAMESPACE,
|
| 42 |
+
DispatchKey,
|
| 43 |
+
FunctionSchema,
|
| 44 |
+
Location,
|
| 45 |
+
NativeFunction,
|
| 46 |
+
NativeFunctionsGroup,
|
| 47 |
+
OperatorName,
|
| 48 |
+
Variant,
|
| 49 |
+
)
|
| 50 |
+
from torchgen.utils import (
|
| 51 |
+
context,
|
| 52 |
+
FileManager,
|
| 53 |
+
make_file_manager,
|
| 54 |
+
mapMaybe,
|
| 55 |
+
NamespaceHelper,
|
| 56 |
+
)
|
| 57 |
+
|
| 58 |
+
|
| 59 |
+
if TYPE_CHECKING:
|
| 60 |
+
from collections.abc import Sequence
|
| 61 |
+
|
| 62 |
+
from torchgen.selective_build.selector import SelectiveBuilder
|
| 63 |
+
|
| 64 |
+
|
| 65 |
+
def _sig_decl_wrapper(sig: CppSignature | ExecutorchCppSignature) -> str:
|
| 66 |
+
"""
|
| 67 |
+
A wrapper function to basically get `sig.decl(include_context=True)`.
|
| 68 |
+
For ATen kernel, the codegen has no idea about ET contextArg, so we
|
| 69 |
+
use this wrapper to add it.
|
| 70 |
+
"""
|
| 71 |
+
if isinstance(sig, ExecutorchCppSignature):
|
| 72 |
+
return sig.decl()
|
| 73 |
+
|
| 74 |
+
returns_type = aten_cpp.returns_type(sig.func.returns).cpp_type()
|
| 75 |
+
cpp_args = [a.decl() for a in sig.arguments()]
|
| 76 |
+
cpp_args_str = ", ".join([contextArg.decl()] + cpp_args)
|
| 77 |
+
sig_decl = f"{returns_type} {sig.name()}({cpp_args_str})"
|
| 78 |
+
return sig_decl
|
| 79 |
+
|
| 80 |
+
|
| 81 |
+
def static_dispatch(
|
| 82 |
+
sig: CppSignature | ExecutorchCppSignature,
|
| 83 |
+
f: NativeFunction,
|
| 84 |
+
backend_indices: list[BackendIndex],
|
| 85 |
+
) -> str:
|
| 86 |
+
"""
|
| 87 |
+
For a given `NativeFunction`, find out the corresponding native function and dispatch to it. If zero or more than one
|
| 88 |
+
native function exists, error out. A simplified version of register_dispatch_key.py
|
| 89 |
+
Arguments:
|
| 90 |
+
sig: A CppSignature for this native function we want to use.
|
| 91 |
+
f: NativeFunction to generate static dispatch.
|
| 92 |
+
backend_indices: All available backends.
|
| 93 |
+
Return:
|
| 94 |
+
C++ code to call backend-specific functions, e.g., "return at::native::add(self, other, scale);"
|
| 95 |
+
"""
|
| 96 |
+
if len(backend_indices) == 0 or f.manual_kernel_registration:
|
| 97 |
+
return ""
|
| 98 |
+
|
| 99 |
+
backends = [b for b in backend_indices if b.has_kernel(f)]
|
| 100 |
+
static_block = None
|
| 101 |
+
if len(backends) == 1:
|
| 102 |
+
backend_metadata = backends[0].get_kernel(f)
|
| 103 |
+
if backend_metadata:
|
| 104 |
+
args = ", ".join(a.name for a in sig.arguments())
|
| 105 |
+
# Here we are assuming there's no difference between CppSignature and NativeSignature for Executorch.
|
| 106 |
+
static_block = f"return ::{backend_metadata.cpp_namespace}::{backend_metadata.kernel}({args});"
|
| 107 |
+
else:
|
| 108 |
+
static_block = f"""
|
| 109 |
+
ET_ASSERT_UNREACHABLE_MSG("The number of native function(s) binding to {f.func.name} is {len(backends)}.");
|
| 110 |
+
"""
|
| 111 |
+
return f"""
|
| 112 |
+
// {f.namespace}::{f.func}
|
| 113 |
+
TORCH_API inline {_sig_decl_wrapper(sig)} {{
|
| 114 |
+
{static_block}
|
| 115 |
+
}}
|
| 116 |
+
"""
|
| 117 |
+
|
| 118 |
+
|
| 119 |
+
# Generates Functions.h, which provides the functional public C++ API,
|
| 120 |
+
# and the scaffolding to call into the dispatcher from these functions.
|
| 121 |
+
@dataclass(frozen=True)
|
| 122 |
+
class ComputeFunction:
|
| 123 |
+
static_dispatch_backend_indices: list[BackendIndex]
|
| 124 |
+
|
| 125 |
+
selector: SelectiveBuilder
|
| 126 |
+
|
| 127 |
+
use_aten_lib: bool
|
| 128 |
+
|
| 129 |
+
is_custom_op: Callable[[NativeFunction], bool]
|
| 130 |
+
|
| 131 |
+
@method_with_native_function
|
| 132 |
+
def __call__(self, f: NativeFunction) -> str | None:
|
| 133 |
+
is_method_variant = False
|
| 134 |
+
if not self.selector.is_root_operator(f"{f.namespace}::{f.func.name}"):
|
| 135 |
+
return None
|
| 136 |
+
|
| 137 |
+
if Variant.function not in f.variants and Variant.method in f.variants:
|
| 138 |
+
is_method_variant = True
|
| 139 |
+
|
| 140 |
+
# only valid remaining case is only function is in f.variants
|
| 141 |
+
elif not (Variant.function in f.variants and Variant.method not in f.variants):
|
| 142 |
+
raise Exception( # noqa: TRY002
|
| 143 |
+
f"Can't handle native function {f.func} with the following variant specification {f.variants}."
|
| 144 |
+
)
|
| 145 |
+
|
| 146 |
+
sig: CppSignature | ExecutorchCppSignature = (
|
| 147 |
+
CppSignatureGroup.from_native_function(
|
| 148 |
+
f, method=False, fallback_binding=f.manual_cpp_binding
|
| 149 |
+
).most_faithful_signature()
|
| 150 |
+
if self.use_aten_lib
|
| 151 |
+
else ExecutorchCppSignature.from_native_function(f)
|
| 152 |
+
)
|
| 153 |
+
if self.use_aten_lib and not self.is_custom_op(f):
|
| 154 |
+
comma = ", "
|
| 155 |
+
|
| 156 |
+
if is_method_variant:
|
| 157 |
+
return f"""
|
| 158 |
+
// {f.namespace}::{f.func}
|
| 159 |
+
TORCH_API inline {_sig_decl_wrapper(sig)} {{
|
| 160 |
+
return {sig.arguments()[0].name}.{sig.name()}({comma.join(e.name for e in sig.arguments()[1:])});
|
| 161 |
+
}}
|
| 162 |
+
"""
|
| 163 |
+
else:
|
| 164 |
+
return f"""
|
| 165 |
+
// {f.namespace}::{f.func}
|
| 166 |
+
TORCH_API inline {_sig_decl_wrapper(sig)} {{
|
| 167 |
+
return at::{sig.name()}({comma.join(e.name for e in sig.arguments())});
|
| 168 |
+
}}
|
| 169 |
+
"""
|
| 170 |
+
|
| 171 |
+
else:
|
| 172 |
+
return static_dispatch(
|
| 173 |
+
sig,
|
| 174 |
+
f,
|
| 175 |
+
backend_indices=self.static_dispatch_backend_indices,
|
| 176 |
+
)
|
| 177 |
+
|
| 178 |
+
|
| 179 |
+
# Generates RegisterCodegenUnboxedKernels.cpp.
|
| 180 |
+
@dataclass(frozen=True)
|
| 181 |
+
class ComputeCodegenUnboxedKernels:
|
| 182 |
+
selector: SelectiveBuilder
|
| 183 |
+
|
| 184 |
+
use_aten_lib: bool
|
| 185 |
+
|
| 186 |
+
@method_with_nested_native_function
|
| 187 |
+
def __call__(
|
| 188 |
+
self,
|
| 189 |
+
unbox_kernel_entry: tuple[NativeFunction, tuple[ETKernelKey, BackendMetadata]],
|
| 190 |
+
) -> str:
|
| 191 |
+
f: NativeFunction = unbox_kernel_entry[0]
|
| 192 |
+
kernel_key: ETKernelKey | list[ETKernelKey] = unbox_kernel_entry[1][0]
|
| 193 |
+
kernel_meta: BackendMetadata = unbox_kernel_entry[1][1]
|
| 194 |
+
|
| 195 |
+
op_name = f"{f.namespace}::{f.func.name}"
|
| 196 |
+
if not self.selector.is_root_operator(op_name):
|
| 197 |
+
return ""
|
| 198 |
+
|
| 199 |
+
if not isinstance(kernel_key, list):
|
| 200 |
+
kernel_key = [kernel_key]
|
| 201 |
+
used_kernel_keys = self.selector.et_get_selected_kernels(
|
| 202 |
+
op_name, [k.to_native_string() for k in kernel_key]
|
| 203 |
+
)
|
| 204 |
+
if not used_kernel_keys:
|
| 205 |
+
return ""
|
| 206 |
+
sig: CppSignature | ExecutorchCppSignature
|
| 207 |
+
argument_type_gen: Callable[..., NamedCType]
|
| 208 |
+
return_type_gen: Callable[..., CType]
|
| 209 |
+
if self.use_aten_lib:
|
| 210 |
+
sig = CppSignatureGroup.from_native_function(
|
| 211 |
+
f, method=False, fallback_binding=f.manual_cpp_binding
|
| 212 |
+
).most_faithful_signature()
|
| 213 |
+
argument_type_gen = aten_cpp.argumenttype_type
|
| 214 |
+
return_type_gen = aten_cpp.returns_type
|
| 215 |
+
arguments = sig.arguments()
|
| 216 |
+
kernel_call = f"torch::executor::{f.namespace}::{sig.name()}"
|
| 217 |
+
else:
|
| 218 |
+
sig = ExecutorchCppSignature.from_native_function(f)
|
| 219 |
+
argument_type_gen = et_cpp.argumenttype_type
|
| 220 |
+
return_type_gen = et_cpp.returns_type
|
| 221 |
+
arguments = sig.arguments(include_context=False)
|
| 222 |
+
kernel_call = f"{kernel_meta.cpp_namespace}::{kernel_meta.kernel}"
|
| 223 |
+
# parse arguments into C++ code
|
| 224 |
+
binding_list, code_list = Unboxing(
|
| 225 |
+
argument_type_gen=argument_type_gen
|
| 226 |
+
).convert_arguments(arguments)
|
| 227 |
+
|
| 228 |
+
# for each C++ argument, generate the conversion code
|
| 229 |
+
code_connector = "\n\t"
|
| 230 |
+
arg_connector = ", "
|
| 231 |
+
|
| 232 |
+
args_str = f"{arg_connector.join(e.name for e in binding_list)}"
|
| 233 |
+
event_tracer_output_logging = ""
|
| 234 |
+
output_ids = []
|
| 235 |
+
|
| 236 |
+
if len(f.func.returns) == 0:
|
| 237 |
+
if len(f.func.arguments.out) == 0:
|
| 238 |
+
raise Exception( # noqa: TRY002
|
| 239 |
+
f"Can't handle native function {f.func} with no returns and no out yet."
|
| 240 |
+
)
|
| 241 |
+
out = f.func.arguments.out[0]
|
| 242 |
+
return_assignment = f"""stack[{len(binding_list)}] = &{out.name};"""
|
| 243 |
+
ret_prefix = ""
|
| 244 |
+
output_ids = [len(binding_list)]
|
| 245 |
+
else:
|
| 246 |
+
if len(f.func.arguments.out) == 0:
|
| 247 |
+
return_assignment = (
|
| 248 |
+
f"""*stack[{len(binding_list)}] = EValue(result_);"""
|
| 249 |
+
)
|
| 250 |
+
ret_prefix = return_type_gen(f.func.returns).cpp_type() + " result_ = "
|
| 251 |
+
output_ids = [len(binding_list)]
|
| 252 |
+
else:
|
| 253 |
+
return_assignment = ""
|
| 254 |
+
ret_prefix = ""
|
| 255 |
+
output_ids = [
|
| 256 |
+
len(binding_list) - (i + 1)
|
| 257 |
+
for i in reversed(range(len(f.func.arguments.out)))
|
| 258 |
+
]
|
| 259 |
+
|
| 260 |
+
for output_id in output_ids:
|
| 261 |
+
event_tracer_output_logging += (
|
| 262 |
+
f"internal::event_tracer_log_evalue("
|
| 263 |
+
f"context.internal_event_tracer(), "
|
| 264 |
+
f"*stack[{output_id}]);\n"
|
| 265 |
+
)
|
| 266 |
+
|
| 267 |
+
newline = "\n "
|
| 268 |
+
return "\n".join(
|
| 269 |
+
[
|
| 270 |
+
f"""
|
| 271 |
+
Kernel(
|
| 272 |
+
"{f.namespace}::{f.func.name}",{newline + '"' + (k + '",') if k != 'default' else ''}
|
| 273 |
+
[]({contextArg.defn()}, EValue** stack) {{
|
| 274 |
+
{code_connector.join(code_list)}
|
| 275 |
+
|
| 276 |
+
internal::EventTracerProfileOpScope event_tracer_op_scope(context.internal_event_tracer(), "native_call_{f.func.name}");
|
| 277 |
+
EXECUTORCH_SCOPE_PROF("native_call_{f.func.name}");
|
| 278 |
+
{ret_prefix}{kernel_call}(context, {args_str});
|
| 279 |
+
{event_tracer_output_logging}
|
| 280 |
+
{return_assignment}
|
| 281 |
+
}}
|
| 282 |
+
),
|
| 283 |
+
"""
|
| 284 |
+
for k in used_kernel_keys
|
| 285 |
+
]
|
| 286 |
+
)
|
| 287 |
+
|
| 288 |
+
|
| 289 |
+
def gen_unboxing(
|
| 290 |
+
*,
|
| 291 |
+
native_functions: Sequence[NativeFunction],
|
| 292 |
+
cpu_fm: FileManager,
|
| 293 |
+
selector: SelectiveBuilder,
|
| 294 |
+
use_aten_lib: bool,
|
| 295 |
+
kernel_index: ETKernelIndex,
|
| 296 |
+
manual_registration: bool,
|
| 297 |
+
) -> None:
|
| 298 |
+
# Iterable type for write_sharded is a Tuple of (native_function, (kernel_key, metadata))
|
| 299 |
+
def key_func(
|
| 300 |
+
item: tuple[NativeFunction, tuple[ETKernelKey, BackendMetadata]],
|
| 301 |
+
) -> str:
|
| 302 |
+
return item[0].root_name + ":" + item[1][0].to_native_string()
|
| 303 |
+
|
| 304 |
+
items: list[tuple[NativeFunction, tuple[ETKernelKey, BackendMetadata]]] = [
|
| 305 |
+
(native_function, (kernel_key, metadata))
|
| 306 |
+
for native_function in native_functions
|
| 307 |
+
for kernel_key, metadata in kernel_index.get_kernels(native_function).items()
|
| 308 |
+
]
|
| 309 |
+
|
| 310 |
+
header = ["Functions.h" if use_aten_lib else "NativeFunctions.h"]
|
| 311 |
+
filename = (
|
| 312 |
+
"RegisterKernels.cpp"
|
| 313 |
+
if manual_registration
|
| 314 |
+
else "RegisterCodegenUnboxedKernels.cpp"
|
| 315 |
+
)
|
| 316 |
+
cpu_fm.write_sharded(
|
| 317 |
+
filename,
|
| 318 |
+
items,
|
| 319 |
+
key_fn=key_func,
|
| 320 |
+
env_callable=lambda unbox_kernel_entry: {
|
| 321 |
+
"unboxed_kernels": [
|
| 322 |
+
ComputeCodegenUnboxedKernels(selector, use_aten_lib)(unbox_kernel_entry)
|
| 323 |
+
],
|
| 324 |
+
"fn_header": header
|
| 325 |
+
if unbox_kernel_entry == items[0]
|
| 326 |
+
else [], # Only write header once
|
| 327 |
+
},
|
| 328 |
+
num_shards=1,
|
| 329 |
+
sharded_keys={"unboxed_kernels", "fn_header"},
|
| 330 |
+
)
|
| 331 |
+
|
| 332 |
+
|
| 333 |
+
@with_native_function_and_index # type: ignore[arg-type]
|
| 334 |
+
def compute_native_function_declaration(
|
| 335 |
+
g: NativeFunctionsGroup | NativeFunction, kernel_index: ETKernelIndex
|
| 336 |
+
) -> list[str]:
|
| 337 |
+
assert isinstance(g, NativeFunction)
|
| 338 |
+
sig = ExecutorchCppSignature.from_native_function(f=g)
|
| 339 |
+
metadata_list = kernel_index.get_kernels(g).values()
|
| 340 |
+
if metadata_list is None:
|
| 341 |
+
return []
|
| 342 |
+
|
| 343 |
+
# for kernels in lean mode, we declare two versions, one with context and one without.
|
| 344 |
+
# In the end we will cleanup the unused one.
|
| 345 |
+
def gen_decl(metadata: BackendMetadata, include_context: bool) -> str:
|
| 346 |
+
return f"{sig.decl(name=metadata.kernel, include_context=include_context)};"
|
| 347 |
+
|
| 348 |
+
return [
|
| 349 |
+
gen_decl(metadata, include_context)
|
| 350 |
+
for include_context in [False, True]
|
| 351 |
+
for metadata in metadata_list
|
| 352 |
+
]
|
| 353 |
+
|
| 354 |
+
|
| 355 |
+
def gen_functions_declarations(
|
| 356 |
+
*,
|
| 357 |
+
native_functions: Sequence[NativeFunction],
|
| 358 |
+
kernel_index: ETKernelIndex,
|
| 359 |
+
selector: SelectiveBuilder,
|
| 360 |
+
use_aten_lib: bool,
|
| 361 |
+
custom_ops_native_functions: Sequence[NativeFunction] | None = None,
|
| 362 |
+
) -> str:
|
| 363 |
+
"""
|
| 364 |
+
Generates namespace separated C++ function API inline declaration/definitions.
|
| 365 |
+
Native functions are grouped by namespaces and the generated code is wrapped inside
|
| 366 |
+
namespace blocks.
|
| 367 |
+
|
| 368 |
+
E.g., for `custom_1::foo.out` in yaml file we will generate a C++ API as a symbol
|
| 369 |
+
in `torch::executor::custom_1::foo_out`. This way we avoid symbol conflict when
|
| 370 |
+
the other `custom_2::foo.out` is available.
|
| 371 |
+
"""
|
| 372 |
+
|
| 373 |
+
# convert kernel index to BackendIndex. This is because we can't handle ETKernelIndex yet.
|
| 374 |
+
# TODO larryliu: evaluate if this code is still needed. If yes let it handle ETKernelIndex.
|
| 375 |
+
|
| 376 |
+
backend_index = kernel_index._to_backend_index()
|
| 377 |
+
|
| 378 |
+
ns_grouped_functions = defaultdict(list)
|
| 379 |
+
for native_function in native_functions:
|
| 380 |
+
ns_grouped_functions[native_function.namespace].append(native_function)
|
| 381 |
+
functions_declarations = ""
|
| 382 |
+
newline = "\n"
|
| 383 |
+
for namespace in ns_grouped_functions:
|
| 384 |
+
ns_helper = NamespaceHelper(
|
| 385 |
+
namespace_str=namespace,
|
| 386 |
+
entity_name="",
|
| 387 |
+
max_level=3,
|
| 388 |
+
)
|
| 389 |
+
declarations = list(
|
| 390 |
+
mapMaybe(
|
| 391 |
+
ComputeFunction(
|
| 392 |
+
static_dispatch_backend_indices=[backend_index],
|
| 393 |
+
selector=selector,
|
| 394 |
+
use_aten_lib=use_aten_lib,
|
| 395 |
+
is_custom_op=lambda f: custom_ops_native_functions is not None
|
| 396 |
+
and f in custom_ops_native_functions,
|
| 397 |
+
),
|
| 398 |
+
ns_grouped_functions[namespace],
|
| 399 |
+
)
|
| 400 |
+
)
|
| 401 |
+
functions_declarations += f"""
|
| 402 |
+
{ns_helper.prologue}
|
| 403 |
+
{newline.join(declarations)}
|
| 404 |
+
{ns_helper.epilogue}
|
| 405 |
+
"""
|
| 406 |
+
return functions_declarations
|
| 407 |
+
|
| 408 |
+
|
| 409 |
+
def get_ns_grouped_kernels(
|
| 410 |
+
*,
|
| 411 |
+
native_functions: Sequence[NativeFunction],
|
| 412 |
+
kernel_index: ETKernelIndex,
|
| 413 |
+
native_function_decl_gen: Callable[
|
| 414 |
+
[
|
| 415 |
+
NativeFunctionsGroup | NativeFunction,
|
| 416 |
+
ETKernelIndex,
|
| 417 |
+
],
|
| 418 |
+
list[str],
|
| 419 |
+
],
|
| 420 |
+
) -> dict[str, list[str]]:
|
| 421 |
+
ns_grouped_kernels: dict[str, list[str]] = defaultdict(list)
|
| 422 |
+
for f in native_functions:
|
| 423 |
+
native_function_namespaces = set()
|
| 424 |
+
op_kernels = kernel_index.get_kernels(f)
|
| 425 |
+
for backend_metadata in op_kernels.values():
|
| 426 |
+
if backend_metadata:
|
| 427 |
+
namespace = backend_metadata.cpp_namespace
|
| 428 |
+
native_function_namespaces.add(namespace)
|
| 429 |
+
else:
|
| 430 |
+
namespace = DEFAULT_KERNEL_NAMESPACE
|
| 431 |
+
assert (
|
| 432 |
+
len(native_function_namespaces) <= 1
|
| 433 |
+
), f"Codegen only supports one namespace per operator, got {native_function_namespaces}"
|
| 434 |
+
ns_grouped_kernels[namespace].extend(
|
| 435 |
+
native_function_decl_gen(f, kernel_index)
|
| 436 |
+
)
|
| 437 |
+
return ns_grouped_kernels
|
| 438 |
+
|
| 439 |
+
|
| 440 |
+
def gen_headers(
|
| 441 |
+
*,
|
| 442 |
+
native_functions: Sequence[NativeFunction],
|
| 443 |
+
gen_custom_ops_header: bool,
|
| 444 |
+
custom_ops_native_functions: Sequence[NativeFunction],
|
| 445 |
+
selector: SelectiveBuilder,
|
| 446 |
+
kernel_index: ETKernelIndex,
|
| 447 |
+
cpu_fm: FileManager,
|
| 448 |
+
use_aten_lib: bool,
|
| 449 |
+
) -> None:
|
| 450 |
+
"""Generate headers.
|
| 451 |
+
|
| 452 |
+
Args:
|
| 453 |
+
native_functions (Sequence[NativeFunction]): a collection of NativeFunction for ATen ops.
|
| 454 |
+
gen_custom_ops_header (bool): whether we should generate CustomOpsNativeFunctions.h
|
| 455 |
+
custom_ops_native_functions (Sequence[NativeFunction]): a collection of NativeFunction for custom ops.
|
| 456 |
+
kernel_index (ETKernelIndex): kernel collection
|
| 457 |
+
cpu_fm (FileManager): file manager manages output stream
|
| 458 |
+
use_aten_lib (bool): whether we are generating for PyTorch types or Executorch types.
|
| 459 |
+
"""
|
| 460 |
+
aten_headers = ["#include <ATen/Functions.h>"]
|
| 461 |
+
backend_indices = {DispatchKey.CPU: kernel_index._to_backend_index()}
|
| 462 |
+
if gen_custom_ops_header:
|
| 463 |
+
cpu_fm.write_with_template(
|
| 464 |
+
"CustomOpsNativeFunctions.h",
|
| 465 |
+
"NativeFunctions.h",
|
| 466 |
+
lambda: {
|
| 467 |
+
"nativeFunctions_declarations": get_native_function_declarations(
|
| 468 |
+
grouped_native_functions=custom_ops_native_functions,
|
| 469 |
+
backend_indices=backend_indices,
|
| 470 |
+
native_function_decl_gen=dest.compute_native_function_declaration,
|
| 471 |
+
),
|
| 472 |
+
"headers": [
|
| 473 |
+
"#include <ATen/ATen.h>",
|
| 474 |
+
"#include <torch/torch.h>",
|
| 475 |
+
],
|
| 476 |
+
},
|
| 477 |
+
)
|
| 478 |
+
aten_headers.append('#include "CustomOpsNativeFunctions.h"')
|
| 479 |
+
cpu_fm.write(
|
| 480 |
+
"Functions.h",
|
| 481 |
+
lambda: {
|
| 482 |
+
"static_dispatch_extra_headers": aten_headers
|
| 483 |
+
if use_aten_lib
|
| 484 |
+
else ['#include "NativeFunctions.h"'],
|
| 485 |
+
"Functions_declarations": gen_functions_declarations(
|
| 486 |
+
native_functions=native_functions,
|
| 487 |
+
kernel_index=kernel_index,
|
| 488 |
+
selector=selector,
|
| 489 |
+
use_aten_lib=use_aten_lib,
|
| 490 |
+
custom_ops_native_functions=custom_ops_native_functions,
|
| 491 |
+
),
|
| 492 |
+
},
|
| 493 |
+
)
|
| 494 |
+
cpu_fm.write(
|
| 495 |
+
"RegisterKernels.h",
|
| 496 |
+
lambda: {
|
| 497 |
+
"generated_comment": "@" + "generated by torchgen/gen_executorch.py",
|
| 498 |
+
},
|
| 499 |
+
)
|
| 500 |
+
headers = {
|
| 501 |
+
"headers": [
|
| 502 |
+
"#include <executorch/runtime/core/exec_aten/exec_aten.h> // at::Tensor etc.",
|
| 503 |
+
"#include <executorch/runtime/kernel/kernel_runtime_context.h>",
|
| 504 |
+
],
|
| 505 |
+
}
|
| 506 |
+
if use_aten_lib:
|
| 507 |
+
headers["headers"].append("#include <executorch/codegen/macros.h> // TORCH_API")
|
| 508 |
+
cpu_fm.write(
|
| 509 |
+
"NativeFunctions.h",
|
| 510 |
+
lambda: dict(
|
| 511 |
+
{
|
| 512 |
+
"nativeFunctions_declarations": get_native_function_declarations(
|
| 513 |
+
grouped_native_functions=native_functions,
|
| 514 |
+
backend_indices=backend_indices,
|
| 515 |
+
native_function_decl_gen=dest.compute_native_function_declaration,
|
| 516 |
+
),
|
| 517 |
+
},
|
| 518 |
+
**headers,
|
| 519 |
+
),
|
| 520 |
+
)
|
| 521 |
+
else:
|
| 522 |
+
ns_grouped_kernels = get_ns_grouped_kernels(
|
| 523 |
+
native_functions=native_functions,
|
| 524 |
+
kernel_index=kernel_index,
|
| 525 |
+
native_function_decl_gen=compute_native_function_declaration, # type: ignore[arg-type]
|
| 526 |
+
)
|
| 527 |
+
cpu_fm.write(
|
| 528 |
+
"NativeFunctions.h",
|
| 529 |
+
lambda: dict(
|
| 530 |
+
{
|
| 531 |
+
"nativeFunctions_declarations": get_native_function_declarations_from_ns_grouped_kernels(
|
| 532 |
+
ns_grouped_kernels=ns_grouped_kernels,
|
| 533 |
+
),
|
| 534 |
+
},
|
| 535 |
+
**headers,
|
| 536 |
+
),
|
| 537 |
+
)
|
| 538 |
+
|
| 539 |
+
|
| 540 |
+
def gen_custom_ops(
|
| 541 |
+
*,
|
| 542 |
+
native_functions: Sequence[NativeFunction],
|
| 543 |
+
selector: SelectiveBuilder,
|
| 544 |
+
kernel_index: ETKernelIndex,
|
| 545 |
+
cpu_fm: FileManager,
|
| 546 |
+
rocm: bool,
|
| 547 |
+
) -> None:
|
| 548 |
+
dispatch_key = DispatchKey.CPU
|
| 549 |
+
(
|
| 550 |
+
anonymous_definition,
|
| 551 |
+
static_init_dispatch_registrations,
|
| 552 |
+
) = gen_custom_ops_registration(
|
| 553 |
+
native_functions=native_functions,
|
| 554 |
+
selector=selector,
|
| 555 |
+
kernel_index=kernel_index,
|
| 556 |
+
rocm=rocm,
|
| 557 |
+
)
|
| 558 |
+
cpu_fm.write_with_template(
|
| 559 |
+
f"Register{dispatch_key}CustomOps.cpp",
|
| 560 |
+
"RegisterDispatchKeyCustomOps.cpp",
|
| 561 |
+
lambda: {
|
| 562 |
+
"ops_headers": '#include "CustomOpsNativeFunctions.h"',
|
| 563 |
+
"DispatchKey": dispatch_key,
|
| 564 |
+
"dispatch_namespace": dispatch_key.lower(),
|
| 565 |
+
"dispatch_namespaced_definitions": "",
|
| 566 |
+
"dispatch_anonymous_definitions": anonymous_definition,
|
| 567 |
+
"static_init_dispatch_registrations": static_init_dispatch_registrations,
|
| 568 |
+
},
|
| 569 |
+
)
|
| 570 |
+
cpu_fm.write_with_template(
|
| 571 |
+
f"Register{dispatch_key}Stub.cpp",
|
| 572 |
+
"RegisterDispatchKeyCustomOps.cpp",
|
| 573 |
+
lambda: {
|
| 574 |
+
"ops_headers": "",
|
| 575 |
+
"DispatchKey": dispatch_key,
|
| 576 |
+
"dispatch_namespace": dispatch_key.lower(),
|
| 577 |
+
"dispatch_namespaced_definitions": "",
|
| 578 |
+
"dispatch_anonymous_definitions": list(
|
| 579 |
+
mapMaybe(ComputeNativeFunctionStub(), native_functions)
|
| 580 |
+
),
|
| 581 |
+
"static_init_dispatch_registrations": static_init_dispatch_registrations,
|
| 582 |
+
},
|
| 583 |
+
)
|
| 584 |
+
|
| 585 |
+
(
|
| 586 |
+
aten_schema_registrations,
|
| 587 |
+
schema_registrations,
|
| 588 |
+
) = get_native_function_schema_registrations(
|
| 589 |
+
native_functions=native_functions,
|
| 590 |
+
schema_selector=selector,
|
| 591 |
+
)
|
| 592 |
+
cpu_fm.write(
|
| 593 |
+
"RegisterSchema.cpp",
|
| 594 |
+
lambda: {
|
| 595 |
+
"schema_registrations": schema_registrations,
|
| 596 |
+
"aten_schema_registrations": aten_schema_registrations,
|
| 597 |
+
},
|
| 598 |
+
)
|
| 599 |
+
|
| 600 |
+
|
| 601 |
+
def translate_native_yaml(
|
| 602 |
+
tags_yaml_path: str,
|
| 603 |
+
aten_yaml_path: str,
|
| 604 |
+
native_yaml_path: str | None,
|
| 605 |
+
use_aten_lib: bool,
|
| 606 |
+
out_file: TextIO,
|
| 607 |
+
) -> None:
|
| 608 |
+
"""Translates Executorch DSL dialect to use the same syntax as
|
| 609 |
+
native_functions.yaml. The major difference is that Executorch DSL dialect
|
| 610 |
+
supports "op" key, where it refers to the operator name in native_functions.yaml.
|
| 611 |
+
|
| 612 |
+
For example, a functions.yaml may have the following entry:
|
| 613 |
+
|
| 614 |
+
- op: add.out
|
| 615 |
+
...
|
| 616 |
+
|
| 617 |
+
It needs to be translated to the following:
|
| 618 |
+
|
| 619 |
+
- func: add.out(Tensor self, Tensor other, *, Scalar alpha=1, Tensor(a!) out) -> Tensor(a!)
|
| 620 |
+
...
|
| 621 |
+
|
| 622 |
+
We go in aten_yaml_path and find the operator schema for "add.out" and add it
|
| 623 |
+
to the original functions.yaml. We also add required field "variants", where for
|
| 624 |
+
Executorch it will always be "function".
|
| 625 |
+
|
| 626 |
+
For ATen mode we don't have to do the translation because native_yaml_path is
|
| 627 |
+
the same as native_functions.yaml.
|
| 628 |
+
|
| 629 |
+
Args:
|
| 630 |
+
tags_yaml_path: Path to a tags.yaml file to satisfy codegen parsing.
|
| 631 |
+
It is not optional.
|
| 632 |
+
aten_yaml_path: Path to ATen operator yaml file native_functions.yaml.
|
| 633 |
+
native_yaml_path: Path to a functions.yaml file to parse.
|
| 634 |
+
If the path does not exist in the filesystem, it is treated as an
|
| 635 |
+
empty file. If `custom_ops_yaml_path` exists, the contents of that
|
| 636 |
+
file are appended to the yaml input to be parsed.
|
| 637 |
+
use_aten_lib: We use this flag to determine if we want to generate native
|
| 638 |
+
functions. In ATen mode we should generate out= variants.
|
| 639 |
+
out_file: The IO object that we are writing into.
|
| 640 |
+
Returns:
|
| 641 |
+
None
|
| 642 |
+
"""
|
| 643 |
+
if use_aten_lib:
|
| 644 |
+
with open(aten_yaml_path) as aten_yaml:
|
| 645 |
+
out_file.writelines(aten_yaml.readlines())
|
| 646 |
+
return
|
| 647 |
+
|
| 648 |
+
native_functions, persisted_fields = parse_et_yaml(
|
| 649 |
+
aten_yaml_path,
|
| 650 |
+
tags_yaml_path,
|
| 651 |
+
None,
|
| 652 |
+
skip_native_fns_gen=False,
|
| 653 |
+
)
|
| 654 |
+
|
| 655 |
+
func_to_scoped_name: dict[FunctionSchema, str] = {
|
| 656 |
+
f.func: f"{f.namespace}::{f.func.name}" for f in native_functions
|
| 657 |
+
}
|
| 658 |
+
op_to_scoped_name: dict[OperatorName, str] = {
|
| 659 |
+
func.name: name for func, name in func_to_scoped_name.items()
|
| 660 |
+
}
|
| 661 |
+
|
| 662 |
+
schema_dict = {name: str(func) for func, name in func_to_scoped_name.items()}
|
| 663 |
+
kernel_persist_dict: dict[str, dict[str, Any]] = {
|
| 664 |
+
op_to_scoped_name[op]: v for op, v in persisted_fields.items()
|
| 665 |
+
}
|
| 666 |
+
|
| 667 |
+
if (
|
| 668 |
+
not native_yaml_path
|
| 669 |
+
or not os.path.exists(native_yaml_path)
|
| 670 |
+
or os.stat(native_yaml_path).st_size == 0
|
| 671 |
+
):
|
| 672 |
+
return
|
| 673 |
+
with open(native_yaml_path) as native_yaml:
|
| 674 |
+
native_es = yaml.load(native_yaml, Loader=LineLoader)
|
| 675 |
+
if not native_es:
|
| 676 |
+
return
|
| 677 |
+
for e in native_es:
|
| 678 |
+
assert isinstance(e.get("__line__"), int), e
|
| 679 |
+
loc = Location(native_yaml_path, e.pop("__line__"))
|
| 680 |
+
with context(lambda: f"in {loc}:\n "):
|
| 681 |
+
if "variants" not in e:
|
| 682 |
+
e["variants"] = "function"
|
| 683 |
+
if "func" in e:
|
| 684 |
+
continue
|
| 685 |
+
assert isinstance(e.get("op"), str), e
|
| 686 |
+
opname = e.pop("op")
|
| 687 |
+
if "::" not in opname:
|
| 688 |
+
opname = "aten::" + opname
|
| 689 |
+
assert opname in schema_dict
|
| 690 |
+
e["func"] = schema_dict.get(opname)
|
| 691 |
+
|
| 692 |
+
# Write out persisted kernel information
|
| 693 |
+
if opname in kernel_persist_dict:
|
| 694 |
+
for k, v in kernel_persist_dict[opname].items():
|
| 695 |
+
e[k] = v
|
| 696 |
+
|
| 697 |
+
yaml.dump(native_es, out_file, width=1000)
|
| 698 |
+
|
| 699 |
+
|
| 700 |
+
def parse_yaml(
|
| 701 |
+
path: str | None,
|
| 702 |
+
tags_yaml_path: str,
|
| 703 |
+
function_filter: Callable[[NativeFunction], bool],
|
| 704 |
+
skip_native_fns_gen: bool = False,
|
| 705 |
+
) -> tuple[
|
| 706 |
+
list[NativeFunction],
|
| 707 |
+
dict[DispatchKey, dict[OperatorName, BackendMetadata]] | ETKernelIndex,
|
| 708 |
+
]:
|
| 709 |
+
if path and os.path.exists(path) and os.stat(path).st_size > 0:
|
| 710 |
+
with open(path) as f:
|
| 711 |
+
es = yaml.load(f, Loader=LineLoader)
|
| 712 |
+
|
| 713 |
+
# Check for kernel index structure
|
| 714 |
+
kernel_index = (
|
| 715 |
+
parse_et_yaml_struct(es) if any("kernels" in e for e in es) else None
|
| 716 |
+
)
|
| 717 |
+
|
| 718 |
+
# Remove ET specific fields from entries for BC compatibility
|
| 719 |
+
for entry in es:
|
| 720 |
+
for field in ET_FIELDS:
|
| 721 |
+
entry.pop(field, None)
|
| 722 |
+
|
| 723 |
+
parsed_yaml = parse_native_yaml(
|
| 724 |
+
path,
|
| 725 |
+
tags_yaml_path,
|
| 726 |
+
None,
|
| 727 |
+
skip_native_fns_gen=skip_native_fns_gen,
|
| 728 |
+
loaded_yaml=es,
|
| 729 |
+
)
|
| 730 |
+
native_functions = list(filter(function_filter, parsed_yaml.native_functions))
|
| 731 |
+
op_names = [f.func.name for f in native_functions]
|
| 732 |
+
|
| 733 |
+
# (1) Return ETKernelIndex if kernel index is present
|
| 734 |
+
if kernel_index is not None:
|
| 735 |
+
filtered_index = {
|
| 736 |
+
op_name: kernel_mapping
|
| 737 |
+
for op_name, kernel_mapping in kernel_index.index.items()
|
| 738 |
+
if op_name in op_names
|
| 739 |
+
}
|
| 740 |
+
return native_functions, ETKernelIndex(index=filtered_index)
|
| 741 |
+
|
| 742 |
+
# (2) Return BackendIndices if kernel index is absent
|
| 743 |
+
def map_index(
|
| 744 |
+
m: dict[OperatorName, BackendMetadata],
|
| 745 |
+
) -> dict[OperatorName, BackendMetadata]:
|
| 746 |
+
return {op: m[op] for op in m if op in op_names}
|
| 747 |
+
|
| 748 |
+
backend_indices = {
|
| 749 |
+
k: map_index(b.index) for (k, b) in parsed_yaml.backend_indices.items()
|
| 750 |
+
}
|
| 751 |
+
|
| 752 |
+
return native_functions, backend_indices
|
| 753 |
+
else:
|
| 754 |
+
return [], {}
|
| 755 |
+
|
| 756 |
+
|
| 757 |
+
def parse_yaml_files(
|
| 758 |
+
tags_yaml_path: str,
|
| 759 |
+
aten_yaml_path: str,
|
| 760 |
+
native_yaml_path: str | None,
|
| 761 |
+
custom_ops_yaml_path: str | None,
|
| 762 |
+
selector: SelectiveBuilder,
|
| 763 |
+
use_aten_lib: bool,
|
| 764 |
+
) -> tuple[ETParsedYaml, ETParsedYaml | None]:
|
| 765 |
+
"""Parses functions.yaml and custom_ops.yaml files.
|
| 766 |
+
|
| 767 |
+
Args:
|
| 768 |
+
tags_yaml_path: Path to a tags.yaml file to satisfy codegen parsing.
|
| 769 |
+
It is not optional.
|
| 770 |
+
aten_yaml_path: Path to ATen operator yaml file native_functions.yaml.
|
| 771 |
+
native_yaml_path: Path to a functions.yaml file to parse.
|
| 772 |
+
If the path does not exist in the filesystem, it is treated as an
|
| 773 |
+
empty file. If `custom_ops_yaml_path` exists, the contents of that
|
| 774 |
+
file are appended to the yaml input to be parsed.
|
| 775 |
+
custom_ops_yaml_path: Path to a custom_ops.yaml file to parse. If
|
| 776 |
+
the path does not exist in the filesystem, it is ignored.
|
| 777 |
+
selector: For selective build.
|
| 778 |
+
use_aten_lib: We use this flag to determine if we want to generate native
|
| 779 |
+
functions. In ATen mode we should generate out= variants.
|
| 780 |
+
Returns:
|
| 781 |
+
A tuple with two elements:
|
| 782 |
+
[0]: The parsed results of concatenating the contents of
|
| 783 |
+
`native_yaml_path` and `custom_ops_yaml_path`.
|
| 784 |
+
[1]: The parsed results of the contents of `custom_ops_yaml_path`, if
|
| 785 |
+
present. If not present, None.
|
| 786 |
+
"""
|
| 787 |
+
import tempfile
|
| 788 |
+
|
| 789 |
+
# only include selected ops, this is because we want to avoid
|
| 790 |
+
def function_filter(f: NativeFunction) -> bool:
|
| 791 |
+
return selector.is_native_function_selected(f)
|
| 792 |
+
|
| 793 |
+
with tempfile.TemporaryDirectory() as tmpdirname:
|
| 794 |
+
translated_yaml_path = os.path.join(tmpdirname, "translated.yaml")
|
| 795 |
+
with open(translated_yaml_path, "w") as translated:
|
| 796 |
+
translate_native_yaml(
|
| 797 |
+
tags_yaml_path,
|
| 798 |
+
aten_yaml_path,
|
| 799 |
+
native_yaml_path,
|
| 800 |
+
use_aten_lib,
|
| 801 |
+
translated,
|
| 802 |
+
)
|
| 803 |
+
|
| 804 |
+
translated_functions, translated_indices = parse_yaml(
|
| 805 |
+
translated_yaml_path, tags_yaml_path, function_filter, not use_aten_lib
|
| 806 |
+
)
|
| 807 |
+
custom_ops_functions, custom_ops_indices = parse_yaml(
|
| 808 |
+
custom_ops_yaml_path, tags_yaml_path, function_filter, True
|
| 809 |
+
)
|
| 810 |
+
|
| 811 |
+
# Convert BackendIndices to ETKernelIndex
|
| 812 |
+
if not isinstance(translated_indices, ETKernelIndex):
|
| 813 |
+
translated_indices = ETKernelIndex.from_backend_indices(translated_indices)
|
| 814 |
+
if not isinstance(custom_ops_indices, ETKernelIndex):
|
| 815 |
+
custom_ops_indices = ETKernelIndex.from_backend_indices(custom_ops_indices)
|
| 816 |
+
|
| 817 |
+
combined_functions = translated_functions + custom_ops_functions
|
| 818 |
+
combined_kernel_index = ETKernelIndex.merge_indices(
|
| 819 |
+
translated_indices, custom_ops_indices
|
| 820 |
+
)
|
| 821 |
+
combined_yaml = ETParsedYaml(combined_functions, combined_kernel_index)
|
| 822 |
+
custom_ops_parsed_yaml = ETParsedYaml(custom_ops_functions, custom_ops_indices)
|
| 823 |
+
|
| 824 |
+
return combined_yaml, custom_ops_parsed_yaml
|
| 825 |
+
|
| 826 |
+
|
| 827 |
+
def main() -> None:
|
| 828 |
+
parser = argparse.ArgumentParser(description="Generate operator source files")
|
| 829 |
+
# Although we don't refer to --source-path directly, make_file_manager()
|
| 830 |
+
# expects it to point to a directory that contains a templates/ subdirectory
|
| 831 |
+
# containing the file templates.
|
| 832 |
+
parser.add_argument(
|
| 833 |
+
"-s",
|
| 834 |
+
"--source-path",
|
| 835 |
+
help="path to source directory for kernel templates",
|
| 836 |
+
)
|
| 837 |
+
parser.add_argument(
|
| 838 |
+
"--functions-yaml-path",
|
| 839 |
+
"--functions_yaml_path",
|
| 840 |
+
help="path to the functions.yaml file to use. Optional, but at least "
|
| 841 |
+
"one of --functions-yaml-path and --custom-ops-yaml-path must be "
|
| 842 |
+
"specified.",
|
| 843 |
+
)
|
| 844 |
+
parser.add_argument(
|
| 845 |
+
"--custom-ops-yaml-path",
|
| 846 |
+
"--custom_ops_yaml_path",
|
| 847 |
+
help="path to the custom_ops.yaml file to use. Optional, but at least "
|
| 848 |
+
"one of --functions-yaml-path and --custom-ops-yaml-path must be "
|
| 849 |
+
"specified.",
|
| 850 |
+
)
|
| 851 |
+
parser.add_argument(
|
| 852 |
+
"--aten-yaml-path",
|
| 853 |
+
"--aten_yaml_path",
|
| 854 |
+
help="path to native_functions.yaml file.",
|
| 855 |
+
)
|
| 856 |
+
# Note that make_file_manager() also looks at --install-dir.
|
| 857 |
+
parser.add_argument(
|
| 858 |
+
"-d",
|
| 859 |
+
"--install-dir",
|
| 860 |
+
"--install_dir",
|
| 861 |
+
help="output directory",
|
| 862 |
+
default="build/generated",
|
| 863 |
+
)
|
| 864 |
+
parser.add_argument(
|
| 865 |
+
"-o",
|
| 866 |
+
"--output-dependencies",
|
| 867 |
+
help="output a list of dependencies into the given file and exit",
|
| 868 |
+
)
|
| 869 |
+
# Although we don't refer to --dry-run directly, make_file_manager() looks
|
| 870 |
+
# for it.
|
| 871 |
+
parser.add_argument(
|
| 872 |
+
"--dry-run",
|
| 873 |
+
action="store_true",
|
| 874 |
+
help="run without writing any files (still updates outputs)",
|
| 875 |
+
)
|
| 876 |
+
parser.add_argument(
|
| 877 |
+
"--static-dispatch-backend",
|
| 878 |
+
"--static_dispatch_backend",
|
| 879 |
+
nargs="*",
|
| 880 |
+
help="generate static dispatch code for the specific backend (if set)",
|
| 881 |
+
)
|
| 882 |
+
parser.add_argument(
|
| 883 |
+
"--op-registration-whitelist",
|
| 884 |
+
"--op_registration_whitelist",
|
| 885 |
+
nargs="*",
|
| 886 |
+
help="filter op registrations by the whitelist (if set); "
|
| 887 |
+
"each item is `namespace`::`operator name` without overload name; "
|
| 888 |
+
"e.g.: aten::empty aten::conv2d ...",
|
| 889 |
+
)
|
| 890 |
+
parser.add_argument(
|
| 891 |
+
"--op-selection-yaml-path",
|
| 892 |
+
"--op_selection_yaml_path",
|
| 893 |
+
help="Provide a path to the operator selection (for custom build) YAML "
|
| 894 |
+
"that contains the information about the set of selected operators "
|
| 895 |
+
"and their categories (training, ...). Each operator is either a "
|
| 896 |
+
"full operator name with overload or just a bare operator name. "
|
| 897 |
+
"The operator names also contain the namespace prefix (e.g. aten::)",
|
| 898 |
+
)
|
| 899 |
+
parser.add_argument(
|
| 900 |
+
"--tags-path",
|
| 901 |
+
help="Path to tags.yaml. Required by yaml parsing in codegen system.",
|
| 902 |
+
)
|
| 903 |
+
parser.add_argument(
|
| 904 |
+
"--rocm",
|
| 905 |
+
action="store_true",
|
| 906 |
+
help="reinterpret CUDA as ROCm/HIP and adjust filepaths accordingly",
|
| 907 |
+
)
|
| 908 |
+
parser.add_argument(
|
| 909 |
+
"--use-aten-lib",
|
| 910 |
+
"--use_aten_lib",
|
| 911 |
+
action="store_true",
|
| 912 |
+
help="a boolean flag to indicate whether we use ATen kernels or not, in the future this flag will be per "
|
| 913 |
+
"operator",
|
| 914 |
+
)
|
| 915 |
+
parser.add_argument(
|
| 916 |
+
"--manual_registration",
|
| 917 |
+
"--manual-registration",
|
| 918 |
+
action="store_true",
|
| 919 |
+
help="a boolean flag to indicate whether we want to manually call"
|
| 920 |
+
"register_kernels() or rely on static init. ",
|
| 921 |
+
)
|
| 922 |
+
parser.add_argument(
|
| 923 |
+
"--generate",
|
| 924 |
+
type=str,
|
| 925 |
+
nargs="*",
|
| 926 |
+
choices=["headers", "sources"],
|
| 927 |
+
default=["headers", "sources"],
|
| 928 |
+
help="Generate only a subset of files",
|
| 929 |
+
)
|
| 930 |
+
options = parser.parse_args()
|
| 931 |
+
assert options.tags_path, "tags.yaml is required by codegen yaml parsing."
|
| 932 |
+
|
| 933 |
+
selector = get_custom_build_selector(
|
| 934 |
+
options.op_registration_whitelist,
|
| 935 |
+
options.op_selection_yaml_path,
|
| 936 |
+
)
|
| 937 |
+
|
| 938 |
+
parsed_yaml, custom_ops_parsed_yaml = parse_yaml_files(
|
| 939 |
+
aten_yaml_path=options.aten_yaml_path,
|
| 940 |
+
tags_yaml_path=options.tags_path,
|
| 941 |
+
native_yaml_path=options.functions_yaml_path,
|
| 942 |
+
custom_ops_yaml_path=options.custom_ops_yaml_path,
|
| 943 |
+
selector=selector,
|
| 944 |
+
use_aten_lib=options.use_aten_lib,
|
| 945 |
+
)
|
| 946 |
+
native_functions, kernel_index = (
|
| 947 |
+
parsed_yaml.native_functions,
|
| 948 |
+
parsed_yaml.kernel_index,
|
| 949 |
+
)
|
| 950 |
+
custom_ops_native_functions = (
|
| 951 |
+
custom_ops_parsed_yaml.native_functions if custom_ops_parsed_yaml else []
|
| 952 |
+
)
|
| 953 |
+
|
| 954 |
+
cpu_fm = make_file_manager(options=options)
|
| 955 |
+
|
| 956 |
+
if "headers" in options.generate:
|
| 957 |
+
# generate CustomOpsNativeFunctions.h when custom_ops.yaml is present, to match the build system.
|
| 958 |
+
gen_headers(
|
| 959 |
+
native_functions=native_functions,
|
| 960 |
+
gen_custom_ops_header=options.custom_ops_yaml_path,
|
| 961 |
+
custom_ops_native_functions=custom_ops_native_functions,
|
| 962 |
+
selector=selector,
|
| 963 |
+
kernel_index=kernel_index,
|
| 964 |
+
cpu_fm=cpu_fm,
|
| 965 |
+
use_aten_lib=options.use_aten_lib,
|
| 966 |
+
)
|
| 967 |
+
|
| 968 |
+
if "sources" in options.generate:
|
| 969 |
+
gen_unboxing(
|
| 970 |
+
native_functions=native_functions,
|
| 971 |
+
cpu_fm=cpu_fm,
|
| 972 |
+
selector=selector,
|
| 973 |
+
use_aten_lib=options.use_aten_lib,
|
| 974 |
+
kernel_index=kernel_index,
|
| 975 |
+
manual_registration=options.manual_registration,
|
| 976 |
+
)
|
| 977 |
+
if custom_ops_native_functions:
|
| 978 |
+
gen_custom_ops(
|
| 979 |
+
native_functions=custom_ops_native_functions,
|
| 980 |
+
selector=selector,
|
| 981 |
+
kernel_index=kernel_index,
|
| 982 |
+
cpu_fm=cpu_fm,
|
| 983 |
+
rocm=options.rocm,
|
| 984 |
+
)
|
| 985 |
+
|
| 986 |
+
if options.output_dependencies:
|
| 987 |
+
depfile_path = Path(options.output_dependencies).resolve()
|
| 988 |
+
depfile_name = depfile_path.name
|
| 989 |
+
depfile_stem = depfile_path.stem
|
| 990 |
+
|
| 991 |
+
for fm, prefix in [
|
| 992 |
+
(cpu_fm, ""),
|
| 993 |
+
]:
|
| 994 |
+
varname = prefix + depfile_stem
|
| 995 |
+
path = depfile_path.parent / (prefix + depfile_name)
|
| 996 |
+
fm.write_outputs(varname, str(path))
|
| 997 |
+
|
| 998 |
+
|
| 999 |
+
if __name__ == "__main__":
|
| 1000 |
+
main()
|
gen_functionalization_type.py
ADDED
|
@@ -0,0 +1,882 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
from dataclasses import dataclass
|
| 4 |
+
from typing import Callable, TYPE_CHECKING
|
| 5 |
+
|
| 6 |
+
from torchgen.api import cpp, dispatcher
|
| 7 |
+
from torchgen.api.translate import translate
|
| 8 |
+
from torchgen.api.types import (
|
| 9 |
+
BaseCType,
|
| 10 |
+
Binding,
|
| 11 |
+
CType,
|
| 12 |
+
DispatcherSignature,
|
| 13 |
+
FunctionalizationLambda,
|
| 14 |
+
iTensorListRefT,
|
| 15 |
+
NativeSignature,
|
| 16 |
+
OptionalCType,
|
| 17 |
+
optionalSymIntArrayRefT,
|
| 18 |
+
symIntArrayRefT,
|
| 19 |
+
SymIntT,
|
| 20 |
+
tensorListT,
|
| 21 |
+
tensorT,
|
| 22 |
+
VectorCType,
|
| 23 |
+
ViewInverseSignature,
|
| 24 |
+
)
|
| 25 |
+
from torchgen.context import (
|
| 26 |
+
method_with_native_function,
|
| 27 |
+
native_function_manager,
|
| 28 |
+
with_native_function,
|
| 29 |
+
with_native_function_and,
|
| 30 |
+
)
|
| 31 |
+
from torchgen.model import (
|
| 32 |
+
Argument,
|
| 33 |
+
BackendIndex,
|
| 34 |
+
BaseTy,
|
| 35 |
+
BaseType,
|
| 36 |
+
FunctionSchema,
|
| 37 |
+
ListType,
|
| 38 |
+
NativeFunction,
|
| 39 |
+
NativeFunctionsGroup,
|
| 40 |
+
NativeFunctionsViewGroup,
|
| 41 |
+
Return,
|
| 42 |
+
SchemaKind,
|
| 43 |
+
SelfArgument,
|
| 44 |
+
TensorOptionsArguments,
|
| 45 |
+
)
|
| 46 |
+
from torchgen.native_function_generation import (
|
| 47 |
+
INPLACE_OPS_THAT_DONT_GET_GROUPED_PROPERLY,
|
| 48 |
+
MUTABLE_OPS_THAT_CANNOT_GET_AN_OUT_VARIANT,
|
| 49 |
+
OUT_OPS_THAT_DONT_GET_GROUPED_PROPERLY,
|
| 50 |
+
)
|
| 51 |
+
from torchgen.utils import dataclass_repr
|
| 52 |
+
|
| 53 |
+
|
| 54 |
+
if TYPE_CHECKING:
|
| 55 |
+
from torchgen.selective_build.selector import SelectiveBuilder
|
| 56 |
+
|
| 57 |
+
|
| 58 |
+
# Note: [Mutable Ops Not Using Functionalization]
|
| 59 |
+
# Ops in this list currently do not work with functionalization and should be fixed.
|
| 60 |
+
MUTABLE_OPS_NOT_USING_FUNCTIONALIZATION = (
|
| 61 |
+
OUT_OPS_THAT_DONT_GET_GROUPED_PROPERLY
|
| 62 |
+
+ MUTABLE_OPS_THAT_CANNOT_GET_AN_OUT_VARIANT
|
| 63 |
+
+ INPLACE_OPS_THAT_DONT_GET_GROUPED_PROPERLY
|
| 64 |
+
+ [
|
| 65 |
+
# It will be BC-breaking, but we should fix their schemas.
|
| 66 |
+
# should be inplace?
|
| 67 |
+
"record_stream",
|
| 68 |
+
# See Note [resize_ in Functionalization]
|
| 69 |
+
"resize_",
|
| 70 |
+
"resize_as_",
|
| 71 |
+
# This function is used as for testing purposes only.
|
| 72 |
+
"_fill_mem_eff_dropout_mask_",
|
| 73 |
+
]
|
| 74 |
+
)
|
| 75 |
+
|
| 76 |
+
# This file contains codegen that relates to the functionalization pass.
|
| 77 |
+
# It includes:
|
| 78 |
+
# - gen_functionalization_definition
|
| 79 |
+
# Generates dispatcher kernel definitions for the functionalization pass.
|
| 80 |
+
# - gen_functionalization_registration
|
| 81 |
+
# Generates dispatcher kernel registrations for the functionalization pass.
|
| 82 |
+
# - gen_functionalization_view_inverse_declaration
|
| 83 |
+
# Generates a declaration for an "inverse view", for every view op
|
| 84 |
+
# that is needed in functionalization. We manually implement their definitions.
|
| 85 |
+
# - gen_composite_view_copy_kernel
|
| 86 |
+
# Generates view_copy() composite kernels for all view_copy operators.
|
| 87 |
+
|
| 88 |
+
|
| 89 |
+
# Generates the body of the default composite C++ kernel for a {view}_copy NativeFunction
|
| 90 |
+
# See Note [view_copy NativeFunctions]
|
| 91 |
+
@dataclass(frozen=True)
|
| 92 |
+
class GenCompositeViewCopyKernel:
|
| 93 |
+
backend_index: BackendIndex
|
| 94 |
+
|
| 95 |
+
@method_with_native_function
|
| 96 |
+
def __call__(self, g: NativeFunctionsViewGroup) -> str | None:
|
| 97 |
+
if g.view_copy is None:
|
| 98 |
+
return None
|
| 99 |
+
elif g.view_copy.func.name.name.base != f"{g.view.func.name.name}_copy":
|
| 100 |
+
# If the view_copy doesn't match the standard naming scheme of <op>_copy,
|
| 101 |
+
# assume it already exists and doesn't need to be generated.
|
| 102 |
+
# Example: slice_inverse() with the copy variant named slice_scatter()
|
| 103 |
+
# instead of slice_inverse_copy()
|
| 104 |
+
return None
|
| 105 |
+
|
| 106 |
+
metadata = self.backend_index.get_kernel(g.view_copy)
|
| 107 |
+
assert metadata is not None
|
| 108 |
+
|
| 109 |
+
# We can make view_copy work in more cases by using reshape()
|
| 110 |
+
# when a normal view call would ordinarily fail.
|
| 111 |
+
# This also makes LTC more efficient, because they don't need to include
|
| 112 |
+
# clone() calls in their graph (which is normally needed by reshape).
|
| 113 |
+
if str(g.view_copy.func.name) == "view_copy":
|
| 114 |
+
assert metadata.kernel == "view_copy_symint"
|
| 115 |
+
return """\
|
| 116 |
+
at::Tensor view_copy_symint(const at::Tensor & self, at::SymIntArrayRef size) {
|
| 117 |
+
c10::SymDimVector shape = infer_size_dv(size, self.sym_numel());
|
| 118 |
+
if (!at::detail::computeStride(self.sym_sizes(), self.sym_strides(), shape).has_value()) {
|
| 119 |
+
return self.reshape_symint(size);
|
| 120 |
+
} else {
|
| 121 |
+
auto output = at::_ops::view::call(self, size);
|
| 122 |
+
return output.clone(/*memory_format=*/at::MemoryFormat::Contiguous);
|
| 123 |
+
}
|
| 124 |
+
}
|
| 125 |
+
"""
|
| 126 |
+
# view_copy is a native signature, since we're generating an at::native:: kernel
|
| 127 |
+
# Functionalization always operates on symints though
|
| 128 |
+
view_copy_sig = NativeSignature(
|
| 129 |
+
g.view_copy.func, symint=metadata.supports_symint()
|
| 130 |
+
)
|
| 131 |
+
|
| 132 |
+
# view is a dispatcher signature, since we're calling into the at::_ops API
|
| 133 |
+
view_sig = DispatcherSignature(g.view.func)
|
| 134 |
+
|
| 135 |
+
view_api_name = g.view.func.name.unambiguous_name()
|
| 136 |
+
exprs = ", ".join(
|
| 137 |
+
[e.expr for e in translate(view_copy_sig.arguments(), view_sig.arguments())]
|
| 138 |
+
)
|
| 139 |
+
|
| 140 |
+
# view ops today always return either a Tensor or a list of Tensors
|
| 141 |
+
assert len(g.view.func.returns) == 1
|
| 142 |
+
assert g.view.func.returns[0].type == BaseType(
|
| 143 |
+
BaseTy.Tensor
|
| 144 |
+
) or g.view.func.returns[0].type == ListType(BaseType(BaseTy.Tensor), None)
|
| 145 |
+
|
| 146 |
+
if g.view.func.returns[0].type == BaseType(BaseTy.Tensor):
|
| 147 |
+
return_cloned_output = """\
|
| 148 |
+
return output.clone(/*memory_format=*/at::MemoryFormat::Contiguous);"""
|
| 149 |
+
else:
|
| 150 |
+
# If the return type is a list, we need to clone each tensor in the list.
|
| 151 |
+
return_cloned_output = f"""\
|
| 152 |
+
{view_copy_sig.returns_type().cpp_type()} out_clone;
|
| 153 |
+
for (const auto i : c10::irange(output.size())) {{
|
| 154 |
+
out_clone.push_back(output[i].clone(/*memory_format=*/at::MemoryFormat::Contiguous));
|
| 155 |
+
}}
|
| 156 |
+
return out_clone;"""
|
| 157 |
+
|
| 158 |
+
# The default generated composite kernel for {view}_copy() operators just clones
|
| 159 |
+
# the input tensor, and runs the underlying view on the clone.
|
| 160 |
+
return f"""
|
| 161 |
+
{view_copy_sig.defn(name=metadata.kernel)} {{
|
| 162 |
+
auto output = at::_ops::{view_api_name}::call({exprs});
|
| 163 |
+
{return_cloned_output}
|
| 164 |
+
}}
|
| 165 |
+
"""
|
| 166 |
+
|
| 167 |
+
|
| 168 |
+
def return_str(rets: tuple[Return, ...], names: list[str]) -> str:
|
| 169 |
+
assert len(rets) == len(names)
|
| 170 |
+
if len(rets) == 0:
|
| 171 |
+
return ""
|
| 172 |
+
elif len(rets) == 1:
|
| 173 |
+
return f"return {names[0]};"
|
| 174 |
+
else:
|
| 175 |
+
return f"return {dispatcher.returns_type(rets).cpp_type()}({', '.join(names)});"
|
| 176 |
+
|
| 177 |
+
|
| 178 |
+
def modifies_arguments(f: NativeFunction) -> bool:
|
| 179 |
+
return any(
|
| 180 |
+
a.annotation is not None and a.annotation.is_write
|
| 181 |
+
for a in f.func.arguments.flat_all
|
| 182 |
+
)
|
| 183 |
+
|
| 184 |
+
|
| 185 |
+
def wrapper_name(func: FunctionSchema) -> str:
|
| 186 |
+
if func.name.overload_name:
|
| 187 |
+
return f"{cpp.name(func)}_{func.name.overload_name}"
|
| 188 |
+
else:
|
| 189 |
+
return cpp.name(func)
|
| 190 |
+
|
| 191 |
+
|
| 192 |
+
def is_tensor_like(a: Argument | TensorOptionsArguments | SelfArgument) -> bool:
|
| 193 |
+
return isinstance(a, SelfArgument) or (
|
| 194 |
+
isinstance(a, Argument) and a.type.is_tensor_like()
|
| 195 |
+
)
|
| 196 |
+
|
| 197 |
+
|
| 198 |
+
# We need to wrap / unwrap various arguments from the op in the functionalization kernels.
|
| 199 |
+
# Some op schemas include non-owning types though (like TensorList),
|
| 200 |
+
# and when we unwrap them we expect to get out an owning type!.
|
| 201 |
+
# We also return a lambda that tells you how to conver the non-owning type argument into the owning type.
|
| 202 |
+
def get_owning_type(t: CType) -> tuple[CType, Callable[[str], str]]:
|
| 203 |
+
if t == BaseCType(tensorListT):
|
| 204 |
+
return VectorCType(BaseCType(tensorT)), lambda x: f"{x}.vec()"
|
| 205 |
+
if t == BaseCType(iTensorListRefT):
|
| 206 |
+
return VectorCType(BaseCType(tensorT)), lambda x: f"{{{x}.begin(), {x}.end()}}"
|
| 207 |
+
# There are technically other non-owning types out there (like IntArrayRef),
|
| 208 |
+
# but functionalization only actually cares about the ones involving tensors.
|
| 209 |
+
return t, lambda x: x
|
| 210 |
+
|
| 211 |
+
|
| 212 |
+
# unwraps all tensor-like arguments, returning:
|
| 213 |
+
# (1) a string containing all of the logic that does the unwrapping
|
| 214 |
+
# (2) a context, to be used by translate(), with all of the relevant bindings.
|
| 215 |
+
def unwrap_tensor_args(
|
| 216 |
+
sig: DispatcherSignature, *, is_view_op: bool
|
| 217 |
+
) -> tuple[str, list[Binding]]:
|
| 218 |
+
context: list[Binding] = []
|
| 219 |
+
unwrapped_tensor_args: list[str] = []
|
| 220 |
+
for arg in sig.arguments():
|
| 221 |
+
if is_tensor_like(arg.argument):
|
| 222 |
+
# for tensor inputs, we want to unwrap them before passing them into the redispatch calls.
|
| 223 |
+
unwrapped_name = f"{arg.name}_"
|
| 224 |
+
# For most ops, the functionalization needs to sync any pending updates on the input tensors
|
| 225 |
+
# before calling the operator, since otherwise the operator will act on stale data.
|
| 226 |
+
# For view ops though, we can continue to defer syncing until the tensor is used by
|
| 227 |
+
# a non-view operator.
|
| 228 |
+
maybe_sync_input = (
|
| 229 |
+
"" if is_view_op else f"at::functionalization::impl::sync({arg.name});"
|
| 230 |
+
)
|
| 231 |
+
unwrapped_type, conversion_fn = get_owning_type(
|
| 232 |
+
arg.nctype.remove_const_ref().type
|
| 233 |
+
)
|
| 234 |
+
unwrapped_tensor_args.append(
|
| 235 |
+
f"""
|
| 236 |
+
{unwrapped_type.cpp_type()} {unwrapped_name};
|
| 237 |
+
if (at::functionalization::impl::isFunctionalTensor({arg.name})) {{
|
| 238 |
+
{maybe_sync_input}
|
| 239 |
+
{unwrapped_name} = at::functionalization::impl::from_functional_tensor({arg.name});
|
| 240 |
+
}} else {{
|
| 241 |
+
{unwrapped_name} = {conversion_fn(arg.name)};
|
| 242 |
+
}}"""
|
| 243 |
+
)
|
| 244 |
+
context.append(arg.with_name(unwrapped_name))
|
| 245 |
+
else:
|
| 246 |
+
# for non-tensor inputs, we want to pass them directly into the redispatch calls.
|
| 247 |
+
context.append(arg)
|
| 248 |
+
unwrap_tensor_args_str = "\n ".join(unwrapped_tensor_args)
|
| 249 |
+
return unwrap_tensor_args_str, context
|
| 250 |
+
|
| 251 |
+
|
| 252 |
+
# converts all tensor-like arguments to meta tensors, which are used to compute stride info. Returns:
|
| 253 |
+
# (1) a string containing all of the logic that does the conversions.
|
| 254 |
+
# (2) a context, to be used by translate(), with all of the relevant bindings.
|
| 255 |
+
def convert_to_meta_tensors(sig: DispatcherSignature) -> tuple[str, list[Binding]]:
|
| 256 |
+
context: list[Binding] = []
|
| 257 |
+
unwrapped_tensor_args: list[str] = []
|
| 258 |
+
for arg in sig.arguments():
|
| 259 |
+
if is_tensor_like(arg.argument):
|
| 260 |
+
# for tensor inputs, we want to unwrap them before passing them into the redispatch calls.
|
| 261 |
+
a_ = arg.name
|
| 262 |
+
unwrapped_name = f"{arg.name}_meta"
|
| 263 |
+
unwrapped_tensor_args.append(f"auto {unwrapped_name} = to_meta({a_});")
|
| 264 |
+
context.append(arg.with_name(unwrapped_name))
|
| 265 |
+
else:
|
| 266 |
+
# for non-tensor inputs, we want to pass them directly into the redispatch calls.
|
| 267 |
+
context.append(arg)
|
| 268 |
+
unwrap_tensor_args_str = "\n ".join(unwrapped_tensor_args)
|
| 269 |
+
return unwrap_tensor_args_str, context
|
| 270 |
+
|
| 271 |
+
|
| 272 |
+
# The functionalization codegen currently expects view op schemas to have this form:
|
| 273 |
+
# foo(Tensor(a), ...) -> Tensor(a) (e.g. transpose)
|
| 274 |
+
# foo(Tensor(a!), ...) -> Tensor(a!) (e.g. transpose_)
|
| 275 |
+
def assert_view_op_properties(func: FunctionSchema) -> None:
|
| 276 |
+
def is_alias(a: Argument) -> bool:
|
| 277 |
+
return a.annotation is not None
|
| 278 |
+
|
| 279 |
+
args = func.arguments.flat_non_out
|
| 280 |
+
# The first argument is a tensor with an alias semantics (annotations)
|
| 281 |
+
assert (
|
| 282 |
+
len(args) > 0 and args[0].type == BaseType(BaseTy.Tensor)
|
| 283 |
+
), f"""In the functionalization codegen, we expect the first argument of every view operator to be a tensor,
|
| 284 |
+
but found an argument of type {str(args[0].type)} for operator: {str(func.name)}."""
|
| 285 |
+
# No other arguments have aliasing semantics
|
| 286 |
+
assert (
|
| 287 |
+
is_alias(args[0]) and not any(is_alias(a) for a in args[1:])
|
| 288 |
+
), """In the functionalization codegen, we expect the first argument of every view operator to alias the output.
|
| 289 |
+
View operators with multiple aliasing inputs aren't supported yet. Found an operator that doesn't satisfy this constraint"""
|
| 290 |
+
|
| 291 |
+
|
| 292 |
+
# One-liner expression for checking if an expression expr of type type has any
|
| 293 |
+
# symbolic values.
|
| 294 |
+
def emit_expr_has_symbolic_values(expr: str, type: CType) -> str:
|
| 295 |
+
if type == BaseCType(SymIntT):
|
| 296 |
+
return f"{expr}.is_symbolic()"
|
| 297 |
+
|
| 298 |
+
if isinstance(type, OptionalCType):
|
| 299 |
+
innerexpr = f"(*{expr})"
|
| 300 |
+
return f"{expr}.has_value() ? {emit_expr_has_symbolic_values(innerexpr, type.elem)} : false"
|
| 301 |
+
|
| 302 |
+
if type == BaseCType(optionalSymIntArrayRefT):
|
| 303 |
+
return emit_expr_has_symbolic_values(
|
| 304 |
+
expr, OptionalCType(BaseCType(symIntArrayRefT))
|
| 305 |
+
)
|
| 306 |
+
|
| 307 |
+
if type in (BaseCType(symIntArrayRefT), VectorCType(BaseCType(SymIntT))):
|
| 308 |
+
argname = "arg"
|
| 309 |
+
lambda_check = emit_expr_has_symbolic_values(argname, BaseCType(SymIntT))
|
| 310 |
+
return (
|
| 311 |
+
"std::any_of("
|
| 312 |
+
f"{expr}.begin(), {expr}.end(), "
|
| 313 |
+
f"[=](auto& {argname}) {{ return {lambda_check}; }})"
|
| 314 |
+
)
|
| 315 |
+
|
| 316 |
+
raise ValueError(
|
| 317 |
+
"unsupported type for has_symbolic_values check. "
|
| 318 |
+
"It should be a SymInt or a collection of those. "
|
| 319 |
+
f"Got: {type.cpp_type()}"
|
| 320 |
+
)
|
| 321 |
+
|
| 322 |
+
|
| 323 |
+
# Detects whether any of the SymInt arguments are, in fact, symbolic values.
|
| 324 |
+
# This is used in the constructor of ViewMeta.
|
| 325 |
+
def emit_has_symbolic_inputs(sig: DispatcherSignature) -> tuple[str, str]:
|
| 326 |
+
name = "has_symbolic_inputs"
|
| 327 |
+
statements = [
|
| 328 |
+
f"{name} = {name} | ({emit_expr_has_symbolic_values(binding.name, binding.nctype.type)});"
|
| 329 |
+
for binding in sig.arguments()
|
| 330 |
+
if (
|
| 331 |
+
isinstance(binding.argument, Argument)
|
| 332 |
+
and binding.argument.type.is_symint_like()
|
| 333 |
+
)
|
| 334 |
+
]
|
| 335 |
+
body = "\n ".join(statements)
|
| 336 |
+
return (
|
| 337 |
+
name,
|
| 338 |
+
f"""
|
| 339 |
+
bool {name} = false;
|
| 340 |
+
{body}""",
|
| 341 |
+
)
|
| 342 |
+
|
| 343 |
+
|
| 344 |
+
# Generates the Functionalization kernel for:
|
| 345 |
+
# - ops that create aliases (e.g. transpose())
|
| 346 |
+
# - ops that are views AND mutations (e.g. transpose_())
|
| 347 |
+
def emit_view_functionalization_body(
|
| 348 |
+
g: NativeFunctionsViewGroup, *, view_inplace: bool
|
| 349 |
+
) -> str:
|
| 350 |
+
if view_inplace:
|
| 351 |
+
# This op is both an inplace op AND a view op.
|
| 352 |
+
# See Note [Functionalization Pass - Inplace View Ops] for details.
|
| 353 |
+
# I currently have the view meta call into the out-of-place variant of the view, to avoid
|
| 354 |
+
# having to define an extra ~20 inplace {view}_inverse_ functions.
|
| 355 |
+
# Most view ops don't have NativeFunctionGroup's both, because we don't define out= variants for view ops.
|
| 356 |
+
# I'm assuming that every inplace-view op has a corresponding out-of-place view op,
|
| 357 |
+
# with the same name but the trailing underscore removed.
|
| 358 |
+
# This is currently asserted at parse time in gen.py (see error_check_native_functions).
|
| 359 |
+
assert g.view_inplace is not None
|
| 360 |
+
f = g.view_inplace
|
| 361 |
+
else:
|
| 362 |
+
f = g.view
|
| 363 |
+
|
| 364 |
+
assert g.view_copy is not None
|
| 365 |
+
with native_function_manager(f):
|
| 366 |
+
call_sig = DispatcherSignature.from_schema(g.view_copy.func)
|
| 367 |
+
|
| 368 |
+
# the "view_copy" op name that the functionalization kernels need to call
|
| 369 |
+
api_name = g.view_copy.func.name.unambiguous_name()
|
| 370 |
+
# Sometimes the functionalization pass needs to no-op (e.g. if it was passed non-functional tensors)
|
| 371 |
+
# "no-op"ing in this context is just redispatching to the original op.
|
| 372 |
+
noop_api_name = f.func.name.unambiguous_name()
|
| 373 |
+
|
| 374 |
+
dispatcher_sig = DispatcherSignature.from_schema(f.func)
|
| 375 |
+
assert_view_op_properties(f.func)
|
| 376 |
+
view_tensor_name = dispatcher_sig.arguments()[0].name
|
| 377 |
+
|
| 378 |
+
return_type = dispatcher_sig.returns_type().remove_const_ref().cpp_type()
|
| 379 |
+
|
| 380 |
+
unwrap_tensor_args_str, unwrapped_args_ctx = unwrap_tensor_args(
|
| 381 |
+
dispatcher_sig, is_view_op=True
|
| 382 |
+
)
|
| 383 |
+
view_redispatch_args = [
|
| 384 |
+
e.expr
|
| 385 |
+
for e in translate(unwrapped_args_ctx, call_sig.arguments(), method=False)
|
| 386 |
+
]
|
| 387 |
+
|
| 388 |
+
forward_lambda = FunctionalizationLambda.from_func(g, is_reverse=False)
|
| 389 |
+
reverse_lambda = FunctionalizationLambda.from_func(g, is_reverse=True)
|
| 390 |
+
|
| 391 |
+
# The meta API call should use the same arguments, but convert all tensors to meta tensors first.
|
| 392 |
+
meta_conversion_str, meta_call_ctx = convert_to_meta_tensors(dispatcher_sig)
|
| 393 |
+
meta_call_args = [
|
| 394 |
+
e.expr for e in translate(meta_call_ctx, call_sig.arguments(), method=False)
|
| 395 |
+
]
|
| 396 |
+
|
| 397 |
+
(
|
| 398 |
+
symbolic_inputs_varname,
|
| 399 |
+
symbolic_inputs_check,
|
| 400 |
+
) = emit_has_symbolic_inputs(call_sig)
|
| 401 |
+
|
| 402 |
+
if "inplace_view" in f.tags:
|
| 403 |
+
# See Note [Functionalization Pass - Inplace View Ops] for more details
|
| 404 |
+
return f"""
|
| 405 |
+
{dispatcher_sig.defn(name=wrapper_name(f.func), is_redispatching_fn=True)} {{
|
| 406 |
+
if (!at::functionalization::impl::isFunctionalTensor({view_tensor_name})) {{
|
| 407 |
+
// functionalization is re-entrant, but will no-op if it wasn't passed a FunctionalTensorWrapper.
|
| 408 |
+
{unwrap_tensor_args_str}
|
| 409 |
+
at::AutoDispatchSkipFunctionalize guard;
|
| 410 |
+
return at::_ops::{noop_api_name}::call({', '.join(view_redispatch_args)});
|
| 411 |
+
}}
|
| 412 |
+
auto reapply_views = at::functionalization::impl::getFunctionalizationReapplyViewsTLS();
|
| 413 |
+
auto inverse_return_mode = (
|
| 414 |
+
reapply_views ? at::functionalization::InverseReturnMode::ViewOrScatterInverse
|
| 415 |
+
: at::functionalization::InverseReturnMode::NeverView
|
| 416 |
+
);
|
| 417 |
+
{symbolic_inputs_check}
|
| 418 |
+
at::functionalization::ViewMeta view_meta = at::functionalization::ViewMeta(
|
| 419 |
+
{forward_lambda.decl()} {{
|
| 420 |
+
if (reapply_views) {{
|
| 421 |
+
return {forward_lambda.inner_call(reapply_views=True)}
|
| 422 |
+
}} else {{
|
| 423 |
+
return {forward_lambda.inner_call(reapply_views=False)}
|
| 424 |
+
}}
|
| 425 |
+
}},
|
| 426 |
+
{reverse_lambda.decl()} {{
|
| 427 |
+
return {reverse_lambda.inner_call()}
|
| 428 |
+
}},
|
| 429 |
+
/*has_symbolic_inputs=*/{symbolic_inputs_varname}
|
| 430 |
+
);
|
| 431 |
+
auto compute_reference_meta =
|
| 432 |
+
{view_tensor_name}.key_set().has_backend(c10::BackendComponent::XLABit) ||
|
| 433 |
+
{view_tensor_name}.key_set().has_backend(c10::BackendComponent::LazyBit);
|
| 434 |
+
{return_type} reference_tensor_output;
|
| 435 |
+
if (compute_reference_meta) {{
|
| 436 |
+
{meta_conversion_str}
|
| 437 |
+
at::AutoDispatchSkipFunctionalize func_guard;
|
| 438 |
+
c10::impl::ExcludeDispatchKeyGuard guard(exclude_keys_for_meta_dispatch);
|
| 439 |
+
reference_tensor_output = at::_ops::{noop_api_name}::call({', '.join(meta_call_args)});
|
| 440 |
+
}}
|
| 441 |
+
// This function adds the above view meta to the current tensor and replays them off the base,
|
| 442 |
+
// mutating the size/stride info of the current FunctionalTensorWrapper.
|
| 443 |
+
// Because of this, we need to make sure to run the reference shape function above,
|
| 444 |
+
// BEFORE doing this (otherwise we'll end up runnin the reference function using the wrong sizes/strides)
|
| 445 |
+
at::functionalization::impl::mutate_view_meta({view_tensor_name}, view_meta);
|
| 446 |
+
// See Note [Propagating strides in the functionalization pass]
|
| 447 |
+
// XLA/LTC don't implement the logic to propagate strides correctly, so we need to rely
|
| 448 |
+
// on a reference implementation here (instead of relying on the output from the forward lambda
|
| 449 |
+
// having the correct stride info)
|
| 450 |
+
if (compute_reference_meta) {{
|
| 451 |
+
at::functionalization::impl::set_sizes_strides_offset({view_tensor_name}, reference_tensor_output);
|
| 452 |
+
}}
|
| 453 |
+
return {view_tensor_name};
|
| 454 |
+
}}
|
| 455 |
+
"""
|
| 456 |
+
|
| 457 |
+
else:
|
| 458 |
+
is_multi_output_view = isinstance(f.func.returns[0].type, ListType)
|
| 459 |
+
return f"""
|
| 460 |
+
{dispatcher_sig.defn(name=wrapper_name(f.func), is_redispatching_fn=True)} {{
|
| 461 |
+
{unwrap_tensor_args_str}
|
| 462 |
+
if (!at::functionalization::impl::isFunctionalTensor({view_tensor_name})) {{
|
| 463 |
+
// functionalization is re-entrant, but will no-op if it wasn't passed a FunctionalTensorWrapper.
|
| 464 |
+
at::AutoDispatchSkipFunctionalize guard;
|
| 465 |
+
return at::_ops::{noop_api_name}::call({', '.join(view_redispatch_args)});
|
| 466 |
+
}}
|
| 467 |
+
auto reapply_views = at::functionalization::impl::getFunctionalizationReapplyViewsTLS();
|
| 468 |
+
auto inverse_return_mode = (
|
| 469 |
+
reapply_views ? at::functionalization::InverseReturnMode::ViewOrScatterInverse
|
| 470 |
+
: at::functionalization::InverseReturnMode::NeverView
|
| 471 |
+
);
|
| 472 |
+
auto compute_reference_meta =
|
| 473 |
+
{view_tensor_name}.key_set().has_backend(c10::BackendComponent::XLABit) ||
|
| 474 |
+
{view_tensor_name}.key_set().has_backend(c10::BackendComponent::LazyBit);
|
| 475 |
+
{return_type} reference_tensor_output;
|
| 476 |
+
if (compute_reference_meta) {{
|
| 477 |
+
{meta_conversion_str}
|
| 478 |
+
at::AutoDispatchSkipFunctionalize func_guard;
|
| 479 |
+
c10::impl::ExcludeDispatchKeyGuard guard(exclude_keys_for_meta_dispatch);
|
| 480 |
+
reference_tensor_output = at::_ops::{noop_api_name}::call({', '.join(meta_call_args)});
|
| 481 |
+
}}
|
| 482 |
+
{return_type} tmp_output;
|
| 483 |
+
{{
|
| 484 |
+
at::AutoDispatchSkipFunctionalize guard;
|
| 485 |
+
if (reapply_views) {{
|
| 486 |
+
tmp_output = at::_ops::{noop_api_name}::call({', '.join(view_redispatch_args)});
|
| 487 |
+
}} else {{
|
| 488 |
+
tmp_output = at::_ops::{api_name}::call({', '.join(view_redispatch_args)});
|
| 489 |
+
}}
|
| 490 |
+
}}
|
| 491 |
+
{symbolic_inputs_check}
|
| 492 |
+
at::functionalization::ViewMeta view_meta = at::functionalization::ViewMeta(
|
| 493 |
+
{forward_lambda.decl()} {{
|
| 494 |
+
if (reapply_views) {{
|
| 495 |
+
return {forward_lambda.inner_call(reapply_views=True)}
|
| 496 |
+
}} else {{
|
| 497 |
+
return {forward_lambda.inner_call(reapply_views=False)}
|
| 498 |
+
}}
|
| 499 |
+
}},
|
| 500 |
+
{reverse_lambda.decl()} {{
|
| 501 |
+
return {reverse_lambda.inner_call()}
|
| 502 |
+
}},
|
| 503 |
+
/*has_symbolic_inputs=*/{symbolic_inputs_varname},
|
| 504 |
+
/*is_multi_output=*/{str(is_multi_output_view).lower()},
|
| 505 |
+
/*is_as_strided=*/{str(str(f.func.name) == 'as_strided').lower()}
|
| 506 |
+
);
|
| 507 |
+
auto out = at::functionalization::impl::create_functional_tensor_with_view_meta(tmp_output, {view_tensor_name}, view_meta);
|
| 508 |
+
// See Note [Propagating strides in the functionalization pass]
|
| 509 |
+
if (compute_reference_meta) {{
|
| 510 |
+
at::functionalization::impl::set_sizes_strides_offset(out, reference_tensor_output);
|
| 511 |
+
}}
|
| 512 |
+
return out;
|
| 513 |
+
}}
|
| 514 |
+
"""
|
| 515 |
+
|
| 516 |
+
|
| 517 |
+
def maybe_create_output(f: NativeFunction, var_name: str) -> str:
|
| 518 |
+
if len(f.func.returns) == 0:
|
| 519 |
+
return ""
|
| 520 |
+
return_type = dispatcher.returns_type(f.func.returns).remove_const_ref().cpp_type()
|
| 521 |
+
return f"{return_type} {var_name} = "
|
| 522 |
+
|
| 523 |
+
|
| 524 |
+
# Given a NativeFunction, and a variable name corresponding to the output of redispatching on the function,
|
| 525 |
+
# this returns two lists of names, consisting of:
|
| 526 |
+
# - the names of returns corresponding to the original (mutable) inputs of the outer function
|
| 527 |
+
# - the names of returns corresponding to the (immutable) outputs of the inner redispatched function
|
| 528 |
+
def get_mutable_redispatch_return_names(
|
| 529 |
+
f: NativeFunction, inner_return_var: str
|
| 530 |
+
) -> tuple[list[str], list[str]]:
|
| 531 |
+
aliased_returns = []
|
| 532 |
+
non_aliased_returns = []
|
| 533 |
+
for i, name in enumerate(f.func.aliased_return_names()):
|
| 534 |
+
if name is not None:
|
| 535 |
+
aliased_returns.append(name)
|
| 536 |
+
else:
|
| 537 |
+
non_aliased_returns.append(
|
| 538 |
+
inner_return_var
|
| 539 |
+
if len(f.func.returns) == 1
|
| 540 |
+
else f"std::get<{i}>({inner_return_var})"
|
| 541 |
+
)
|
| 542 |
+
return aliased_returns, non_aliased_returns
|
| 543 |
+
|
| 544 |
+
|
| 545 |
+
# When functionalization "no-op's" and redispatches on a mutable operator, we need to take care so that:
|
| 546 |
+
# - For fresh outputs, we return the result of the redispatch (without wrapping outputs)
|
| 547 |
+
# - For outputs that were aliased to inputs, we return the inputs directly (since some of them might have been wrapped)
|
| 548 |
+
def return_from_mutable_noop_redispatch(
|
| 549 |
+
f: NativeFunction, inner_return_var: str
|
| 550 |
+
) -> str:
|
| 551 |
+
aliased, non_aliased = get_mutable_redispatch_return_names(f, inner_return_var)
|
| 552 |
+
# Just get all of the return names, and immediately return them
|
| 553 |
+
return return_str(f.func.returns, aliased + non_aliased)
|
| 554 |
+
|
| 555 |
+
|
| 556 |
+
def wrap_propagate_mutations_and_return(
|
| 557 |
+
f: NativeFunction, functional_op: NativeFunction, inner_return_var: str
|
| 558 |
+
) -> str:
|
| 559 |
+
mutable_arg_names = f.func.arguments.mutable_arg_names()
|
| 560 |
+
(
|
| 561 |
+
aliased_outer_rets,
|
| 562 |
+
non_aliased_outer_rets,
|
| 563 |
+
) = get_mutable_redispatch_return_names(f, inner_return_var)
|
| 564 |
+
_, non_aliased_inner_rets = get_mutable_redispatch_return_names(
|
| 565 |
+
functional_op, inner_return_var
|
| 566 |
+
)
|
| 567 |
+
# The outer function may have a mix of aliased and non-aliased outputs,
|
| 568 |
+
# But the inner functional op that we're transforming to should only have non-aliased outputs
|
| 569 |
+
assert len(mutable_arg_names) + len(non_aliased_outer_rets) == len(
|
| 570 |
+
non_aliased_inner_rets
|
| 571 |
+
)
|
| 572 |
+
|
| 573 |
+
# First, take all of the newly created outputs from the inner call and wrap them into functional tensors
|
| 574 |
+
updates = []
|
| 575 |
+
non_aliased_wrapped_ret_names = []
|
| 576 |
+
for i, inner_ret in enumerate(
|
| 577 |
+
non_aliased_inner_rets[: len(non_aliased_outer_rets)]
|
| 578 |
+
):
|
| 579 |
+
ret_name = f"output_{i}"
|
| 580 |
+
updates.append(
|
| 581 |
+
f"""\
|
| 582 |
+
auto output_{i} = at::functionalization::impl::to_functional_tensor({inner_ret});"""
|
| 583 |
+
)
|
| 584 |
+
non_aliased_wrapped_ret_names.append(ret_name)
|
| 585 |
+
|
| 586 |
+
# Next, take all of the mutated outputs from the inner call corresponding to mutated inputs,
|
| 587 |
+
# and propagate the mutations
|
| 588 |
+
for outer_arg, inner_ret in zip(
|
| 589 |
+
mutable_arg_names, non_aliased_inner_rets[len(non_aliased_outer_rets) :]
|
| 590 |
+
):
|
| 591 |
+
updates.append(
|
| 592 |
+
f"""\
|
| 593 |
+
auto {outer_arg}_inner = at::functionalization::impl::from_functional_tensor({outer_arg});
|
| 594 |
+
at::functionalization::impl::replace_({outer_arg}, {inner_ret});
|
| 595 |
+
at::functionalization::impl::commit_update({outer_arg});
|
| 596 |
+
at::functionalization::impl::sync({outer_arg});
|
| 597 |
+
auto {outer_arg}_inner_updated = at::functionalization::impl::from_functional_tensor({outer_arg});
|
| 598 |
+
at::functionalization::impl::propagate_xla_data_direct({outer_arg}_inner, {outer_arg}_inner_updated);"""
|
| 599 |
+
)
|
| 600 |
+
|
| 601 |
+
# Finally, we return:
|
| 602 |
+
# - Any mutable arguments that also returns
|
| 603 |
+
# - Any immutable returns that were created wrapping the output from the inner call
|
| 604 |
+
returns_str = return_str(
|
| 605 |
+
f.func.returns, aliased_outer_rets + non_aliased_wrapped_ret_names
|
| 606 |
+
)
|
| 607 |
+
updates_str = "\n".join(updates)
|
| 608 |
+
return f"""\
|
| 609 |
+
{updates_str}
|
| 610 |
+
{returns_str}"""
|
| 611 |
+
|
| 612 |
+
|
| 613 |
+
# Generates the Functionalization kernel for:
|
| 614 |
+
# - mutation ops (inplace and out= ops)
|
| 615 |
+
@with_native_function_and
|
| 616 |
+
def emit_inplace_functionalization_body(
|
| 617 |
+
f: NativeFunction, g: NativeFunctionsGroup
|
| 618 |
+
) -> str:
|
| 619 |
+
# mutation case
|
| 620 |
+
assert modifies_arguments(f)
|
| 621 |
+
|
| 622 |
+
dispatcher_sig = DispatcherSignature.from_schema(f.func)
|
| 623 |
+
|
| 624 |
+
unwrap_tensor_args_str, unwrapped_args_ctx = unwrap_tensor_args(
|
| 625 |
+
dispatcher_sig, is_view_op=False
|
| 626 |
+
)
|
| 627 |
+
|
| 628 |
+
mutated_names = [
|
| 629 |
+
a.name
|
| 630 |
+
for a in f.func.arguments.flat_all
|
| 631 |
+
if a.type.is_tensor_like() and a.annotation is not None
|
| 632 |
+
]
|
| 633 |
+
non_mutated_names = [
|
| 634 |
+
a.name
|
| 635 |
+
for a in f.func.arguments.flat_all
|
| 636 |
+
if a.type.is_tensor_like() and a.annotation is None
|
| 637 |
+
]
|
| 638 |
+
non_mutated_tensor_names = [
|
| 639 |
+
a.name
|
| 640 |
+
for a in f.func.arguments.flat_all
|
| 641 |
+
if a.type == BaseType(BaseTy.Tensor) and a.annotation is None
|
| 642 |
+
]
|
| 643 |
+
# all mutable inputs must be functional tensors in order to participate in functionalization
|
| 644 |
+
check_all_mutated_args_are_functional = " && ".join(
|
| 645 |
+
["true"]
|
| 646 |
+
+ [
|
| 647 |
+
f"at::functionalization::impl::isFunctionalTensor({a})"
|
| 648 |
+
for a in mutated_names
|
| 649 |
+
]
|
| 650 |
+
)
|
| 651 |
+
check_any_non_mutated_args_are_functional = " || ".join(
|
| 652 |
+
["false"]
|
| 653 |
+
+ [
|
| 654 |
+
f"at::functionalization::impl::isFunctionalTensor({a})"
|
| 655 |
+
for a in non_mutated_names
|
| 656 |
+
]
|
| 657 |
+
)
|
| 658 |
+
|
| 659 |
+
check_any_non_mutated_tensors_are_xla = " || ".join(
|
| 660 |
+
["false"]
|
| 661 |
+
+ [
|
| 662 |
+
f"{a}.device().type() == c10::DeviceType::XLA"
|
| 663 |
+
for a in non_mutated_tensor_names
|
| 664 |
+
]
|
| 665 |
+
)
|
| 666 |
+
# These are used in the cases where we don't functionalize and redispatch to the inplace op
|
| 667 |
+
# case 1: we hit an inplace op that doesn't have an out-of-place equivalent
|
| 668 |
+
# case 2: we hit an inplace ops but our inputs are not functional tensors (in which case our kernel just no-ops)
|
| 669 |
+
inplace_exprs = [
|
| 670 |
+
e.expr
|
| 671 |
+
for e in translate(unwrapped_args_ctx, dispatcher_sig.arguments(), method=False)
|
| 672 |
+
]
|
| 673 |
+
|
| 674 |
+
# call the out-of-place variant of the op
|
| 675 |
+
return_type = (
|
| 676 |
+
dispatcher.returns_type(g.functional.func.returns).remove_const_ref().cpp_type()
|
| 677 |
+
)
|
| 678 |
+
functional_sig = DispatcherSignature.from_schema(g.functional.func)
|
| 679 |
+
functional_exprs = [
|
| 680 |
+
e.expr
|
| 681 |
+
for e in translate(unwrapped_args_ctx, functional_sig.arguments(), method=False)
|
| 682 |
+
]
|
| 683 |
+
|
| 684 |
+
if f.func.is_out_fn():
|
| 685 |
+
mutable_input_post_processing = "\n".join(
|
| 686 |
+
[
|
| 687 |
+
f"""
|
| 688 |
+
at::functionalization::impl::replace_(
|
| 689 |
+
{a.name}, {'std::get<' + str(i) + '>(tmp_output)' if len(f.func.returns) > 1 else 'tmp_output'});
|
| 690 |
+
at::functionalization::impl::commit_update({a.name});"""
|
| 691 |
+
for (i, a) in enumerate(f.func.arguments.out)
|
| 692 |
+
if a.annotation and a.annotation.is_write and a.type.is_tensor_like()
|
| 693 |
+
]
|
| 694 |
+
)
|
| 695 |
+
else:
|
| 696 |
+
mutable_input_post_processing = "\n".join( # noqa: F841
|
| 697 |
+
[
|
| 698 |
+
f"""
|
| 699 |
+
at::functionalization::impl::replace_({a.name}, tmp_output);
|
| 700 |
+
at::functionalization::impl::commit_update({a.name});"""
|
| 701 |
+
for a in f.func.arguments.flat_all
|
| 702 |
+
if a.annotation and a.annotation.is_write and a.type.is_tensor_like()
|
| 703 |
+
]
|
| 704 |
+
)
|
| 705 |
+
|
| 706 |
+
meta_conversion_str, meta_call_ctx = convert_to_meta_tensors(dispatcher_sig)
|
| 707 |
+
# We don't want to run the inplace meta func for ops like .set_(), because:
|
| 708 |
+
# (1) they're unnecessary: inplace meta checks are only useful for ops like add_(),
|
| 709 |
+
# where broadcasting will work for the out-of-place case but should fail on the inplace call
|
| 710 |
+
# (2) They'll also fail without adding extra infra: we'd need to convert the input storage argument
|
| 711 |
+
# into a meta storage
|
| 712 |
+
any_storage_args = any(
|
| 713 |
+
a.type == BaseType(BaseTy.Storage) for a in f.func.arguments.flat_all
|
| 714 |
+
)
|
| 715 |
+
|
| 716 |
+
return f"""
|
| 717 |
+
{dispatcher_sig.defn(name=wrapper_name(f.func), is_redispatching_fn=True)} {{
|
| 718 |
+
if ({str(not any_storage_args and f.func.kind() == SchemaKind.inplace).lower()}) {{
|
| 719 |
+
// Before converting the mutable op to its functional variant, run meta tensors through the original op.
|
| 720 |
+
// This will help us catch shape errors that apply to inplace ops that wouldn't apply to their functional variants.
|
| 721 |
+
// (We can only do this for inplace ops today though, because they technically all support meta tensors).
|
| 722 |
+
{meta_conversion_str}
|
| 723 |
+
at::AutoDispatchSkipFunctionalize func_guard;
|
| 724 |
+
c10::impl::ExcludeDispatchKeyGuard guard(exclude_keys_for_meta_dispatch);
|
| 725 |
+
at::_ops::{f.func.name.unambiguous_name()}::call({', '.join(a.name for a in meta_call_ctx)});
|
| 726 |
+
}}
|
| 727 |
+
{unwrap_tensor_args_str}
|
| 728 |
+
if (!({check_all_mutated_args_are_functional})) {{
|
| 729 |
+
// We want to disable this check if there are any XLA tensors.
|
| 730 |
+
// cpu_tensor.copy_(xla_tensor) is valid code.
|
| 731 |
+
if (!({check_any_non_mutated_tensors_are_xla}) && ({check_any_non_mutated_args_are_functional})) {{
|
| 732 |
+
// case 1: trying to mutate a non functional tensor with a functional tensor is an error
|
| 733 |
+
TORCH_INTERNAL_ASSERT(false,
|
| 734 |
+
"mutating a non-functional tensor with a functional tensor is not allowed.",
|
| 735 |
+
" Please ensure that all of your inputs are wrapped inside of a functionalize() call.");
|
| 736 |
+
}} else {{
|
| 737 |
+
// case 2: arguments are not functional tensors, so we no-op and redispatch.
|
| 738 |
+
at::AutoDispatchSkipFunctionalize guard;
|
| 739 |
+
{maybe_create_output(f, 'tmp_output')}at::_ops::{f.func.name.unambiguous_name()}::call({', '.join(inplace_exprs)});
|
| 740 |
+
{return_from_mutable_noop_redispatch(f, 'tmp_output')}
|
| 741 |
+
}}
|
| 742 |
+
}} else {{
|
| 743 |
+
{return_type} tmp_output;
|
| 744 |
+
{{
|
| 745 |
+
at::AutoDispatchSkipFunctionalize guard;
|
| 746 |
+
tmp_output = at::_ops::{g.functional.func.name.unambiguous_name()}::call({', '.join(functional_exprs)});
|
| 747 |
+
}}
|
| 748 |
+
{wrap_propagate_mutations_and_return(f, g.functional, 'tmp_output')}
|
| 749 |
+
}}
|
| 750 |
+
}}"""
|
| 751 |
+
|
| 752 |
+
|
| 753 |
+
# The below functions generate RegisterFunctionalization.cpp
|
| 754 |
+
# These files provide the kernels that run the functionalization pass, which can be opted into
|
| 755 |
+
# per backend (e.g. XLA or Vulkan), or as a composable transform (functionalize() in functorch).
|
| 756 |
+
|
| 757 |
+
|
| 758 |
+
# See Note [Functionalization Pass: View Inverses].
|
| 759 |
+
def gen_functionalization_view_inverse_declaration(
|
| 760 |
+
selector: SelectiveBuilder, g: NativeFunctionsViewGroup
|
| 761 |
+
) -> str | None:
|
| 762 |
+
# For every (non-composite) view op, we need a corresponding "inverse view" function.
|
| 763 |
+
# This generates the declarations so we get a good compiler error when someone adds a new view.
|
| 764 |
+
@with_native_function
|
| 765 |
+
def emit_decl_helper(g: NativeFunctionsViewGroup) -> str | None:
|
| 766 |
+
if g.view.has_composite_implicit_autograd_kernel:
|
| 767 |
+
return None
|
| 768 |
+
view_inverse_sig = ViewInverseSignature(g)
|
| 769 |
+
return view_inverse_sig.decl()
|
| 770 |
+
|
| 771 |
+
return emit_decl_helper(g)
|
| 772 |
+
|
| 773 |
+
|
| 774 |
+
def gen_functionalization_registration(
|
| 775 |
+
selector: SelectiveBuilder,
|
| 776 |
+
g: NativeFunction | NativeFunctionsGroup | NativeFunctionsViewGroup,
|
| 777 |
+
composite_implicit_autograd_index: BackendIndex,
|
| 778 |
+
) -> list[str]:
|
| 779 |
+
@with_native_function
|
| 780 |
+
def emit_registration_helper(f: NativeFunction) -> str:
|
| 781 |
+
assert not f.has_composite_implicit_autograd_kernel
|
| 782 |
+
registration_str = f"TORCH_FN(functionalization::{wrapper_name(f.func)})"
|
| 783 |
+
return f'm.impl("{f.func.name}", {registration_str});'
|
| 784 |
+
|
| 785 |
+
# Don't generate kernels in mobile build
|
| 786 |
+
if not selector.include_all_operators:
|
| 787 |
+
return []
|
| 788 |
+
|
| 789 |
+
if isinstance(g, NativeFunctionsViewGroup):
|
| 790 |
+
# functionalization needs to register kernels for view + view_inplace ops
|
| 791 |
+
# See Note [Functionalization <> torch.Tensor constructor]
|
| 792 |
+
if str(g.view.func.name) == "lift_fresh":
|
| 793 |
+
return []
|
| 794 |
+
view_str = []
|
| 795 |
+
if not g.view.has_composite_implicit_autograd_kernel:
|
| 796 |
+
view_str.append(emit_registration_helper(g.view))
|
| 797 |
+
if (
|
| 798 |
+
g.view_inplace is not None
|
| 799 |
+
and not g.view_inplace.has_composite_implicit_autograd_kernel
|
| 800 |
+
):
|
| 801 |
+
assert g.view_inplace.is_view_op
|
| 802 |
+
view_str.append(emit_registration_helper(g.view_inplace))
|
| 803 |
+
return view_str
|
| 804 |
+
|
| 805 |
+
elif isinstance(g, NativeFunctionsGroup):
|
| 806 |
+
# Gets a hand-written functionalization kernel
|
| 807 |
+
if g.inplace is not None and str(g.inplace.func.name) == "set_.source_Tensor":
|
| 808 |
+
fns = []
|
| 809 |
+
else:
|
| 810 |
+
fns = list(g.functions())
|
| 811 |
+
else:
|
| 812 |
+
if str(g.func.name) in MUTABLE_OPS_NOT_USING_FUNCTIONALIZATION:
|
| 813 |
+
return []
|
| 814 |
+
fns = [g]
|
| 815 |
+
|
| 816 |
+
registrations = []
|
| 817 |
+
for f in fns:
|
| 818 |
+
if f.has_composite_implicit_autograd_kernel:
|
| 819 |
+
continue
|
| 820 |
+
if str(f.func.name) == "lift":
|
| 821 |
+
# See Note [Functionalization <> torch.Tensor constructor]
|
| 822 |
+
return []
|
| 823 |
+
if str(f.func.name) == "resize_":
|
| 824 |
+
# See Note [resize_ in Functionalization]
|
| 825 |
+
return []
|
| 826 |
+
if str(f.func.name.name) != "set_":
|
| 827 |
+
assert not f.is_view_op
|
| 828 |
+
# functionalization needs to generate and register kernels for inplace ops.
|
| 829 |
+
# We *also* need to directly register CompositeImplicitAUtograd kernels
|
| 830 |
+
# so that they decompose properly before functioanlization.
|
| 831 |
+
if modifies_arguments(f):
|
| 832 |
+
registrations.append(emit_registration_helper(f))
|
| 833 |
+
return registrations
|
| 834 |
+
|
| 835 |
+
|
| 836 |
+
def gen_functionalization_definition(
|
| 837 |
+
selector: SelectiveBuilder,
|
| 838 |
+
# Note: Ideally this code should never have to look at NativeFunction
|
| 839 |
+
# (and instead only need to operate on grouped NativeFunctions).
|
| 840 |
+
# The only reason currently is because we need to emit direct dispatch registrations
|
| 841 |
+
# For CompositeImplicitAutograd operators, which are potentially ungrouped.
|
| 842 |
+
g: NativeFunction | NativeFunctionsGroup | NativeFunctionsViewGroup,
|
| 843 |
+
) -> list[str]:
|
| 844 |
+
# Don't generate kernels in mobile build
|
| 845 |
+
if not selector.include_all_operators:
|
| 846 |
+
return []
|
| 847 |
+
|
| 848 |
+
if isinstance(g, NativeFunctionsViewGroup):
|
| 849 |
+
# Case 1: emit view -> view_copy kernels for the functionalization pass
|
| 850 |
+
view_defs = []
|
| 851 |
+
if not g.composite:
|
| 852 |
+
# invariant: NativeFunctionsViewGroup's always have a view_copy operator
|
| 853 |
+
# if the view is not composite (implicit autograd)
|
| 854 |
+
assert g.view_copy is not None, dataclass_repr(g, indent=1)
|
| 855 |
+
view_defs.append(emit_view_functionalization_body(g, view_inplace=False))
|
| 856 |
+
if g.view_inplace is not None:
|
| 857 |
+
view_defs.append(emit_view_functionalization_body(g, view_inplace=True))
|
| 858 |
+
return view_defs
|
| 859 |
+
elif isinstance(g, NativeFunction):
|
| 860 |
+
# Invariant: all mutable operators that we need to handle in functionalization
|
| 861 |
+
# should have been properly grouped up.
|
| 862 |
+
# TODO: The below ops all have "problematic" schemas that prevent them from
|
| 863 |
+
# getting functionalized. Instead of bending over backwards to get things to work,
|
| 864 |
+
# I think we should either:
|
| 865 |
+
# (1) fix their schemas (BC-breaking)
|
| 866 |
+
# (2) hand-write their functionalization kernels
|
| 867 |
+
if (
|
| 868 |
+
str(g.func.name) not in MUTABLE_OPS_NOT_USING_FUNCTIONALIZATION
|
| 869 |
+
and str(g.func.name.name) not in MUTABLE_OPS_NOT_USING_FUNCTIONALIZATION
|
| 870 |
+
):
|
| 871 |
+
assert g.has_composite_implicit_autograd_kernel or not modifies_arguments(g)
|
| 872 |
+
return []
|
| 873 |
+
else:
|
| 874 |
+
# Case 2: emit inplace -> out-of-place kernels for the functionalization pass
|
| 875 |
+
mutation_defs = []
|
| 876 |
+
mutation_defs.append(emit_inplace_functionalization_body(g.out, g))
|
| 877 |
+
if g.inplace is not None:
|
| 878 |
+
mutation_defs.append(emit_inplace_functionalization_body(g.inplace, g))
|
| 879 |
+
if g.mutable is not None:
|
| 880 |
+
mutation_defs.append(emit_inplace_functionalization_body(g.mutable, g))
|
| 881 |
+
return mutation_defs
|
| 882 |
+
return []
|
gen_lazy_tensor.py
ADDED
|
@@ -0,0 +1,585 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
import argparse
|
| 4 |
+
import os
|
| 5 |
+
from collections import namedtuple
|
| 6 |
+
from pathlib import Path
|
| 7 |
+
from typing import Any, Callable, TYPE_CHECKING
|
| 8 |
+
|
| 9 |
+
import yaml
|
| 10 |
+
|
| 11 |
+
import torchgen.dest as dest
|
| 12 |
+
from torchgen.api.lazy import setValueT
|
| 13 |
+
from torchgen.api.types import BaseCppType
|
| 14 |
+
from torchgen.dest.lazy_ir import GenLazyIR, GenLazyNativeFuncDefinition, GenTSLazyIR
|
| 15 |
+
from torchgen.gen import get_grouped_native_functions, parse_native_yaml
|
| 16 |
+
from torchgen.gen_backend_stubs import (
|
| 17 |
+
error_on_missing_kernels,
|
| 18 |
+
gen_dispatcher_registrations,
|
| 19 |
+
gen_dispatchkey_nativefunc_headers,
|
| 20 |
+
parse_backend_yaml,
|
| 21 |
+
)
|
| 22 |
+
from torchgen.model import NativeFunction, NativeFunctionsGroup, OperatorName
|
| 23 |
+
from torchgen.selective_build.selector import SelectiveBuilder
|
| 24 |
+
from torchgen.utils import FileManager, NamespaceHelper
|
| 25 |
+
from torchgen.yaml_utils import YamlLoader
|
| 26 |
+
|
| 27 |
+
|
| 28 |
+
if TYPE_CHECKING:
|
| 29 |
+
from collections.abc import Iterable, Iterator, Sequence
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #
|
| 33 |
+
#
|
| 34 |
+
# Lazy Tensor Codegen
|
| 35 |
+
#
|
| 36 |
+
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #
|
| 37 |
+
# Overview
|
| 38 |
+
# ~~~~~~~~
|
| 39 |
+
#
|
| 40 |
+
# This codegen script builds on existing data models and helpers used
|
| 41 |
+
# by all ATen backends, and adds new functionality specific to lazy
|
| 42 |
+
# tensor backends.
|
| 43 |
+
#
|
| 44 |
+
# Inputs:
|
| 45 |
+
# - <backend>_native_functions.yaml: controls which operators are
|
| 46 |
+
# supported by the backend.
|
| 47 |
+
#
|
| 48 |
+
# Outputs:
|
| 49 |
+
# (for all backends)
|
| 50 |
+
# <DispatchKey>Ir.h defines Lazy IR classes to be constructed during tracing
|
| 51 |
+
# - opt-in: also generate 'lowering' methods for the TorchScript backend only
|
| 52 |
+
# <DispatchKey>NativeFunctions.cpp defines implementations of native functions which perform lazy tracing
|
| 53 |
+
# - opt-in: 'full_codegen' section of backend yaml; 'supported' section omits these implementations
|
| 54 |
+
# <DispatchKey>NativeFunctions.h declares implementations of native functions for both 'supported' and 'full_codegen'
|
| 55 |
+
# ops
|
| 56 |
+
#
|
| 57 |
+
# Register<DispatchKey>.cpp registers all op implementations with the dispatcher
|
| 58 |
+
# RegisterAutograd<DispatchKey>.cpp registers all autograd implementations with the dispatcher
|
| 59 |
+
#
|
| 60 |
+
# Validation Helpers:
|
| 61 |
+
# - Shape Inference: errs if any ops in backend yaml require shape inference not provided by meta kernels or
|
| 62 |
+
# implementations in torch/csrc/lazy/core/shape_inference.*
|
| 63 |
+
# - native function impls: errs if any 'supported' ops do not have an implementation defined in the backend
|
| 64 |
+
# (non-codegen) implementation file
|
| 65 |
+
#
|
| 66 |
+
#
|
| 67 |
+
# About the Data Model
|
| 68 |
+
# ~~~~~~~~~~~~~~~~~~~~
|
| 69 |
+
#
|
| 70 |
+
# Modeled after ATen codegen, the first step is to parse yaml and build a data model for the operators
|
| 71 |
+
# we care about. In this case, the <backend>_native_functions yaml defines a subset of the core operators
|
| 72 |
+
# (defined in more detail in the main native_functions.yaml), which will be supported by your backend.
|
| 73 |
+
# Backends can list ops in two categories:
|
| 74 |
+
# - `supported` ops require hand-implementations but still get codegenned declarations and registrations
|
| 75 |
+
# - `full_codegen` ops get implementations (and IR classes) generated too
|
| 76 |
+
#
|
| 77 |
+
# Each native function is modeled as an object with a schema, and each schema has objects representing their
|
| 78 |
+
# arguments. Much of the codegen is manipulation of the arguments and their types. For example, lazy tensor
|
| 79 |
+
# backends need to transform 'at::Tensor' arguments into 'lazy::Value' objects, as well as replacing reference
|
| 80 |
+
# types (stringref) with actual string objects, and this is done by manipulating the data model objects.
|
| 81 |
+
# - see api/lazy.py for the lazy data model
|
| 82 |
+
#
|
| 83 |
+
# Once the data model is set up, the rest of this script processes a number of templates for output CPP file
|
| 84 |
+
# and fills in the template values using helpers in `dest/lazy_ir.py` and `dest/lazy_ts_lowering.py`. These
|
| 85 |
+
# helpers mostly iterate over functions and their arguments, outputting different c++ snippets.
|
| 86 |
+
#
|
| 87 |
+
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #
|
| 88 |
+
|
| 89 |
+
|
| 90 |
+
# Parses the external backend's yaml, and adds a new BackendIndex for the backend's dispatch key.
|
| 91 |
+
# Returns a Tuple of (backend_key, autograd_key, cpp_namespace, updated BackendIndex mapping, full_codegen)
|
| 92 |
+
ParsedExternalYaml = namedtuple(
|
| 93 |
+
"ParsedExternalYaml",
|
| 94 |
+
["backend_key", "autograd_key", "cpp_namespace", "backend_indices", "full_codegen"],
|
| 95 |
+
)
|
| 96 |
+
|
| 97 |
+
|
| 98 |
+
def parse_native_functions_keys(
|
| 99 |
+
backend_yaml_path: str,
|
| 100 |
+
grouped_native_functions: Sequence[NativeFunction | NativeFunctionsGroup],
|
| 101 |
+
) -> tuple[list[OperatorName], list[Any], list[OperatorName]]:
|
| 102 |
+
with open(backend_yaml_path) as f:
|
| 103 |
+
yaml_values = yaml.load(f, Loader=YamlLoader)
|
| 104 |
+
assert isinstance(yaml_values, dict)
|
| 105 |
+
|
| 106 |
+
full_codegen = yaml_values.pop("full_codegen", [])
|
| 107 |
+
non_native = yaml_values.pop("non_native", [])
|
| 108 |
+
ir_gen = yaml_values.pop("ir_gen", [])
|
| 109 |
+
assert isinstance(full_codegen, list)
|
| 110 |
+
assert isinstance(non_native, list)
|
| 111 |
+
assert isinstance(ir_gen, list)
|
| 112 |
+
full_codegen_opnames = [OperatorName.parse(name) for name in full_codegen]
|
| 113 |
+
ir_gen_opnames = [OperatorName.parse(name) for name in ir_gen]
|
| 114 |
+
return full_codegen_opnames, non_native, ir_gen_opnames
|
| 115 |
+
|
| 116 |
+
|
| 117 |
+
def validate_shape_inference_header(
|
| 118 |
+
shape_inference_hdr: str, expected_shape_infr_decls: list[str]
|
| 119 |
+
) -> None:
|
| 120 |
+
try:
|
| 121 |
+
with open(shape_inference_hdr) as f:
|
| 122 |
+
shape_infr_decls = f.read()
|
| 123 |
+
shape_infr_decl_lines = set(shape_infr_decls.split("\n"))
|
| 124 |
+
except OSError as e:
|
| 125 |
+
raise AssertionError(
|
| 126 |
+
f"Unable to read from the specified shape_inference_hdr file: {shape_inference_hdr}"
|
| 127 |
+
) from e
|
| 128 |
+
|
| 129 |
+
# TODO(whc) add a check for shape inference functions that have meta kernels implement and should be retired.
|
| 130 |
+
|
| 131 |
+
missing_decls = [
|
| 132 |
+
decl for decl in expected_shape_infr_decls if decl not in shape_infr_decl_lines
|
| 133 |
+
]
|
| 134 |
+
if missing_decls:
|
| 135 |
+
raise Exception( # noqa: TRY002
|
| 136 |
+
f"""Missing shape inference function.\n
|
| 137 |
+
Please add declare this function in {shape_inference_hdr}:\n
|
| 138 |
+
and implement it in the corresponding shape_inference.cpp file.\n
|
| 139 |
+
{os.linesep.join(missing_decls)}"""
|
| 140 |
+
)
|
| 141 |
+
|
| 142 |
+
|
| 143 |
+
# Some helper functions for the codegen.
|
| 144 |
+
def get_ltc_helper_fns() -> str:
|
| 145 |
+
return """\
|
| 146 |
+
at::Tensor to_meta(const at::Tensor& tensor) {
|
| 147 |
+
// undefined tensors can't be converted to the meta device, since they don't have sizes/strides
|
| 148 |
+
if (!tensor.defined()) return tensor;
|
| 149 |
+
auto out = at::native::empty_strided_meta_symint(tensor.sym_sizes(), tensor.sym_strides(), \
|
| 150 |
+
/*dtype=*/tensor.scalar_type(), /*layout=*/tensor.layout(), \
|
| 151 |
+
/*device=*/c10::Device(c10::kMeta), /*pin_memory=*/std::nullopt);
|
| 152 |
+
// needs to handle wrapped numbers, so dtype promotion works properly.
|
| 153 |
+
if (tensor.unsafeGetTensorImpl()->is_wrapped_number()) {
|
| 154 |
+
out.unsafeGetTensorImpl()->set_wrapped_number(true);
|
| 155 |
+
}
|
| 156 |
+
return out;
|
| 157 |
+
}
|
| 158 |
+
std::optional<at::Tensor> to_meta(const std::optional<at::Tensor>& tensor) {
|
| 159 |
+
if (tensor.has_value()) {
|
| 160 |
+
return to_meta(*tensor);
|
| 161 |
+
}
|
| 162 |
+
return std::nullopt;
|
| 163 |
+
}
|
| 164 |
+
|
| 165 |
+
std::vector<at::Tensor> to_meta(at::ITensorListRef t_list) {
|
| 166 |
+
std::vector<at::Tensor> outs;
|
| 167 |
+
outs.reserve(t_list.size());
|
| 168 |
+
for (const auto& tensor : t_list) {
|
| 169 |
+
outs.push_back(to_meta(tensor));
|
| 170 |
+
}
|
| 171 |
+
return outs;
|
| 172 |
+
}
|
| 173 |
+
"""
|
| 174 |
+
|
| 175 |
+
|
| 176 |
+
class default_args:
|
| 177 |
+
node_base: str = "Node"
|
| 178 |
+
node_base_hdr: str | None = None
|
| 179 |
+
shape_inference_hdr: str = "torch/csrc/lazy/core/shape_inference.h"
|
| 180 |
+
tensor_class: str = "torch::lazy::LazyTensor"
|
| 181 |
+
tensor_class_hdr: str = "torch/csrc/lazy/core/tensor.h"
|
| 182 |
+
lazy_ir_generator: type[GenLazyIR] = GenLazyIR
|
| 183 |
+
native_func_definition_generator: type[GenLazyNativeFuncDefinition] = (
|
| 184 |
+
GenLazyNativeFuncDefinition
|
| 185 |
+
)
|
| 186 |
+
backend_name: str = "TorchScript"
|
| 187 |
+
|
| 188 |
+
|
| 189 |
+
def main() -> None:
|
| 190 |
+
parser = argparse.ArgumentParser(description="Generate Lazy Tensor backend files")
|
| 191 |
+
parser.add_argument(
|
| 192 |
+
"-s",
|
| 193 |
+
"--source-yaml",
|
| 194 |
+
"--source_yaml",
|
| 195 |
+
help="path to source yaml file containing operator external definitions",
|
| 196 |
+
)
|
| 197 |
+
parser.add_argument("-o", "--output-dir", "--output_dir", help="output directory")
|
| 198 |
+
parser.add_argument(
|
| 199 |
+
"--dry-run", "--dry_run", type=bool, default=False, help="output directory"
|
| 200 |
+
)
|
| 201 |
+
parser.add_argument(
|
| 202 |
+
"--impl-path",
|
| 203 |
+
"--impl_path",
|
| 204 |
+
type=str,
|
| 205 |
+
default=None,
|
| 206 |
+
help="path to the source C++ file containing kernel definitions",
|
| 207 |
+
)
|
| 208 |
+
parser.add_argument(
|
| 209 |
+
"--gen-ts-lowerings",
|
| 210 |
+
"--gen_ts_lowerings",
|
| 211 |
+
action="store_true",
|
| 212 |
+
help="Generate TorchScript lowerings in addition to Lazy IR and NativeFunctions",
|
| 213 |
+
)
|
| 214 |
+
parser.add_argument(
|
| 215 |
+
"--node-base",
|
| 216 |
+
"--node_base",
|
| 217 |
+
type=str,
|
| 218 |
+
default=default_args.node_base,
|
| 219 |
+
help="Name of backend specific custom Lazy IR Node base class",
|
| 220 |
+
)
|
| 221 |
+
parser.add_argument(
|
| 222 |
+
"--node-base-hdr",
|
| 223 |
+
"--node_base_hdr",
|
| 224 |
+
type=str,
|
| 225 |
+
default=default_args.node_base_hdr,
|
| 226 |
+
help="Path to header file defining custom Lazy IR Node base class",
|
| 227 |
+
)
|
| 228 |
+
parser.add_argument(
|
| 229 |
+
"--shape-inference-hdr",
|
| 230 |
+
"--shape_inference_hdr",
|
| 231 |
+
type=str,
|
| 232 |
+
default=default_args.shape_inference_hdr,
|
| 233 |
+
help="Path to header file defining custom Lazy shape inference functions",
|
| 234 |
+
)
|
| 235 |
+
parser.add_argument(
|
| 236 |
+
"--tensor-class",
|
| 237 |
+
"--tensor_class",
|
| 238 |
+
type=str,
|
| 239 |
+
default=default_args.tensor_class,
|
| 240 |
+
help="Name of backend specific custom Lazy Tensor class",
|
| 241 |
+
)
|
| 242 |
+
parser.add_argument(
|
| 243 |
+
"--tensor-class-hdr",
|
| 244 |
+
"--tensor_class_hdr",
|
| 245 |
+
type=str,
|
| 246 |
+
default=default_args.tensor_class_hdr,
|
| 247 |
+
help="Path to header file defining custom Lazy Tensor class",
|
| 248 |
+
)
|
| 249 |
+
parser.add_argument(
|
| 250 |
+
"--backend-name",
|
| 251 |
+
"--backend_name",
|
| 252 |
+
type=str,
|
| 253 |
+
default=default_args.backend_name,
|
| 254 |
+
help="Name of the backend to generate",
|
| 255 |
+
)
|
| 256 |
+
options = parser.parse_args()
|
| 257 |
+
|
| 258 |
+
# Assumes that this file lives at PYTORCH_ROOT/torchgen/gen_backend_stubs.py
|
| 259 |
+
torch_root = Path(__file__).parent.parent.parent.absolute()
|
| 260 |
+
aten_path = str(torch_root / "aten" / "src" / "ATen")
|
| 261 |
+
lazy_ir_generator: type[GenLazyIR] = default_args.lazy_ir_generator
|
| 262 |
+
if options.gen_ts_lowerings:
|
| 263 |
+
lazy_ir_generator = GenTSLazyIR
|
| 264 |
+
native_func_definition_generator: type[GenLazyNativeFuncDefinition] = (
|
| 265 |
+
default_args.native_func_definition_generator
|
| 266 |
+
)
|
| 267 |
+
|
| 268 |
+
run_gen_lazy_tensor(
|
| 269 |
+
aten_path,
|
| 270 |
+
options.source_yaml,
|
| 271 |
+
options.output_dir,
|
| 272 |
+
options.dry_run,
|
| 273 |
+
options.impl_path,
|
| 274 |
+
options.node_base,
|
| 275 |
+
options.node_base_hdr,
|
| 276 |
+
options.tensor_class,
|
| 277 |
+
options.tensor_class_hdr,
|
| 278 |
+
options.shape_inference_hdr,
|
| 279 |
+
lazy_ir_generator,
|
| 280 |
+
native_func_definition_generator,
|
| 281 |
+
options.backend_name,
|
| 282 |
+
)
|
| 283 |
+
|
| 284 |
+
|
| 285 |
+
def run_gen_lazy_tensor(
|
| 286 |
+
aten_path: str,
|
| 287 |
+
source_yaml: str,
|
| 288 |
+
output_dir: str,
|
| 289 |
+
dry_run: bool,
|
| 290 |
+
impl_path: str | None,
|
| 291 |
+
node_base: str = default_args.node_base,
|
| 292 |
+
node_base_hdr: str | None = default_args.node_base_hdr,
|
| 293 |
+
tensor_class: str = default_args.tensor_class,
|
| 294 |
+
tensor_class_hdr: str = default_args.tensor_class_hdr,
|
| 295 |
+
shape_inference_hdr: str = default_args.shape_inference_hdr,
|
| 296 |
+
lazy_ir_generator: type[GenLazyIR] = default_args.lazy_ir_generator,
|
| 297 |
+
native_func_definition_generator: type[
|
| 298 |
+
GenLazyNativeFuncDefinition
|
| 299 |
+
] = default_args.native_func_definition_generator,
|
| 300 |
+
# build_in_tree is true for TS backend and affects include paths
|
| 301 |
+
build_in_tree: bool = False,
|
| 302 |
+
# per_operator_headers changes whether ATen/Functions.h or individual operator headers are used
|
| 303 |
+
# it must match how ATen was built
|
| 304 |
+
per_operator_headers: bool = False,
|
| 305 |
+
backend_name: str = default_args.backend_name,
|
| 306 |
+
gen_forced_fallback_code: bool = False,
|
| 307 |
+
use_lazy_shape: bool = True,
|
| 308 |
+
# the following arguments are temporary customization points for xla backend migration.
|
| 309 |
+
# do not rely on them otherwise, they should be removed once migration is complete
|
| 310 |
+
backend_namespace: str = "torch::lazy",
|
| 311 |
+
get_tensorlist: str = "GetTensorList",
|
| 312 |
+
get_tensor_or_wrap_number: str = "GetLtcTensorOrCreateForWrappedNumber",
|
| 313 |
+
try_get_tensor: str = "TryGetLtcTensor",
|
| 314 |
+
metrics_counter: str = 'TORCH_LAZY_FN_COUNTER("lazy::")',
|
| 315 |
+
create_tensor: str = "LazyTensor::Create",
|
| 316 |
+
create_from_first_tensor: bool = False,
|
| 317 |
+
create_aten_from_ltc_tensor: str = "torch::lazy::CreateAtenFromLtcTensor",
|
| 318 |
+
tuple_aten_from_ltc_tensors: str = "torch::lazy::TupleAtenFromLtcTensors",
|
| 319 |
+
lazy_value_class: str = "torch::lazy::Value",
|
| 320 |
+
lazy_tensor_ptr: str = "LazyTensorPtr",
|
| 321 |
+
get_device_fn: str = "torch::lazy::GetBackendDevice",
|
| 322 |
+
) -> None:
|
| 323 |
+
lv_tokens = lazy_value_class.split("::")
|
| 324 |
+
lv_class = lv_tokens[-1]
|
| 325 |
+
lv_ns = "::".join(lv_tokens[:-1])
|
| 326 |
+
setValueT(BaseCppType(lv_ns, lv_class))
|
| 327 |
+
template_dir = os.path.join(aten_path, "templates")
|
| 328 |
+
|
| 329 |
+
def make_file_manager(install_dir: str) -> FileManager:
|
| 330 |
+
return FileManager(
|
| 331 |
+
install_dir=install_dir, template_dir=template_dir, dry_run=dry_run
|
| 332 |
+
)
|
| 333 |
+
|
| 334 |
+
fm = make_file_manager(output_dir)
|
| 335 |
+
|
| 336 |
+
native_yaml_path = os.path.join(aten_path, "native/native_functions.yaml")
|
| 337 |
+
tags_yaml_path = os.path.join(aten_path, "native/tags.yaml")
|
| 338 |
+
parsed_yaml = parse_native_yaml(native_yaml_path, tags_yaml_path)
|
| 339 |
+
native_functions, backend_indices = (
|
| 340 |
+
parsed_yaml.native_functions,
|
| 341 |
+
parsed_yaml.backend_indices,
|
| 342 |
+
)
|
| 343 |
+
grouped_native_functions = get_grouped_native_functions(native_functions)
|
| 344 |
+
|
| 345 |
+
def sort_native_function(f: NativeFunctionsGroup | NativeFunction) -> str:
|
| 346 |
+
"""
|
| 347 |
+
We sort the native function because of the note in concat_map_codegen.
|
| 348 |
+
TODO(alanwaketan): Remove this sorting hack once all ops are grouped properly.
|
| 349 |
+
"""
|
| 350 |
+
func = f.functional.func if isinstance(f, NativeFunctionsGroup) else f.func
|
| 351 |
+
return str(func.name.name)
|
| 352 |
+
|
| 353 |
+
grouped_native_functions = sorted(
|
| 354 |
+
grouped_native_functions, key=sort_native_function
|
| 355 |
+
)
|
| 356 |
+
|
| 357 |
+
parsed_backend_yaml = parse_backend_yaml(
|
| 358 |
+
source_yaml, grouped_native_functions, backend_indices
|
| 359 |
+
)
|
| 360 |
+
backend_key = parsed_backend_yaml.backend_key
|
| 361 |
+
autograd_key = parsed_backend_yaml.autograd_key
|
| 362 |
+
cpp_namespace = parsed_backend_yaml.cpp_namespace
|
| 363 |
+
backend_indices = parsed_backend_yaml.backend_indices
|
| 364 |
+
# the following 3 keys are all processed differently
|
| 365 |
+
# for full_codegen, we generate IR, kernels, etc
|
| 366 |
+
# for ir_gen, we generate only IR
|
| 367 |
+
# non_native is used to register kernels not declared in
|
| 368 |
+
# native_functions.yaml
|
| 369 |
+
full_codegen, non_native, ir_gen = parse_native_functions_keys(
|
| 370 |
+
source_yaml, grouped_native_functions
|
| 371 |
+
)
|
| 372 |
+
|
| 373 |
+
def concat_map_codegen(
|
| 374 |
+
func: Callable[[NativeFunction], Sequence[str]],
|
| 375 |
+
xs: Iterable[NativeFunctionsGroup | NativeFunction],
|
| 376 |
+
ops_list: list[OperatorName] = full_codegen,
|
| 377 |
+
) -> Iterator[str]:
|
| 378 |
+
"""
|
| 379 |
+
We code-gen for the functional variant, which is all we need for IR classes/lowerings/shape inferences, but we
|
| 380 |
+
only code-gen additional entries for the inplace variant for the native functions.
|
| 381 |
+
"""
|
| 382 |
+
|
| 383 |
+
for x in xs:
|
| 384 |
+
fs = list(x.functions()) if isinstance(x, NativeFunctionsGroup) else [x]
|
| 385 |
+
for f in fs:
|
| 386 |
+
if f.func.name in ops_list:
|
| 387 |
+
yield from func(f)
|
| 388 |
+
|
| 389 |
+
selector = SelectiveBuilder.get_nop_selector()
|
| 390 |
+
|
| 391 |
+
assert backend_key is not None
|
| 392 |
+
class_name = backend_indices[backend_key].native_function_class_name()
|
| 393 |
+
|
| 394 |
+
if impl_path is not None:
|
| 395 |
+
error_on_missing_kernels(
|
| 396 |
+
native_functions,
|
| 397 |
+
backend_indices,
|
| 398 |
+
backend_key,
|
| 399 |
+
autograd_key,
|
| 400 |
+
class_name,
|
| 401 |
+
impl_path,
|
| 402 |
+
full_codegen,
|
| 403 |
+
)
|
| 404 |
+
|
| 405 |
+
""" Validate Shape Inference Definitions
|
| 406 |
+
|
| 407 |
+
Generated lazy native functions all perform shape inference, by first using a meta:: kernel
|
| 408 |
+
if available for that op, and otherwise using a 'compute_shape_{op}' function instead. The generator
|
| 409 |
+
knows the call signature for compute_shape_{op} because it matches the nativefunction (and meta::) signature,
|
| 410 |
+
so it just has to check whether the op is structured and generate a call for one or the other. It's up to the dev
|
| 411 |
+
to supply the missing compute_shape_{op} function, but the codegen at least warns you about this and provides
|
| 412 |
+
the expected signature which can be copy-pasted into shape_inference.h.
|
| 413 |
+
|
| 414 |
+
compute_shape_{op} functions are handwritten and should be replaced over time as ops get ported
|
| 415 |
+
to structured kernels.
|
| 416 |
+
|
| 417 |
+
See torch/csrc/lazy/core/shape_inference.cpp #READ THIS! for more information.
|
| 418 |
+
"""
|
| 419 |
+
if shape_inference_hdr is not None:
|
| 420 |
+
expected_shape_infr_decls = list(
|
| 421 |
+
concat_map_codegen(
|
| 422 |
+
dest.GenLazyShapeInferenceDefinition(
|
| 423 |
+
backend_indices[backend_key], tensor_class
|
| 424 |
+
),
|
| 425 |
+
grouped_native_functions,
|
| 426 |
+
)
|
| 427 |
+
)
|
| 428 |
+
|
| 429 |
+
validate_shape_inference_header(shape_inference_hdr, expected_shape_infr_decls)
|
| 430 |
+
assert class_name is not None
|
| 431 |
+
|
| 432 |
+
# Generate nativefunction declarations
|
| 433 |
+
# Note, eager registrations is set to False for the lazy TS backend as another LTC backend
|
| 434 |
+
# may want to register their own lazy kernels instead of registering the TS ones.
|
| 435 |
+
# The registration will lazily happen when init_ts_backend is called.
|
| 436 |
+
gen_dispatchkey_nativefunc_headers(
|
| 437 |
+
fm,
|
| 438 |
+
class_name,
|
| 439 |
+
cpp_namespace,
|
| 440 |
+
backend_indices,
|
| 441 |
+
grouped_native_functions,
|
| 442 |
+
backend_key,
|
| 443 |
+
autograd_key,
|
| 444 |
+
backend_name,
|
| 445 |
+
)
|
| 446 |
+
|
| 447 |
+
# Generate Dispatcher registrations which hook up the nativefunctions
|
| 448 |
+
for dispatch_key in (
|
| 449 |
+
[backend_key] if autograd_key is None else [backend_key, autograd_key]
|
| 450 |
+
):
|
| 451 |
+
gen_dispatcher_registrations(
|
| 452 |
+
fm,
|
| 453 |
+
output_dir,
|
| 454 |
+
class_name,
|
| 455 |
+
backend_indices,
|
| 456 |
+
grouped_native_functions,
|
| 457 |
+
backend_key,
|
| 458 |
+
dispatch_key,
|
| 459 |
+
selector,
|
| 460 |
+
build_in_tree=build_in_tree,
|
| 461 |
+
per_operator_headers=per_operator_headers,
|
| 462 |
+
backend_name=backend_name,
|
| 463 |
+
eager_registration=False,
|
| 464 |
+
)
|
| 465 |
+
|
| 466 |
+
# Generate native function impls that build IR nodes
|
| 467 |
+
ns_helper = NamespaceHelper(cpp_namespace)
|
| 468 |
+
fm.write_with_template(
|
| 469 |
+
f"{backend_key}NativeFunctions.cpp",
|
| 470 |
+
"DispatchKeyNativeFunctions.cpp",
|
| 471 |
+
lambda: {
|
| 472 |
+
"includes": [
|
| 473 |
+
f"#include <{path}>"
|
| 474 |
+
for path in [
|
| 475 |
+
tensor_class_hdr,
|
| 476 |
+
shape_inference_hdr,
|
| 477 |
+
"ATen/Functions.h",
|
| 478 |
+
"ATen/native/TensorConversions.h",
|
| 479 |
+
"ATen/NativeFunctions.h",
|
| 480 |
+
"ATen/CompositeExplicitAutogradNonFunctionalFunctions.h",
|
| 481 |
+
"ATen/MetaFunctions.h",
|
| 482 |
+
"ATen/Operators.h",
|
| 483 |
+
"ATen/native/CPUFallback.h",
|
| 484 |
+
"torch/csrc/lazy/core/ir_builder.h",
|
| 485 |
+
"torch/csrc/lazy/core/lazy_graph_executor.h",
|
| 486 |
+
"torch/csrc/lazy/core/metrics.h",
|
| 487 |
+
"torch/csrc/lazy/core/shape.h",
|
| 488 |
+
f"{output_dir}/{backend_key}NativeFunctions.h",
|
| 489 |
+
f"{output_dir}/LazyIr.h",
|
| 490 |
+
]
|
| 491 |
+
+ (
|
| 492 |
+
["torch/csrc/lazy/ts_backend/ts_eager_fallback.h"]
|
| 493 |
+
if gen_forced_fallback_code
|
| 494 |
+
else []
|
| 495 |
+
)
|
| 496 |
+
],
|
| 497 |
+
"helper_fns": get_ltc_helper_fns(),
|
| 498 |
+
"native_functions_include": "",
|
| 499 |
+
"namespace_prologue": ns_helper.prologue,
|
| 500 |
+
"namespace_epilogue": ns_helper.epilogue,
|
| 501 |
+
"native_function_definitions": list(
|
| 502 |
+
concat_map_codegen(
|
| 503 |
+
native_func_definition_generator(
|
| 504 |
+
f"{backend_key}NativeFunctions",
|
| 505 |
+
backend_indices[backend_key],
|
| 506 |
+
tensor_class,
|
| 507 |
+
gen_forced_fallback_code,
|
| 508 |
+
backend_namespace,
|
| 509 |
+
get_tensorlist,
|
| 510 |
+
get_tensor_or_wrap_number,
|
| 511 |
+
try_get_tensor,
|
| 512 |
+
metrics_counter,
|
| 513 |
+
create_tensor,
|
| 514 |
+
create_from_first_tensor,
|
| 515 |
+
create_aten_from_ltc_tensor,
|
| 516 |
+
tuple_aten_from_ltc_tensors,
|
| 517 |
+
lazy_tensor_ptr,
|
| 518 |
+
get_device_fn,
|
| 519 |
+
),
|
| 520 |
+
grouped_native_functions,
|
| 521 |
+
)
|
| 522 |
+
),
|
| 523 |
+
},
|
| 524 |
+
)
|
| 525 |
+
# Generate IR node classes
|
| 526 |
+
lazy_ir_obj = lazy_ir_generator(
|
| 527 |
+
backend_indices[backend_key], backend_name, node_base, use_lazy_shape
|
| 528 |
+
)
|
| 529 |
+
|
| 530 |
+
fm.write_with_template(
|
| 531 |
+
"LazyIr.h",
|
| 532 |
+
"LazyIr.h",
|
| 533 |
+
lambda: {
|
| 534 |
+
"lazy_ir_sysinc": [
|
| 535 |
+
f"#include <{path}>"
|
| 536 |
+
for path in [
|
| 537 |
+
"ATen/core/Formatting.h",
|
| 538 |
+
"c10/core/ScalarType.h",
|
| 539 |
+
"torch/csrc/lazy/core/hash.h",
|
| 540 |
+
"torch/csrc/lazy/core/ir.h",
|
| 541 |
+
"torch/csrc/lazy/core/shape.h",
|
| 542 |
+
"optional",
|
| 543 |
+
"vector",
|
| 544 |
+
]
|
| 545 |
+
],
|
| 546 |
+
"lazy_ir_inc": [f'#include "{node_base_hdr}"']
|
| 547 |
+
if node_base_hdr is not None
|
| 548 |
+
else [],
|
| 549 |
+
"ir_declarations": list(
|
| 550 |
+
concat_map_codegen(
|
| 551 |
+
lazy_ir_obj, grouped_native_functions, full_codegen + ir_gen
|
| 552 |
+
)
|
| 553 |
+
),
|
| 554 |
+
"namespace_prologue": ns_helper.prologue,
|
| 555 |
+
"namespace_epilogue": ns_helper.epilogue,
|
| 556 |
+
},
|
| 557 |
+
)
|
| 558 |
+
|
| 559 |
+
# Generate Non Native IR Node classes
|
| 560 |
+
fm.write_with_template(
|
| 561 |
+
"LazyNonNativeIr.h",
|
| 562 |
+
"LazyNonNativeIr.h",
|
| 563 |
+
lambda: {
|
| 564 |
+
"lazy_non_native_ir_inc": [
|
| 565 |
+
f"#include <{path}>"
|
| 566 |
+
for path in [
|
| 567 |
+
"torch/csrc/lazy/core/ir.h",
|
| 568 |
+
"torch/csrc/lazy/core/ir_builder.h",
|
| 569 |
+
"torch/csrc/lazy/core/internal_ops/ltc_ops.h",
|
| 570 |
+
"torch/csrc/lazy/core/shape_inference.h",
|
| 571 |
+
]
|
| 572 |
+
+ ([node_base_hdr] if node_base_hdr else [])
|
| 573 |
+
if path
|
| 574 |
+
],
|
| 575 |
+
"non_native_ir_nodes": dest.generate_non_native_lazy_ir_nodes(
|
| 576 |
+
non_native, lazy_ir_obj
|
| 577 |
+
),
|
| 578 |
+
"namespace_prologue": ns_helper.prologue,
|
| 579 |
+
"namespace_epilogue": ns_helper.epilogue,
|
| 580 |
+
},
|
| 581 |
+
)
|
| 582 |
+
|
| 583 |
+
|
| 584 |
+
if __name__ == "__main__":
|
| 585 |
+
main()
|
gen_schema_utils.py
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from typing import Any, Optional, Union
|
| 2 |
+
|
| 3 |
+
from torchgen.model import (
|
| 4 |
+
Annotation,
|
| 5 |
+
Argument,
|
| 6 |
+
Arguments,
|
| 7 |
+
BaseOperatorName,
|
| 8 |
+
BaseTy,
|
| 9 |
+
BaseType,
|
| 10 |
+
CustomClassType,
|
| 11 |
+
FunctionSchema,
|
| 12 |
+
ListType,
|
| 13 |
+
OperatorName,
|
| 14 |
+
Return,
|
| 15 |
+
)
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
# Note: These aren't actually used in torchgen, they're some utilities for generating a schema
|
| 19 |
+
# from real arguments. For example, this is used to generate HigherOrderOperators' schema since
|
| 20 |
+
# their schemas can vary for different instances of the same HOP.
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
class TypeGen:
|
| 24 |
+
convert_to_base_ty = {
|
| 25 |
+
int: BaseTy.int,
|
| 26 |
+
float: BaseTy.float,
|
| 27 |
+
str: BaseTy.str,
|
| 28 |
+
bool: BaseTy.bool,
|
| 29 |
+
}
|
| 30 |
+
|
| 31 |
+
@staticmethod
|
| 32 |
+
def from_example(obj: Any) -> Union[BaseType, ListType, CustomClassType]:
|
| 33 |
+
import torch
|
| 34 |
+
|
| 35 |
+
if isinstance(obj, torch.fx.GraphModule):
|
| 36 |
+
return BaseType(BaseTy.GraphModule)
|
| 37 |
+
elif isinstance(obj, torch.Tensor):
|
| 38 |
+
return BaseType(BaseTy.Tensor)
|
| 39 |
+
elif isinstance(obj, torch.SymInt):
|
| 40 |
+
return BaseType(BaseTy.SymInt)
|
| 41 |
+
elif isinstance(obj, torch.SymBool):
|
| 42 |
+
return BaseType(BaseTy.SymBool)
|
| 43 |
+
elif isinstance(obj, torch.ScriptObject):
|
| 44 |
+
return CustomClassType(obj._type().name()) # type: ignore[attr-defined]
|
| 45 |
+
elif isinstance(obj, (list, tuple)):
|
| 46 |
+
assert len(obj) > 0
|
| 47 |
+
all_base_tys = [TypeGen.from_example(x) for x in obj]
|
| 48 |
+
if len(set(all_base_tys)) > 1:
|
| 49 |
+
raise RuntimeError(
|
| 50 |
+
f"Cannot generate schema for a seqeunce of args of heterogeneous types: {all_base_tys}. "
|
| 51 |
+
"Consider unpacking the argument and give proper names to them if possible "
|
| 52 |
+
"instead of using *args."
|
| 53 |
+
)
|
| 54 |
+
return ListType(all_base_tys[0], len(obj))
|
| 55 |
+
tp = type(obj)
|
| 56 |
+
if tp not in TypeGen.convert_to_base_ty:
|
| 57 |
+
raise RuntimeError(f"unsupported type {tp}")
|
| 58 |
+
return BaseType(TypeGen.convert_to_base_ty[tp])
|
| 59 |
+
|
| 60 |
+
|
| 61 |
+
class ReturnGen:
|
| 62 |
+
@staticmethod
|
| 63 |
+
def from_example(
|
| 64 |
+
name: Optional[str], obj: Any, annotation: Optional[Annotation]
|
| 65 |
+
) -> Return:
|
| 66 |
+
return Return(name, TypeGen.from_example(obj), annotation)
|
| 67 |
+
|
| 68 |
+
|
| 69 |
+
class ArgumentGen:
|
| 70 |
+
@staticmethod
|
| 71 |
+
def from_example(
|
| 72 |
+
name: str, obj: Any, default: Optional[str], annotation: Optional[Annotation]
|
| 73 |
+
) -> Argument:
|
| 74 |
+
return Argument(
|
| 75 |
+
name, TypeGen.from_example(obj), default=default, annotation=annotation
|
| 76 |
+
)
|
| 77 |
+
|
| 78 |
+
|
| 79 |
+
class FunctionSchemaGen:
|
| 80 |
+
@staticmethod
|
| 81 |
+
def from_example(
|
| 82 |
+
op_name: str,
|
| 83 |
+
example_inputs: tuple[tuple[str, Any], ...],
|
| 84 |
+
example_outputs: tuple[Any, ...],
|
| 85 |
+
) -> FunctionSchema:
|
| 86 |
+
args = []
|
| 87 |
+
for name, inp in example_inputs:
|
| 88 |
+
args.append(ArgumentGen.from_example(name, inp, None, None))
|
| 89 |
+
# ignore the annotations and other attributes for now, we could add more when needed.
|
| 90 |
+
arguments = Arguments(
|
| 91 |
+
tuple(), None, tuple(args), tuple(), None, tuple(), tuple()
|
| 92 |
+
)
|
| 93 |
+
returns = tuple(
|
| 94 |
+
ReturnGen.from_example(None, out, None) for out in example_outputs
|
| 95 |
+
)
|
| 96 |
+
op_name = OperatorName(BaseOperatorName(op_name, False, False, False), "")
|
| 97 |
+
return FunctionSchema(op_name, arguments, returns)
|
gen_vmap_plumbing.py
ADDED
|
@@ -0,0 +1,275 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
import textwrap
|
| 4 |
+
from dataclasses import dataclass
|
| 5 |
+
from typing import TYPE_CHECKING
|
| 6 |
+
|
| 7 |
+
from torchgen.api.translate import translate
|
| 8 |
+
from torchgen.api.types import DispatcherSignature
|
| 9 |
+
from torchgen.context import method_with_native_function
|
| 10 |
+
from torchgen.model import (
|
| 11 |
+
Argument,
|
| 12 |
+
BaseTy,
|
| 13 |
+
BaseType,
|
| 14 |
+
FunctionSchema,
|
| 15 |
+
ListType,
|
| 16 |
+
NativeFunction,
|
| 17 |
+
OptionalType,
|
| 18 |
+
Return,
|
| 19 |
+
SchemaKind,
|
| 20 |
+
Type,
|
| 21 |
+
)
|
| 22 |
+
from torchgen.utils import mapMaybe
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
if TYPE_CHECKING:
|
| 26 |
+
from collections.abc import Sequence
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
def is_tensor(typ: Type) -> bool:
|
| 30 |
+
return isinstance(typ, BaseType) and typ.name == BaseTy.Tensor
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
def is_optional_tensor(typ: Type) -> bool:
|
| 34 |
+
return isinstance(typ, OptionalType) and is_tensor(typ.elem)
|
| 35 |
+
|
| 36 |
+
|
| 37 |
+
def is_tensor_list(typ: Type) -> bool:
|
| 38 |
+
return isinstance(typ, ListType) and is_tensor(typ.elem)
|
| 39 |
+
|
| 40 |
+
|
| 41 |
+
def unwrap_tensor(name: str, cur_level_var: str) -> list[str]:
|
| 42 |
+
result = f"""\
|
| 43 |
+
auto [{name}_value, {name}_bdim] = unwrapTensorAtLevel({name}, {cur_level_var});"""
|
| 44 |
+
return textwrap.dedent(result).split("\n")
|
| 45 |
+
|
| 46 |
+
|
| 47 |
+
def unwrap_optional_tensor(name: str, cur_level_var: str) -> list[str]:
|
| 48 |
+
result = f"""\
|
| 49 |
+
std::optional<Tensor> {name}_value;
|
| 50 |
+
std::optional<int64_t> {name}_bdim;
|
| 51 |
+
if ({name}) {{
|
| 52 |
+
std::tie({name}_value, {name}_bdim) = unwrapTensorAtLevel({name}.value(), {cur_level_var});
|
| 53 |
+
}}"""
|
| 54 |
+
return textwrap.dedent(result).split("\n")
|
| 55 |
+
|
| 56 |
+
|
| 57 |
+
def gen_unwraps(
|
| 58 |
+
flat_arguments: Sequence[Argument], cur_level_var: str
|
| 59 |
+
) -> tuple[str, list[str]]:
|
| 60 |
+
arg_names = [a.name for a in flat_arguments]
|
| 61 |
+
arg_types = [a.type for a in flat_arguments]
|
| 62 |
+
|
| 63 |
+
tensors = [name for typ, name in zip(arg_types, arg_names) if is_tensor(typ)]
|
| 64 |
+
optional_tensors = [
|
| 65 |
+
name for typ, name in zip(arg_types, arg_names) if is_optional_tensor(typ)
|
| 66 |
+
]
|
| 67 |
+
|
| 68 |
+
unwraps = []
|
| 69 |
+
for tensor in tensors:
|
| 70 |
+
unwraps += unwrap_tensor(tensor, cur_level_var)
|
| 71 |
+
|
| 72 |
+
for opt_tensor in optional_tensors:
|
| 73 |
+
unwraps += unwrap_optional_tensor(opt_tensor, cur_level_var)
|
| 74 |
+
unwrap_code = "\n".join(unwraps)
|
| 75 |
+
|
| 76 |
+
unwrapped_arg_list = []
|
| 77 |
+
for arg in arg_names:
|
| 78 |
+
if arg in tensors or arg in optional_tensors:
|
| 79 |
+
unwrapped_arg_list += [f"{arg}_value", f"{arg}_bdim"]
|
| 80 |
+
else:
|
| 81 |
+
unwrapped_arg_list.append(arg)
|
| 82 |
+
return unwrap_code, unwrapped_arg_list
|
| 83 |
+
|
| 84 |
+
|
| 85 |
+
def gen_case_where_all_bdims_are_none(
|
| 86 |
+
outer_sig: DispatcherSignature, schema: FunctionSchema, cur_level_var: str
|
| 87 |
+
) -> str:
|
| 88 |
+
conditions = []
|
| 89 |
+
flat_args = schema.arguments.flat_all
|
| 90 |
+
for arg in flat_args:
|
| 91 |
+
if not arg.type.is_tensor_like():
|
| 92 |
+
continue
|
| 93 |
+
conditions.append(f"!isBatchedAtLevel({arg.name}, {cur_level_var})")
|
| 94 |
+
|
| 95 |
+
sig = DispatcherSignature.from_schema(schema)
|
| 96 |
+
translated_args = ", ".join(
|
| 97 |
+
e.expr for e in translate(outer_sig.arguments(), sig.arguments())
|
| 98 |
+
)
|
| 99 |
+
return f"""\
|
| 100 |
+
if ({' && '.join(conditions)}) {{
|
| 101 |
+
return at::_ops::{sig.func.name.unambiguous_name()}::call({translated_args});
|
| 102 |
+
}}"""
|
| 103 |
+
|
| 104 |
+
|
| 105 |
+
def gen_returns(
|
| 106 |
+
returns: tuple[Return, ...], cur_level_var: str, results_var: str
|
| 107 |
+
) -> str:
|
| 108 |
+
idx = 0
|
| 109 |
+
wrapped_returns = []
|
| 110 |
+
for ret in returns:
|
| 111 |
+
if is_tensor(ret.type):
|
| 112 |
+
wrapped_returns.append(
|
| 113 |
+
f"makeBatched(std::get<{idx}>({results_var}), std::get<{idx + 1}>({results_var}), {cur_level_var})"
|
| 114 |
+
)
|
| 115 |
+
idx += 2
|
| 116 |
+
elif is_tensor_list(ret.type):
|
| 117 |
+
wrapped_returns.append(
|
| 118 |
+
f"makeBatchedVector(std::get<{idx}>({results_var}), std::get<{idx + 1}>({results_var}), {cur_level_var})"
|
| 119 |
+
)
|
| 120 |
+
idx += 2
|
| 121 |
+
else:
|
| 122 |
+
wrapped_returns.append(f"std::get<{idx}>({results_var})")
|
| 123 |
+
idx += 1
|
| 124 |
+
if len(wrapped_returns) == 1:
|
| 125 |
+
result = f"return {wrapped_returns[0]};"
|
| 126 |
+
else:
|
| 127 |
+
result = f'return std::make_tuple({", ".join(wrapped_returns)});'
|
| 128 |
+
return result
|
| 129 |
+
|
| 130 |
+
|
| 131 |
+
def accepts_at_least_one_tensor_input(schema: FunctionSchema) -> bool:
|
| 132 |
+
return any(a.type.is_tensor_like() for a in schema.arguments.flat_all)
|
| 133 |
+
|
| 134 |
+
|
| 135 |
+
def is_mutated_arg(argument: Argument) -> bool:
|
| 136 |
+
return argument.annotation is not None and argument.annotation.is_write
|
| 137 |
+
|
| 138 |
+
|
| 139 |
+
def gen_vmap_inplace_plumbing(native_function: NativeFunction) -> str | None:
|
| 140 |
+
# Assumptions:
|
| 141 |
+
# - only one argument is being modified in-place
|
| 142 |
+
# - the argument that is being modified in-place is the first argument
|
| 143 |
+
# - all returns are either Tensor, tuple of Tensor, or TensorList
|
| 144 |
+
schema = native_function.func
|
| 145 |
+
sig = DispatcherSignature.from_schema(schema)
|
| 146 |
+
returns = schema.returns
|
| 147 |
+
|
| 148 |
+
# Check assumptions. If these are invalid we return None
|
| 149 |
+
# and punt the work to handle them to the future.
|
| 150 |
+
assert schema.kind() == SchemaKind.inplace
|
| 151 |
+
if not is_mutated_arg(schema.arguments.flat_all[0]):
|
| 152 |
+
return None
|
| 153 |
+
if not len([arg for arg in schema.arguments.flat_all if is_mutated_arg(arg)]) == 1:
|
| 154 |
+
return None
|
| 155 |
+
|
| 156 |
+
# Only support cases where all returns are Tensors or vector<Tensor>
|
| 157 |
+
if len(returns) == 0:
|
| 158 |
+
return None
|
| 159 |
+
if not all(is_tensor(ret.type) or is_tensor_list(ret.type) for ret in returns):
|
| 160 |
+
return None
|
| 161 |
+
if not accepts_at_least_one_tensor_input(schema):
|
| 162 |
+
return None
|
| 163 |
+
|
| 164 |
+
cur_level_var = "cur_level"
|
| 165 |
+
|
| 166 |
+
unwraps, unwrapped_arg_list = gen_unwraps(schema.arguments.flat_all, cur_level_var)
|
| 167 |
+
bdims_all_none_case = gen_case_where_all_bdims_are_none(sig, schema, cur_level_var)
|
| 168 |
+
|
| 169 |
+
return f"""\
|
| 170 |
+
template <typename batch_rule_t, batch_rule_t batch_rule>
|
| 171 |
+
{sig.decl(name=schema.name.unambiguous_name() + '_generated_plumbing')} {{
|
| 172 |
+
c10::impl::ExcludeDispatchKeyGuard guard(DispatchKey::FuncTorchBatched);
|
| 173 |
+
auto maybe_layer = maybeCurrentDynamicLayer();
|
| 174 |
+
vmap_check_escaped(maybe_layer, "gen_vmap_inplace_plumbing");
|
| 175 |
+
int64_t {cur_level_var} = maybe_layer->layerId();
|
| 176 |
+
{textwrap.indent(bdims_all_none_case, " ")}
|
| 177 |
+
{textwrap.indent(unwraps, " ")}
|
| 178 |
+
batch_rule({', '.join(unwrapped_arg_list)});
|
| 179 |
+
return {schema.arguments.flat_all[0].name};
|
| 180 |
+
}}"""
|
| 181 |
+
|
| 182 |
+
|
| 183 |
+
def gen_vmap_plumbing_no_returns(native_function: NativeFunction) -> str:
|
| 184 |
+
schema = native_function.func
|
| 185 |
+
sig = DispatcherSignature.from_schema(schema)
|
| 186 |
+
cur_level_var = "cur_level"
|
| 187 |
+
|
| 188 |
+
unwraps, unwrapped_arg_list = gen_unwraps(schema.arguments.flat_all, cur_level_var)
|
| 189 |
+
bdims_all_none_case = gen_case_where_all_bdims_are_none(sig, schema, cur_level_var)
|
| 190 |
+
|
| 191 |
+
return f"""\
|
| 192 |
+
template <typename batch_rule_t, batch_rule_t batch_rule>
|
| 193 |
+
{sig.decl(name=schema.name.unambiguous_name() + '_generated_plumbing')} {{
|
| 194 |
+
c10::impl::ExcludeDispatchKeyGuard guard(DispatchKey::FuncTorchBatched);
|
| 195 |
+
auto maybe_layer = maybeCurrentDynamicLayer();
|
| 196 |
+
vmap_check_escaped(maybe_layer, "gen_vmap_plumbing_no_returns");
|
| 197 |
+
int64_t {cur_level_var} = maybe_layer->layerId();
|
| 198 |
+
{textwrap.indent(bdims_all_none_case, " ")}
|
| 199 |
+
{textwrap.indent(unwraps, " ")}
|
| 200 |
+
batch_rule({', '.join(unwrapped_arg_list)});
|
| 201 |
+
}}"""
|
| 202 |
+
|
| 203 |
+
|
| 204 |
+
def gen_vmap_plumbing(native_function: NativeFunction) -> str | None:
|
| 205 |
+
schema = native_function.func
|
| 206 |
+
sig = DispatcherSignature.from_schema(schema)
|
| 207 |
+
returns = schema.returns
|
| 208 |
+
|
| 209 |
+
# Only support cases where all returns are Tensors or vector<Tensor>
|
| 210 |
+
if not accepts_at_least_one_tensor_input(schema):
|
| 211 |
+
return None
|
| 212 |
+
if len(returns) == 0:
|
| 213 |
+
return gen_vmap_plumbing_no_returns(native_function)
|
| 214 |
+
return_symint_overrides = [
|
| 215 |
+
"_scaled_dot_product_flash_attention",
|
| 216 |
+
"_scaled_dot_product_cudnn_attention",
|
| 217 |
+
]
|
| 218 |
+
if (
|
| 219 |
+
not all(ret.type.is_tensor_like() for ret in returns)
|
| 220 |
+
and schema.name.unambiguous_name() not in return_symint_overrides
|
| 221 |
+
):
|
| 222 |
+
return None
|
| 223 |
+
# in-place views need special handling
|
| 224 |
+
if "inplace_view" in native_function.tags:
|
| 225 |
+
return None
|
| 226 |
+
|
| 227 |
+
if schema.kind() == SchemaKind.inplace:
|
| 228 |
+
return gen_vmap_inplace_plumbing(native_function)
|
| 229 |
+
|
| 230 |
+
# Don't support these (mutable, out, scratch)
|
| 231 |
+
if schema.kind() != SchemaKind.functional:
|
| 232 |
+
return None
|
| 233 |
+
|
| 234 |
+
results_var = "results"
|
| 235 |
+
cur_level_var = "cur_level"
|
| 236 |
+
|
| 237 |
+
unwraps, unwrapped_arg_list = gen_unwraps(schema.arguments.flat_all, cur_level_var)
|
| 238 |
+
bdims_all_none_case = gen_case_where_all_bdims_are_none(sig, schema, cur_level_var)
|
| 239 |
+
|
| 240 |
+
wrapped_returns = gen_returns(returns, cur_level_var, results_var)
|
| 241 |
+
return f"""\
|
| 242 |
+
template <typename batch_rule_t, batch_rule_t batch_rule>
|
| 243 |
+
{sig.decl(name=schema.name.unambiguous_name() + '_generated_plumbing')} {{
|
| 244 |
+
c10::impl::ExcludeDispatchKeyGuard guard(DispatchKey::FuncTorchBatched);
|
| 245 |
+
auto maybe_layer = maybeCurrentDynamicLayer();
|
| 246 |
+
vmap_check_escaped(maybe_layer, "gen_vmap_plumbing");
|
| 247 |
+
int64_t {cur_level_var} = maybe_layer->layerId();
|
| 248 |
+
{textwrap.indent(bdims_all_none_case, " ")}
|
| 249 |
+
{textwrap.indent(unwraps, " ")}
|
| 250 |
+
auto {results_var} = batch_rule({', '.join(unwrapped_arg_list)});
|
| 251 |
+
{wrapped_returns}
|
| 252 |
+
}}"""
|
| 253 |
+
|
| 254 |
+
|
| 255 |
+
@dataclass(frozen=True)
|
| 256 |
+
class ComputeBatchRulePlumbing:
|
| 257 |
+
@method_with_native_function
|
| 258 |
+
def __call__(self, f: NativeFunction) -> str | None:
|
| 259 |
+
result = gen_vmap_plumbing(f)
|
| 260 |
+
return result
|
| 261 |
+
|
| 262 |
+
|
| 263 |
+
def gen_all_vmap_plumbing(native_functions: Sequence[NativeFunction]) -> str:
|
| 264 |
+
body = "\n".join(list(mapMaybe(ComputeBatchRulePlumbing(), native_functions)))
|
| 265 |
+
return f"""
|
| 266 |
+
#pragma once
|
| 267 |
+
#include <ATen/Operators.h>
|
| 268 |
+
#include <ATen/functorch/PlumbingHelper.h>
|
| 269 |
+
|
| 270 |
+
namespace at {{ namespace functorch {{
|
| 271 |
+
|
| 272 |
+
{body}
|
| 273 |
+
|
| 274 |
+
}}}} // namespace at::functorch
|
| 275 |
+
"""
|
get_gprof
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!C:\Users\migue\Downloads\Codigo\AI\HuggingModelAI\IAservicies\myenv\Scripts\python.exe
|
| 2 |
+
#
|
| 3 |
+
# Author: Mike McKerns (mmckerns @caltech and @uqfoundation)
|
| 4 |
+
# Copyright (c) 2008-2016 California Institute of Technology.
|
| 5 |
+
# Copyright (c) 2016-2024 The Uncertainty Quantification Foundation.
|
| 6 |
+
# License: 3-clause BSD. The full license text is available at:
|
| 7 |
+
# - https://github.com/uqfoundation/dill/blob/master/LICENSE
|
| 8 |
+
'''
|
| 9 |
+
build profile graph for the given instance
|
| 10 |
+
|
| 11 |
+
running:
|
| 12 |
+
$ get_gprof <args> <instance>
|
| 13 |
+
|
| 14 |
+
executes:
|
| 15 |
+
gprof2dot -f pstats <args> <type>.prof | dot -Tpng -o <type>.call.png
|
| 16 |
+
|
| 17 |
+
where:
|
| 18 |
+
<args> are arguments for gprof2dot, such as "-n 5 -e 5"
|
| 19 |
+
<instance> is code to create the instance to profile
|
| 20 |
+
<type> is the class of the instance (i.e. type(instance))
|
| 21 |
+
|
| 22 |
+
For example:
|
| 23 |
+
$ get_gprof -n 5 -e 1 "import numpy; numpy.array([1,2])"
|
| 24 |
+
|
| 25 |
+
will create 'ndarray.call.png' with the profile graph for numpy.array([1,2]),
|
| 26 |
+
where '-n 5' eliminates nodes below 5% threshold, similarly '-e 1' eliminates
|
| 27 |
+
edges below 1% threshold
|
| 28 |
+
'''
|
| 29 |
+
|
| 30 |
+
if __name__ == "__main__":
|
| 31 |
+
import sys
|
| 32 |
+
if len(sys.argv) < 2:
|
| 33 |
+
print ("Please provide an object instance (e.g. 'import math; math.pi')")
|
| 34 |
+
sys.exit()
|
| 35 |
+
# grab args for gprof2dot
|
| 36 |
+
args = sys.argv[1:-1]
|
| 37 |
+
args = ' '.join(args)
|
| 38 |
+
# last arg builds the object
|
| 39 |
+
obj = sys.argv[-1]
|
| 40 |
+
obj = obj.split(';')
|
| 41 |
+
# multi-line prep for generating an instance
|
| 42 |
+
for line in obj[:-1]:
|
| 43 |
+
exec(line)
|
| 44 |
+
# one-line generation of an instance
|
| 45 |
+
try:
|
| 46 |
+
obj = eval(obj[-1])
|
| 47 |
+
except Exception:
|
| 48 |
+
print ("Error processing object instance")
|
| 49 |
+
sys.exit()
|
| 50 |
+
|
| 51 |
+
# get object 'name'
|
| 52 |
+
objtype = type(obj)
|
| 53 |
+
name = getattr(objtype, '__name__', getattr(objtype, '__class__', objtype))
|
| 54 |
+
|
| 55 |
+
# profile dumping an object
|
| 56 |
+
import dill
|
| 57 |
+
import os
|
| 58 |
+
import cProfile
|
| 59 |
+
#name = os.path.splitext(os.path.basename(__file__))[0]
|
| 60 |
+
cProfile.run("dill.dumps(obj)", filename="%s.prof" % name)
|
| 61 |
+
msg = "gprof2dot -f pstats %s %s.prof | dot -Tpng -o %s.call.png" % (args, name, name)
|
| 62 |
+
try:
|
| 63 |
+
res = os.system(msg)
|
| 64 |
+
except Exception:
|
| 65 |
+
print ("Please verify install of 'gprof2dot' to view profile graphs")
|
| 66 |
+
if res:
|
| 67 |
+
print ("Please verify install of 'gprof2dot' to view profile graphs")
|
| 68 |
+
|
| 69 |
+
# get stats
|
| 70 |
+
f_prof = "%s.prof" % name
|
| 71 |
+
import pstats
|
| 72 |
+
stats = pstats.Stats(f_prof, stream=sys.stdout)
|
| 73 |
+
stats.strip_dirs().sort_stats('cumtime')
|
| 74 |
+
stats.print_stats(20) #XXX: save to file instead of print top 20?
|
| 75 |
+
os.remove(f_prof)
|
get_objgraph
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!C:\Users\migue\Downloads\Codigo\AI\HuggingModelAI\IAservicies\myenv\Scripts\python.exe
|
| 2 |
+
#
|
| 3 |
+
# Author: Mike McKerns (mmckerns @caltech and @uqfoundation)
|
| 4 |
+
# Copyright (c) 2008-2016 California Institute of Technology.
|
| 5 |
+
# Copyright (c) 2016-2024 The Uncertainty Quantification Foundation.
|
| 6 |
+
# License: 3-clause BSD. The full license text is available at:
|
| 7 |
+
# - https://github.com/uqfoundation/dill/blob/master/LICENSE
|
| 8 |
+
"""
|
| 9 |
+
display the reference paths for objects in ``dill.types`` or a .pkl file
|
| 10 |
+
|
| 11 |
+
Notes:
|
| 12 |
+
the generated image is useful in showing the pointer references in
|
| 13 |
+
objects that are or can be pickled. Any object in ``dill.objects``
|
| 14 |
+
listed in ``dill.load_types(picklable=True, unpicklable=True)`` works.
|
| 15 |
+
|
| 16 |
+
Examples::
|
| 17 |
+
|
| 18 |
+
$ get_objgraph ArrayType
|
| 19 |
+
Image generated as ArrayType.png
|
| 20 |
+
"""
|
| 21 |
+
|
| 22 |
+
import dill as pickle
|
| 23 |
+
#pickle.debug.trace(True)
|
| 24 |
+
#import pickle
|
| 25 |
+
|
| 26 |
+
# get all objects for testing
|
| 27 |
+
from dill import load_types
|
| 28 |
+
load_types(pickleable=True,unpickleable=True)
|
| 29 |
+
from dill import objects
|
| 30 |
+
|
| 31 |
+
if __name__ == "__main__":
|
| 32 |
+
import sys
|
| 33 |
+
if len(sys.argv) != 2:
|
| 34 |
+
print ("Please provide exactly one file or type name (e.g. 'IntType')")
|
| 35 |
+
msg = "\n"
|
| 36 |
+
for objtype in list(objects.keys())[:40]:
|
| 37 |
+
msg += objtype + ', '
|
| 38 |
+
print (msg + "...")
|
| 39 |
+
else:
|
| 40 |
+
objtype = str(sys.argv[-1])
|
| 41 |
+
try:
|
| 42 |
+
obj = objects[objtype]
|
| 43 |
+
except KeyError:
|
| 44 |
+
obj = pickle.load(open(objtype,'rb'))
|
| 45 |
+
import os
|
| 46 |
+
objtype = os.path.splitext(objtype)[0]
|
| 47 |
+
try:
|
| 48 |
+
import objgraph
|
| 49 |
+
objgraph.show_refs(obj, filename=objtype+'.png')
|
| 50 |
+
except ImportError:
|
| 51 |
+
print ("Please install 'objgraph' to view object graphs")
|
| 52 |
+
|
| 53 |
+
|
| 54 |
+
# EOF
|
huggingface-cli.exe
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:c574193300d5f23639772059c556e84d222138dd251cf9ddee95eb5a4663f37c
|
| 3 |
+
size 108461
|
import_pb_to_tensorboard.exe
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:5c8bab1d9edcde78e2e8158df29baa25eff6ed268a881514bb24f6b6a52849fd
|
| 3 |
+
size 108471
|
isympy.1
ADDED
|
@@ -0,0 +1,188 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
'\" -*- coding: us-ascii -*-
|
| 2 |
+
.if \n(.g .ds T< \\FC
|
| 3 |
+
.if \n(.g .ds T> \\F[\n[.fam]]
|
| 4 |
+
.de URL
|
| 5 |
+
\\$2 \(la\\$1\(ra\\$3
|
| 6 |
+
..
|
| 7 |
+
.if \n(.g .mso www.tmac
|
| 8 |
+
.TH isympy 1 2007-10-8 "" ""
|
| 9 |
+
.SH NAME
|
| 10 |
+
isympy \- interactive shell for SymPy
|
| 11 |
+
.SH SYNOPSIS
|
| 12 |
+
'nh
|
| 13 |
+
.fi
|
| 14 |
+
.ad l
|
| 15 |
+
\fBisympy\fR \kx
|
| 16 |
+
.if (\nx>(\n(.l/2)) .nr x (\n(.l/5)
|
| 17 |
+
'in \n(.iu+\nxu
|
| 18 |
+
[\fB-c\fR | \fB--console\fR] [\fB-p\fR ENCODING | \fB--pretty\fR ENCODING] [\fB-t\fR TYPE | \fB--types\fR TYPE] [\fB-o\fR ORDER | \fB--order\fR ORDER] [\fB-q\fR | \fB--quiet\fR] [\fB-d\fR | \fB--doctest\fR] [\fB-C\fR | \fB--no-cache\fR] [\fB-a\fR | \fB--auto\fR] [\fB-D\fR | \fB--debug\fR] [
|
| 19 |
+
-- | PYTHONOPTIONS]
|
| 20 |
+
'in \n(.iu-\nxu
|
| 21 |
+
.ad b
|
| 22 |
+
'hy
|
| 23 |
+
'nh
|
| 24 |
+
.fi
|
| 25 |
+
.ad l
|
| 26 |
+
\fBisympy\fR \kx
|
| 27 |
+
.if (\nx>(\n(.l/2)) .nr x (\n(.l/5)
|
| 28 |
+
'in \n(.iu+\nxu
|
| 29 |
+
[
|
| 30 |
+
{\fB-h\fR | \fB--help\fR}
|
| 31 |
+
|
|
| 32 |
+
{\fB-v\fR | \fB--version\fR}
|
| 33 |
+
]
|
| 34 |
+
'in \n(.iu-\nxu
|
| 35 |
+
.ad b
|
| 36 |
+
'hy
|
| 37 |
+
.SH DESCRIPTION
|
| 38 |
+
isympy is a Python shell for SymPy. It is just a normal python shell
|
| 39 |
+
(ipython shell if you have the ipython package installed) that executes
|
| 40 |
+
the following commands so that you don't have to:
|
| 41 |
+
.PP
|
| 42 |
+
.nf
|
| 43 |
+
\*(T<
|
| 44 |
+
>>> from __future__ import division
|
| 45 |
+
>>> from sympy import *
|
| 46 |
+
>>> x, y, z = symbols("x,y,z")
|
| 47 |
+
>>> k, m, n = symbols("k,m,n", integer=True)
|
| 48 |
+
\*(T>
|
| 49 |
+
.fi
|
| 50 |
+
.PP
|
| 51 |
+
So starting isympy is equivalent to starting python (or ipython) and
|
| 52 |
+
executing the above commands by hand. It is intended for easy and quick
|
| 53 |
+
experimentation with SymPy. For more complicated programs, it is recommended
|
| 54 |
+
to write a script and import things explicitly (using the "from sympy
|
| 55 |
+
import sin, log, Symbol, ..." idiom).
|
| 56 |
+
.SH OPTIONS
|
| 57 |
+
.TP
|
| 58 |
+
\*(T<\fB\-c \fR\*(T>\fISHELL\fR, \*(T<\fB\-\-console=\fR\*(T>\fISHELL\fR
|
| 59 |
+
Use the specified shell (python or ipython) as
|
| 60 |
+
console backend instead of the default one (ipython
|
| 61 |
+
if present or python otherwise).
|
| 62 |
+
|
| 63 |
+
Example: isympy -c python
|
| 64 |
+
|
| 65 |
+
\fISHELL\fR could be either
|
| 66 |
+
\&'ipython' or 'python'
|
| 67 |
+
.TP
|
| 68 |
+
\*(T<\fB\-p \fR\*(T>\fIENCODING\fR, \*(T<\fB\-\-pretty=\fR\*(T>\fIENCODING\fR
|
| 69 |
+
Setup pretty printing in SymPy. By default, the most pretty, unicode
|
| 70 |
+
printing is enabled (if the terminal supports it). You can use less
|
| 71 |
+
pretty ASCII printing instead or no pretty printing at all.
|
| 72 |
+
|
| 73 |
+
Example: isympy -p no
|
| 74 |
+
|
| 75 |
+
\fIENCODING\fR must be one of 'unicode',
|
| 76 |
+
\&'ascii' or 'no'.
|
| 77 |
+
.TP
|
| 78 |
+
\*(T<\fB\-t \fR\*(T>\fITYPE\fR, \*(T<\fB\-\-types=\fR\*(T>\fITYPE\fR
|
| 79 |
+
Setup the ground types for the polys. By default, gmpy ground types
|
| 80 |
+
are used if gmpy2 or gmpy is installed, otherwise it falls back to python
|
| 81 |
+
ground types, which are a little bit slower. You can manually
|
| 82 |
+
choose python ground types even if gmpy is installed (e.g., for testing purposes).
|
| 83 |
+
|
| 84 |
+
Note that sympy ground types are not supported, and should be used
|
| 85 |
+
only for experimental purposes.
|
| 86 |
+
|
| 87 |
+
Note that the gmpy1 ground type is primarily intended for testing; it the
|
| 88 |
+
use of gmpy even if gmpy2 is available.
|
| 89 |
+
|
| 90 |
+
This is the same as setting the environment variable
|
| 91 |
+
SYMPY_GROUND_TYPES to the given ground type (e.g.,
|
| 92 |
+
SYMPY_GROUND_TYPES='gmpy')
|
| 93 |
+
|
| 94 |
+
The ground types can be determined interactively from the variable
|
| 95 |
+
sympy.polys.domains.GROUND_TYPES inside the isympy shell itself.
|
| 96 |
+
|
| 97 |
+
Example: isympy -t python
|
| 98 |
+
|
| 99 |
+
\fITYPE\fR must be one of 'gmpy',
|
| 100 |
+
\&'gmpy1' or 'python'.
|
| 101 |
+
.TP
|
| 102 |
+
\*(T<\fB\-o \fR\*(T>\fIORDER\fR, \*(T<\fB\-\-order=\fR\*(T>\fIORDER\fR
|
| 103 |
+
Setup the ordering of terms for printing. The default is lex, which
|
| 104 |
+
orders terms lexicographically (e.g., x**2 + x + 1). You can choose
|
| 105 |
+
other orderings, such as rev-lex, which will use reverse
|
| 106 |
+
lexicographic ordering (e.g., 1 + x + x**2).
|
| 107 |
+
|
| 108 |
+
Note that for very large expressions, ORDER='none' may speed up
|
| 109 |
+
printing considerably, with the tradeoff that the order of the terms
|
| 110 |
+
in the printed expression will have no canonical order
|
| 111 |
+
|
| 112 |
+
Example: isympy -o rev-lax
|
| 113 |
+
|
| 114 |
+
\fIORDER\fR must be one of 'lex', 'rev-lex', 'grlex',
|
| 115 |
+
\&'rev-grlex', 'grevlex', 'rev-grevlex', 'old', or 'none'.
|
| 116 |
+
.TP
|
| 117 |
+
\*(T<\fB\-q\fR\*(T>, \*(T<\fB\-\-quiet\fR\*(T>
|
| 118 |
+
Print only Python's and SymPy's versions to stdout at startup, and nothing else.
|
| 119 |
+
.TP
|
| 120 |
+
\*(T<\fB\-d\fR\*(T>, \*(T<\fB\-\-doctest\fR\*(T>
|
| 121 |
+
Use the same format that should be used for doctests. This is
|
| 122 |
+
equivalent to '\fIisympy -c python -p no\fR'.
|
| 123 |
+
.TP
|
| 124 |
+
\*(T<\fB\-C\fR\*(T>, \*(T<\fB\-\-no\-cache\fR\*(T>
|
| 125 |
+
Disable the caching mechanism. Disabling the cache may slow certain
|
| 126 |
+
operations down considerably. This is useful for testing the cache,
|
| 127 |
+
or for benchmarking, as the cache can result in deceptive benchmark timings.
|
| 128 |
+
|
| 129 |
+
This is the same as setting the environment variable SYMPY_USE_CACHE
|
| 130 |
+
to 'no'.
|
| 131 |
+
.TP
|
| 132 |
+
\*(T<\fB\-a\fR\*(T>, \*(T<\fB\-\-auto\fR\*(T>
|
| 133 |
+
Automatically create missing symbols. Normally, typing a name of a
|
| 134 |
+
Symbol that has not been instantiated first would raise NameError,
|
| 135 |
+
but with this option enabled, any undefined name will be
|
| 136 |
+
automatically created as a Symbol. This only works in IPython 0.11.
|
| 137 |
+
|
| 138 |
+
Note that this is intended only for interactive, calculator style
|
| 139 |
+
usage. In a script that uses SymPy, Symbols should be instantiated
|
| 140 |
+
at the top, so that it's clear what they are.
|
| 141 |
+
|
| 142 |
+
This will not override any names that are already defined, which
|
| 143 |
+
includes the single character letters represented by the mnemonic
|
| 144 |
+
QCOSINE (see the "Gotchas and Pitfalls" document in the
|
| 145 |
+
documentation). You can delete existing names by executing "del
|
| 146 |
+
name" in the shell itself. You can see if a name is defined by typing
|
| 147 |
+
"'name' in globals()".
|
| 148 |
+
|
| 149 |
+
The Symbols that are created using this have default assumptions.
|
| 150 |
+
If you want to place assumptions on symbols, you should create them
|
| 151 |
+
using symbols() or var().
|
| 152 |
+
|
| 153 |
+
Finally, this only works in the top level namespace. So, for
|
| 154 |
+
example, if you define a function in isympy with an undefined
|
| 155 |
+
Symbol, it will not work.
|
| 156 |
+
.TP
|
| 157 |
+
\*(T<\fB\-D\fR\*(T>, \*(T<\fB\-\-debug\fR\*(T>
|
| 158 |
+
Enable debugging output. This is the same as setting the
|
| 159 |
+
environment variable SYMPY_DEBUG to 'True'. The debug status is set
|
| 160 |
+
in the variable SYMPY_DEBUG within isympy.
|
| 161 |
+
.TP
|
| 162 |
+
-- \fIPYTHONOPTIONS\fR
|
| 163 |
+
These options will be passed on to \fIipython (1)\fR shell.
|
| 164 |
+
Only supported when ipython is being used (standard python shell not supported).
|
| 165 |
+
|
| 166 |
+
Two dashes (--) are required to separate \fIPYTHONOPTIONS\fR
|
| 167 |
+
from the other isympy options.
|
| 168 |
+
|
| 169 |
+
For example, to run iSymPy without startup banner and colors:
|
| 170 |
+
|
| 171 |
+
isympy -q -c ipython -- --colors=NoColor
|
| 172 |
+
.TP
|
| 173 |
+
\*(T<\fB\-h\fR\*(T>, \*(T<\fB\-\-help\fR\*(T>
|
| 174 |
+
Print help output and exit.
|
| 175 |
+
.TP
|
| 176 |
+
\*(T<\fB\-v\fR\*(T>, \*(T<\fB\-\-version\fR\*(T>
|
| 177 |
+
Print isympy version information and exit.
|
| 178 |
+
.SH FILES
|
| 179 |
+
.TP
|
| 180 |
+
\*(T<\fI${HOME}/.sympy\-history\fR\*(T>
|
| 181 |
+
Saves the history of commands when using the python
|
| 182 |
+
shell as backend.
|
| 183 |
+
.SH BUGS
|
| 184 |
+
The upstreams BTS can be found at \(lahttps://github.com/sympy/sympy/issues\(ra
|
| 185 |
+
Please report all bugs that you find in there, this will help improve
|
| 186 |
+
the overall quality of SymPy.
|
| 187 |
+
.SH "SEE ALSO"
|
| 188 |
+
\fBipython\fR(1), \fBpython\fR(1)
|
isympy.exe
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:c5b00e29646484644b74c45d8d9469b294cbe12de1ec5a9f23483ad98cf8ee2b
|
| 3 |
+
size 108427
|
isympy.py
ADDED
|
@@ -0,0 +1,342 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Python shell for SymPy.
|
| 3 |
+
|
| 4 |
+
This is just a normal Python shell (IPython shell if you have the
|
| 5 |
+
IPython package installed), that executes the following commands for
|
| 6 |
+
the user:
|
| 7 |
+
|
| 8 |
+
>>> from __future__ import division
|
| 9 |
+
>>> from sympy import *
|
| 10 |
+
>>> x, y, z, t = symbols('x y z t')
|
| 11 |
+
>>> k, m, n = symbols('k m n', integer=True)
|
| 12 |
+
>>> f, g, h = symbols('f g h', cls=Function)
|
| 13 |
+
>>> init_printing()
|
| 14 |
+
|
| 15 |
+
So starting 'isympy' is equivalent to starting Python (or IPython) and
|
| 16 |
+
executing the above commands by hand. It is intended for easy and quick
|
| 17 |
+
experimentation with SymPy. isympy is a good way to use SymPy as an
|
| 18 |
+
interactive calculator. If you have IPython and Matplotlib installed, then
|
| 19 |
+
interactive plotting is enabled by default.
|
| 20 |
+
|
| 21 |
+
COMMAND LINE OPTIONS
|
| 22 |
+
--------------------
|
| 23 |
+
|
| 24 |
+
-c CONSOLE, --console=CONSOLE
|
| 25 |
+
|
| 26 |
+
Use the specified shell (Python or IPython) shell as the console
|
| 27 |
+
backend instead of the default one (IPython if present, Python
|
| 28 |
+
otherwise), e.g.:
|
| 29 |
+
|
| 30 |
+
$isympy -c python
|
| 31 |
+
|
| 32 |
+
CONSOLE must be one of 'ipython' or 'python'
|
| 33 |
+
|
| 34 |
+
-p PRETTY, --pretty PRETTY
|
| 35 |
+
|
| 36 |
+
Setup pretty-printing in SymPy. When pretty-printing is enabled,
|
| 37 |
+
expressions can be printed with Unicode or ASCII. The default is
|
| 38 |
+
to use pretty-printing (with Unicode if the terminal supports it).
|
| 39 |
+
When this option is 'no', expressions will not be pretty-printed
|
| 40 |
+
and ASCII will be used:
|
| 41 |
+
|
| 42 |
+
$isympy -p no
|
| 43 |
+
|
| 44 |
+
PRETTY must be one of 'unicode', 'ascii', or 'no'
|
| 45 |
+
|
| 46 |
+
-t TYPES, --types=TYPES
|
| 47 |
+
|
| 48 |
+
Setup the ground types for the polys. By default, gmpy ground types
|
| 49 |
+
are used if gmpy2 or gmpy is installed, otherwise it falls back to python
|
| 50 |
+
ground types, which are a little bit slower. You can manually
|
| 51 |
+
choose python ground types even if gmpy is installed (e.g., for
|
| 52 |
+
testing purposes):
|
| 53 |
+
|
| 54 |
+
$isympy -t python
|
| 55 |
+
|
| 56 |
+
TYPES must be one of 'gmpy', 'gmpy1' or 'python'
|
| 57 |
+
|
| 58 |
+
Note that the ground type gmpy1 is primarily intended for testing; it
|
| 59 |
+
forces the use of gmpy version 1 even if gmpy2 is available.
|
| 60 |
+
|
| 61 |
+
This is the same as setting the environment variable
|
| 62 |
+
SYMPY_GROUND_TYPES to the given ground type (e.g.,
|
| 63 |
+
SYMPY_GROUND_TYPES='gmpy')
|
| 64 |
+
|
| 65 |
+
The ground types can be determined interactively from the variable
|
| 66 |
+
sympy.polys.domains.GROUND_TYPES.
|
| 67 |
+
|
| 68 |
+
-o ORDER, --order ORDER
|
| 69 |
+
|
| 70 |
+
Setup the ordering of terms for printing. The default is lex, which
|
| 71 |
+
orders terms lexicographically (e.g., x**2 + x + 1). You can choose
|
| 72 |
+
other orderings, such as rev-lex, which will use reverse
|
| 73 |
+
lexicographic ordering (e.g., 1 + x + x**2):
|
| 74 |
+
|
| 75 |
+
$isympy -o rev-lex
|
| 76 |
+
|
| 77 |
+
ORDER must be one of 'lex', 'rev-lex', 'grlex', 'rev-grlex',
|
| 78 |
+
'grevlex', 'rev-grevlex', 'old', or 'none'.
|
| 79 |
+
|
| 80 |
+
Note that for very large expressions, ORDER='none' may speed up
|
| 81 |
+
printing considerably but the terms will have no canonical order.
|
| 82 |
+
|
| 83 |
+
-q, --quiet
|
| 84 |
+
|
| 85 |
+
Print only Python's and SymPy's versions to stdout at startup.
|
| 86 |
+
|
| 87 |
+
-d, --doctest
|
| 88 |
+
|
| 89 |
+
Use the same format that should be used for doctests. This is
|
| 90 |
+
equivalent to -c python -p no.
|
| 91 |
+
|
| 92 |
+
-C, --no-cache
|
| 93 |
+
|
| 94 |
+
Disable the caching mechanism. Disabling the cache may slow certain
|
| 95 |
+
operations down considerably. This is useful for testing the cache,
|
| 96 |
+
or for benchmarking, as the cache can result in deceptive timings.
|
| 97 |
+
|
| 98 |
+
This is equivalent to setting the environment variable
|
| 99 |
+
SYMPY_USE_CACHE to 'no'.
|
| 100 |
+
|
| 101 |
+
-a, --auto-symbols (requires at least IPython 0.11)
|
| 102 |
+
|
| 103 |
+
Automatically create missing symbols. Normally, typing a name of a
|
| 104 |
+
Symbol that has not been instantiated first would raise NameError,
|
| 105 |
+
but with this option enabled, any undefined name will be
|
| 106 |
+
automatically created as a Symbol.
|
| 107 |
+
|
| 108 |
+
Note that this is intended only for interactive, calculator style
|
| 109 |
+
usage. In a script that uses SymPy, Symbols should be instantiated
|
| 110 |
+
at the top, so that it's clear what they are.
|
| 111 |
+
|
| 112 |
+
This will not override any names that are already defined, which
|
| 113 |
+
includes the single character letters represented by the mnemonic
|
| 114 |
+
QCOSINE (see the "Gotchas and Pitfalls" document in the
|
| 115 |
+
documentation). You can delete existing names by executing "del
|
| 116 |
+
name". If a name is defined, typing "'name' in dir()" will return True.
|
| 117 |
+
|
| 118 |
+
The Symbols that are created using this have default assumptions.
|
| 119 |
+
If you want to place assumptions on symbols, you should create them
|
| 120 |
+
using symbols() or var().
|
| 121 |
+
|
| 122 |
+
Finally, this only works in the top level namespace. So, for
|
| 123 |
+
example, if you define a function in isympy with an undefined
|
| 124 |
+
Symbol, it will not work.
|
| 125 |
+
|
| 126 |
+
See also the -i and -I options.
|
| 127 |
+
|
| 128 |
+
-i, --int-to-Integer (requires at least IPython 0.11)
|
| 129 |
+
|
| 130 |
+
Automatically wrap int literals with Integer. This makes it so that
|
| 131 |
+
things like 1/2 will come out as Rational(1, 2), rather than 0.5. This
|
| 132 |
+
works by preprocessing the source and wrapping all int literals with
|
| 133 |
+
Integer. Note that this will not change the behavior of int literals
|
| 134 |
+
assigned to variables, and it also won't change the behavior of functions
|
| 135 |
+
that return int literals.
|
| 136 |
+
|
| 137 |
+
If you want an int, you can wrap the literal in int(), e.g. int(3)/int(2)
|
| 138 |
+
gives 1.5 (with division imported from __future__).
|
| 139 |
+
|
| 140 |
+
-I, --interactive (requires at least IPython 0.11)
|
| 141 |
+
|
| 142 |
+
This is equivalent to --auto-symbols --int-to-Integer. Future options
|
| 143 |
+
designed for ease of interactive use may be added to this.
|
| 144 |
+
|
| 145 |
+
-D, --debug
|
| 146 |
+
|
| 147 |
+
Enable debugging output. This is the same as setting the
|
| 148 |
+
environment variable SYMPY_DEBUG to 'True'. The debug status is set
|
| 149 |
+
in the variable SYMPY_DEBUG within isympy.
|
| 150 |
+
|
| 151 |
+
-- IPython options
|
| 152 |
+
|
| 153 |
+
Additionally you can pass command line options directly to the IPython
|
| 154 |
+
interpreter (the standard Python shell is not supported). However you
|
| 155 |
+
need to add the '--' separator between two types of options, e.g the
|
| 156 |
+
startup banner option and the colors option. You need to enter the
|
| 157 |
+
options as required by the version of IPython that you are using, too:
|
| 158 |
+
|
| 159 |
+
in IPython 0.11,
|
| 160 |
+
|
| 161 |
+
$isympy -q -- --colors=NoColor
|
| 162 |
+
|
| 163 |
+
or older versions of IPython,
|
| 164 |
+
|
| 165 |
+
$isympy -q -- -colors NoColor
|
| 166 |
+
|
| 167 |
+
See also isympy --help.
|
| 168 |
+
"""
|
| 169 |
+
|
| 170 |
+
import os
|
| 171 |
+
import sys
|
| 172 |
+
|
| 173 |
+
# DO NOT IMPORT SYMPY HERE! Or the setting of the sympy environment variables
|
| 174 |
+
# by the command line will break.
|
| 175 |
+
|
| 176 |
+
def main() -> None:
|
| 177 |
+
from argparse import ArgumentParser, RawDescriptionHelpFormatter
|
| 178 |
+
|
| 179 |
+
VERSION = None
|
| 180 |
+
if '--version' in sys.argv:
|
| 181 |
+
# We cannot import sympy before this is run, because flags like -C and
|
| 182 |
+
# -t set environment variables that must be set before SymPy is
|
| 183 |
+
# imported. The only thing we need to import it for is to get the
|
| 184 |
+
# version, which only matters with the --version flag.
|
| 185 |
+
import sympy
|
| 186 |
+
VERSION = sympy.__version__
|
| 187 |
+
|
| 188 |
+
usage = 'isympy [options] -- [ipython options]'
|
| 189 |
+
parser = ArgumentParser(
|
| 190 |
+
usage=usage,
|
| 191 |
+
description=__doc__,
|
| 192 |
+
formatter_class=RawDescriptionHelpFormatter,
|
| 193 |
+
)
|
| 194 |
+
|
| 195 |
+
parser.add_argument('--version', action='version', version=VERSION)
|
| 196 |
+
|
| 197 |
+
parser.add_argument(
|
| 198 |
+
'-c', '--console',
|
| 199 |
+
dest='console',
|
| 200 |
+
action='store',
|
| 201 |
+
default=None,
|
| 202 |
+
choices=['ipython', 'python'],
|
| 203 |
+
metavar='CONSOLE',
|
| 204 |
+
help='select type of interactive session: ipython | python; defaults '
|
| 205 |
+
'to ipython if IPython is installed, otherwise python')
|
| 206 |
+
|
| 207 |
+
parser.add_argument(
|
| 208 |
+
'-p', '--pretty',
|
| 209 |
+
dest='pretty',
|
| 210 |
+
action='store',
|
| 211 |
+
default=None,
|
| 212 |
+
metavar='PRETTY',
|
| 213 |
+
choices=['unicode', 'ascii', 'no'],
|
| 214 |
+
help='setup pretty printing: unicode | ascii | no; defaults to '
|
| 215 |
+
'unicode printing if the terminal supports it, otherwise ascii')
|
| 216 |
+
|
| 217 |
+
parser.add_argument(
|
| 218 |
+
'-t', '--types',
|
| 219 |
+
dest='types',
|
| 220 |
+
action='store',
|
| 221 |
+
default=None,
|
| 222 |
+
metavar='TYPES',
|
| 223 |
+
choices=['gmpy', 'gmpy1', 'python'],
|
| 224 |
+
help='setup ground types: gmpy | gmpy1 | python; defaults to gmpy if gmpy2 '
|
| 225 |
+
'or gmpy is installed, otherwise python')
|
| 226 |
+
|
| 227 |
+
parser.add_argument(
|
| 228 |
+
'-o', '--order',
|
| 229 |
+
dest='order',
|
| 230 |
+
action='store',
|
| 231 |
+
default=None,
|
| 232 |
+
metavar='ORDER',
|
| 233 |
+
choices=['lex', 'grlex', 'grevlex', 'rev-lex', 'rev-grlex', 'rev-grevlex', 'old', 'none'],
|
| 234 |
+
help='setup ordering of terms: [rev-]lex | [rev-]grlex | [rev-]grevlex | old | none; defaults to lex')
|
| 235 |
+
|
| 236 |
+
parser.add_argument(
|
| 237 |
+
'-q', '--quiet',
|
| 238 |
+
dest='quiet',
|
| 239 |
+
action='store_true',
|
| 240 |
+
default=False,
|
| 241 |
+
help='print only version information at startup')
|
| 242 |
+
|
| 243 |
+
parser.add_argument(
|
| 244 |
+
'-d', '--doctest',
|
| 245 |
+
dest='doctest',
|
| 246 |
+
action='store_true',
|
| 247 |
+
default=False,
|
| 248 |
+
help='use the doctest format for output (you can just copy and paste it)')
|
| 249 |
+
|
| 250 |
+
parser.add_argument(
|
| 251 |
+
'-C', '--no-cache',
|
| 252 |
+
dest='cache',
|
| 253 |
+
action='store_false',
|
| 254 |
+
default=True,
|
| 255 |
+
help='disable caching mechanism')
|
| 256 |
+
|
| 257 |
+
parser.add_argument(
|
| 258 |
+
'-a', '--auto-symbols',
|
| 259 |
+
dest='auto_symbols',
|
| 260 |
+
action='store_true',
|
| 261 |
+
default=False,
|
| 262 |
+
help='automatically construct missing symbols')
|
| 263 |
+
|
| 264 |
+
parser.add_argument(
|
| 265 |
+
'-i', '--int-to-Integer',
|
| 266 |
+
dest='auto_int_to_Integer',
|
| 267 |
+
action='store_true',
|
| 268 |
+
default=False,
|
| 269 |
+
help="automatically wrap int literals with Integer")
|
| 270 |
+
|
| 271 |
+
parser.add_argument(
|
| 272 |
+
'-I', '--interactive',
|
| 273 |
+
dest='interactive',
|
| 274 |
+
action='store_true',
|
| 275 |
+
default=False,
|
| 276 |
+
help="equivalent to -a -i")
|
| 277 |
+
|
| 278 |
+
parser.add_argument(
|
| 279 |
+
'-D', '--debug',
|
| 280 |
+
dest='debug',
|
| 281 |
+
action='store_true',
|
| 282 |
+
default=False,
|
| 283 |
+
help='enable debugging output')
|
| 284 |
+
|
| 285 |
+
(options, ipy_args) = parser.parse_known_args()
|
| 286 |
+
if '--' in ipy_args:
|
| 287 |
+
ipy_args.remove('--')
|
| 288 |
+
|
| 289 |
+
if not options.cache:
|
| 290 |
+
os.environ['SYMPY_USE_CACHE'] = 'no'
|
| 291 |
+
|
| 292 |
+
if options.types:
|
| 293 |
+
os.environ['SYMPY_GROUND_TYPES'] = options.types
|
| 294 |
+
|
| 295 |
+
if options.debug:
|
| 296 |
+
os.environ['SYMPY_DEBUG'] = str(options.debug)
|
| 297 |
+
|
| 298 |
+
if options.doctest:
|
| 299 |
+
options.pretty = 'no'
|
| 300 |
+
options.console = 'python'
|
| 301 |
+
|
| 302 |
+
session = options.console
|
| 303 |
+
|
| 304 |
+
if session is not None:
|
| 305 |
+
ipython = session == 'ipython'
|
| 306 |
+
else:
|
| 307 |
+
try:
|
| 308 |
+
import IPython
|
| 309 |
+
ipython = True
|
| 310 |
+
except ImportError:
|
| 311 |
+
if not options.quiet:
|
| 312 |
+
from sympy.interactive.session import no_ipython
|
| 313 |
+
print(no_ipython)
|
| 314 |
+
ipython = False
|
| 315 |
+
|
| 316 |
+
args = {
|
| 317 |
+
'pretty_print': True,
|
| 318 |
+
'use_unicode': None,
|
| 319 |
+
'use_latex': None,
|
| 320 |
+
'order': None,
|
| 321 |
+
'argv': ipy_args,
|
| 322 |
+
}
|
| 323 |
+
|
| 324 |
+
if options.pretty == 'unicode':
|
| 325 |
+
args['use_unicode'] = True
|
| 326 |
+
elif options.pretty == 'ascii':
|
| 327 |
+
args['use_unicode'] = False
|
| 328 |
+
elif options.pretty == 'no':
|
| 329 |
+
args['pretty_print'] = False
|
| 330 |
+
|
| 331 |
+
if options.order is not None:
|
| 332 |
+
args['order'] = options.order
|
| 333 |
+
|
| 334 |
+
args['quiet'] = options.quiet
|
| 335 |
+
args['auto_symbols'] = options.auto_symbols or options.interactive
|
| 336 |
+
args['auto_int_to_Integer'] = options.auto_int_to_Integer or options.interactive
|
| 337 |
+
|
| 338 |
+
from sympy.interactive import init_session
|
| 339 |
+
init_session(ipython, **args)
|
| 340 |
+
|
| 341 |
+
if __name__ == "__main__":
|
| 342 |
+
main()
|
local.py
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
import threading
|
| 4 |
+
from contextlib import contextmanager
|
| 5 |
+
from typing import TYPE_CHECKING
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
if TYPE_CHECKING:
|
| 9 |
+
from collections.abc import Iterator
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
# Simple dynamic scoping implementation. The name "parametrize" comes
|
| 13 |
+
# from Racket.
|
| 14 |
+
#
|
| 15 |
+
# WARNING WARNING: LOOKING TO EDIT THIS FILE? Think carefully about
|
| 16 |
+
# why you need to add a toggle to the global behavior of code
|
| 17 |
+
# generation. The parameters here should really only be used
|
| 18 |
+
# for "temporary" situations, where we need to temporarily change
|
| 19 |
+
# the codegen in some cases because we cannot conveniently update
|
| 20 |
+
# all call sites, and are slated to be eliminated once all call
|
| 21 |
+
# sites are eliminated. If you don't have a plan for how to get there,
|
| 22 |
+
# DON'T add a new entry here.
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
class Locals(threading.local):
|
| 26 |
+
use_const_ref_for_mutable_tensors: bool | None = None
|
| 27 |
+
use_ilistref_for_tensor_lists: bool | None = None
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
_locals = Locals()
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
def use_const_ref_for_mutable_tensors() -> bool:
|
| 34 |
+
assert _locals.use_const_ref_for_mutable_tensors is not None, (
|
| 35 |
+
"need to initialize local.use_const_ref_for_mutable_tensors with "
|
| 36 |
+
"local.parametrize"
|
| 37 |
+
)
|
| 38 |
+
return _locals.use_const_ref_for_mutable_tensors
|
| 39 |
+
|
| 40 |
+
|
| 41 |
+
def use_ilistref_for_tensor_lists() -> bool:
|
| 42 |
+
assert _locals.use_ilistref_for_tensor_lists is not None, (
|
| 43 |
+
"need to initialize local.use_ilistref_for_tensor_lists with "
|
| 44 |
+
"local.parametrize"
|
| 45 |
+
)
|
| 46 |
+
return _locals.use_ilistref_for_tensor_lists
|
| 47 |
+
|
| 48 |
+
|
| 49 |
+
@contextmanager
|
| 50 |
+
def parametrize(
|
| 51 |
+
*, use_const_ref_for_mutable_tensors: bool, use_ilistref_for_tensor_lists: bool
|
| 52 |
+
) -> Iterator[None]:
|
| 53 |
+
old_use_const_ref_for_mutable_tensors = _locals.use_const_ref_for_mutable_tensors
|
| 54 |
+
old_use_ilistref_for_tensor_lists = _locals.use_ilistref_for_tensor_lists
|
| 55 |
+
try:
|
| 56 |
+
_locals.use_const_ref_for_mutable_tensors = use_const_ref_for_mutable_tensors
|
| 57 |
+
_locals.use_ilistref_for_tensor_lists = use_ilistref_for_tensor_lists
|
| 58 |
+
yield
|
| 59 |
+
finally:
|
| 60 |
+
_locals.use_const_ref_for_mutable_tensors = (
|
| 61 |
+
old_use_const_ref_for_mutable_tensors
|
| 62 |
+
)
|
| 63 |
+
_locals.use_ilistref_for_tensor_lists = old_use_ilistref_for_tensor_lists
|
markdown-it.exe
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:11d8735f26e48e2a2f578c86866dd60b5c4e99c0afa7e7904bbc1c3a213003f9
|
| 3 |
+
size 108444
|
markdown_py.exe
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:eb545c7705c5f8f2fc008a81c5848028645bf057f596c6340d1dfa6f115d2eb8
|
| 3 |
+
size 108438
|
model.py
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
native_function_generation.py
ADDED
|
@@ -0,0 +1,651 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
import string
|
| 4 |
+
from collections import defaultdict
|
| 5 |
+
from typing import TYPE_CHECKING
|
| 6 |
+
|
| 7 |
+
import torchgen.api.dispatcher as dispatcher
|
| 8 |
+
from torchgen.api.translate import translate
|
| 9 |
+
from torchgen.api.types import Binding, DispatcherSignature, Expr
|
| 10 |
+
from torchgen.context import with_native_function
|
| 11 |
+
from torchgen.model import (
|
| 12 |
+
Annotation,
|
| 13 |
+
Argument,
|
| 14 |
+
BackendIndex,
|
| 15 |
+
BackendMetadata,
|
| 16 |
+
BaseOperatorName,
|
| 17 |
+
BaseTy,
|
| 18 |
+
BaseType,
|
| 19 |
+
DEFAULT_KERNEL_NAMESPACE,
|
| 20 |
+
DeviceCheckType,
|
| 21 |
+
DispatchKey,
|
| 22 |
+
FunctionSchema,
|
| 23 |
+
NativeFunction,
|
| 24 |
+
NativeFunctionsGroup,
|
| 25 |
+
OperatorName,
|
| 26 |
+
Return,
|
| 27 |
+
SchemaKind,
|
| 28 |
+
Variant,
|
| 29 |
+
)
|
| 30 |
+
from torchgen.utils import concatMap
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
if TYPE_CHECKING:
|
| 34 |
+
from collections.abc import Sequence
|
| 35 |
+
|
| 36 |
+
|
| 37 |
+
# See Note: [Out ops with functional variants that don't get grouped properly]
|
| 38 |
+
OUT_OPS_THAT_DONT_GET_GROUPED_PROPERLY = [
|
| 39 |
+
# This has a functional variant, but it's currently marked private.
|
| 40 |
+
# This function should be marked private as well (*_backward ops aren't exposed to python anyway).
|
| 41 |
+
"adaptive_avg_pool3d_backward.grad_input",
|
| 42 |
+
# There's a functional variant, _slow_conv2d_backward.output_mask, that isn't grouped properly.
|
| 43 |
+
# Maybe we can kill this operator in favor of convolution_backward?
|
| 44 |
+
"_slow_conv2d_backward.grad_input",
|
| 45 |
+
]
|
| 46 |
+
|
| 47 |
+
|
| 48 |
+
# See Note: [Mutable ops that cannot get an out variant]
|
| 49 |
+
MUTABLE_OPS_THAT_CANNOT_GET_AN_OUT_VARIANT = [
|
| 50 |
+
# should be out=?
|
| 51 |
+
"_cummax_helper",
|
| 52 |
+
# should be out=?
|
| 53 |
+
"_cummin_helper",
|
| 54 |
+
]
|
| 55 |
+
|
| 56 |
+
# All of these operators don't have any tensor like returns
|
| 57 |
+
FUNCTIONAL_OPS_THAT_CANNOT_GET_AN_OUT_VARIANT = [
|
| 58 |
+
"_assert_async", # no return
|
| 59 |
+
"_assert_async.msg", # no return
|
| 60 |
+
"_assert_tensor_metadata", # no return
|
| 61 |
+
"_cslt_sparse_mm_search", # returns an int
|
| 62 |
+
"_assert_scalar", # no return
|
| 63 |
+
"_dimI", # returns an int
|
| 64 |
+
"_dimV", # returns an int
|
| 65 |
+
"_has_same_storage_numel", # returns a boolean
|
| 66 |
+
"_linalg_check_errors", # no return
|
| 67 |
+
"_local_scalar_dense", # returns a Scalar
|
| 68 |
+
"_nested_tensor_from_mask_left_aligned", # returns a boolean
|
| 69 |
+
"_nnz", # returns an int
|
| 70 |
+
"_use_cudnn_ctc_loss", # returns a boolean
|
| 71 |
+
"_use_cudnn_ctc_loss.Tensor", # returns a boolean
|
| 72 |
+
"_validate_compressed_sparse_indices", # no return
|
| 73 |
+
"allclose", # returns a boolean
|
| 74 |
+
"dense_dim", # returns an int
|
| 75 |
+
"equal", # returns a boolean
|
| 76 |
+
"is_coalesced", # returns an boolean
|
| 77 |
+
"is_pinned", # returns a boolean
|
| 78 |
+
"is_same_size", # returns a boolean
|
| 79 |
+
"is_set_to", # returns a boolean
|
| 80 |
+
"q_per_channel_axis", # returns an int
|
| 81 |
+
"q_scale", # returns a float
|
| 82 |
+
"q_zero_point", # returns an int
|
| 83 |
+
"qscheme", # returns a QScheme
|
| 84 |
+
"record_stream", # no return
|
| 85 |
+
"sparse_dim", # returns an int
|
| 86 |
+
"sym_constrain_range", # no return
|
| 87 |
+
"sym_constrain_range_for_size", # no return
|
| 88 |
+
"_nested_tensor_storage_offsets", # returns a vector of ints
|
| 89 |
+
"_chunk_grad_outputs_efficient_attention", # returns a bool
|
| 90 |
+
"_fused_sdp_choice", # returns an int
|
| 91 |
+
"_print", # no return
|
| 92 |
+
"_sink_tokens", # no return
|
| 93 |
+
"_nested_get_ragged_idx", # returns an int
|
| 94 |
+
]
|
| 95 |
+
|
| 96 |
+
INPLACE_OPS_THAT_DONT_GET_GROUPED_PROPERLY = [
|
| 97 |
+
# polygamma and polygamma.out both exist, but have a
|
| 98 |
+
# pre-self arg (while polygamma_ does not)
|
| 99 |
+
# We should either fix this schema so it can be grouped properly,
|
| 100 |
+
# or allow the codegen to generate new functional/out= NativeFunctions for this op
|
| 101 |
+
# (which would require changing its overload name to prevent overload ambiguity).
|
| 102 |
+
"polygamma_"
|
| 103 |
+
]
|
| 104 |
+
|
| 105 |
+
|
| 106 |
+
# Groups "similar" NativeFunctions together
|
| 107 |
+
# example add.Tensor, add_.Tensor, add.out
|
| 108 |
+
# "similar" NativeFunctions are all expected to have an identical `signature()`,
|
| 109 |
+
# But have differing SchemaKinds.
|
| 110 |
+
def pre_group_native_functions(
|
| 111 |
+
native_functions: Sequence[NativeFunction],
|
| 112 |
+
) -> dict[FunctionSchema, dict[SchemaKind, NativeFunction]]:
|
| 113 |
+
pre_grouped_native_functions: dict[
|
| 114 |
+
FunctionSchema, dict[SchemaKind, NativeFunction]
|
| 115 |
+
] = defaultdict(dict)
|
| 116 |
+
for f in native_functions:
|
| 117 |
+
d = pre_grouped_native_functions[f.func.signature()]
|
| 118 |
+
assert f.func.kind() not in d
|
| 119 |
+
d[f.func.kind()] = f
|
| 120 |
+
return pre_grouped_native_functions
|
| 121 |
+
|
| 122 |
+
|
| 123 |
+
# Returns the out variant overload name given a base function overload name
|
| 124 |
+
def get_expected_out_variant_overload_name(overload_name: str | None) -> str:
|
| 125 |
+
return "out" if not overload_name else f"{overload_name}_out"
|
| 126 |
+
|
| 127 |
+
|
| 128 |
+
# Helper function: given an inplace FunctionSchema, generate its corresponding out= variant
|
| 129 |
+
# Example before:
|
| 130 |
+
# _add_relu_.Scalar(Tensor(a!) self, Scalar other, Scalar alpha=1) -> Tensor(a!)
|
| 131 |
+
# Example after:
|
| 132 |
+
# _add_relu.Scalar_out(Tensor self, Scalar other, Scalar alpha=1, *, Tensor(a!) out)
|
| 133 |
+
def self_to_out_signature(func: FunctionSchema) -> FunctionSchema:
|
| 134 |
+
# Generating an out= schema from an inplace schema.
|
| 135 |
+
assert func.kind() == SchemaKind.inplace
|
| 136 |
+
assert func.arguments.self_arg is not None
|
| 137 |
+
# The new out= schema has:
|
| 138 |
+
# - a new out argument with the same type as "func" (but with a mutable annotation)
|
| 139 |
+
# - The returns (if any) now alias the out= argument instead of "func"
|
| 140 |
+
# - an "out" overload name
|
| 141 |
+
return FunctionSchema(
|
| 142 |
+
name=func.name.remove_inplace().with_overload(
|
| 143 |
+
get_expected_out_variant_overload_name(func.name.overload_name)
|
| 144 |
+
),
|
| 145 |
+
arguments=func.arguments.remove_self_annotation().with_out_args(
|
| 146 |
+
[
|
| 147 |
+
Argument(
|
| 148 |
+
name="out",
|
| 149 |
+
type=func.arguments.self_arg.argument.type,
|
| 150 |
+
default=None,
|
| 151 |
+
annotation=func.arguments.self_arg.argument.annotation,
|
| 152 |
+
)
|
| 153 |
+
]
|
| 154 |
+
),
|
| 155 |
+
returns=func.returns,
|
| 156 |
+
)
|
| 157 |
+
|
| 158 |
+
|
| 159 |
+
# Helper function: given a functional FunctionSchema, generate its corresponding out= variant
|
| 160 |
+
# Example before:
|
| 161 |
+
# _to_copy(Tensor self, *, ScalarType? dtype=None, Layout? layout=None, Device? device=None,
|
| 162 |
+
# bool? pin_memory=None, bool non_blocking=False, MemoryFormat? memory_format=None) -> Tensor
|
| 163 |
+
# Example after:
|
| 164 |
+
# _to_copy._out(Tensor self, *, bool non_blocking=False, MemoryFormat? memory_format=None,
|
| 165 |
+
# Tensor(a!) out) -> Tensor(a!)
|
| 166 |
+
def functional_to_out_signature(func: FunctionSchema) -> FunctionSchema:
|
| 167 |
+
# Generating an out= schema from a functional schema.
|
| 168 |
+
assert func.kind() == SchemaKind.functional
|
| 169 |
+
|
| 170 |
+
new_returns, new_out_args = generate_out_args_from_schema(func)
|
| 171 |
+
# The new out= schema has:
|
| 172 |
+
# - one or more new out argument(s) with the same type as returns (but with a mutable annotation)
|
| 173 |
+
# - The returns now alias the out= arguments
|
| 174 |
+
# - an "_out" overload name
|
| 175 |
+
return FunctionSchema(
|
| 176 |
+
name=func.name.with_overload(
|
| 177 |
+
get_expected_out_variant_overload_name(func.name.overload_name)
|
| 178 |
+
),
|
| 179 |
+
arguments=func.arguments.signature().with_out_args(
|
| 180 |
+
new_out_args,
|
| 181 |
+
),
|
| 182 |
+
returns=tuple(new_returns),
|
| 183 |
+
)
|
| 184 |
+
|
| 185 |
+
|
| 186 |
+
# Helper function: given a function schema, generate corresponding out arguments, also the updated return annotations.
|
| 187 |
+
def generate_out_args_from_schema(
|
| 188 |
+
func: FunctionSchema,
|
| 189 |
+
) -> tuple[list[Return], list[Argument]]:
|
| 190 |
+
# More of a sanity check - our existing restrictions on schemas should enforce that
|
| 191 |
+
# mutable schema kinds never return their mutable arguments.
|
| 192 |
+
assert not any(
|
| 193 |
+
r.annotation is not None and r.annotation.is_write for r in func.returns
|
| 194 |
+
)
|
| 195 |
+
|
| 196 |
+
tensorlike_rets = [r for r in func.returns if r.type.is_tensor_like()]
|
| 197 |
+
assert len(tensorlike_rets) > 0
|
| 198 |
+
|
| 199 |
+
used_annotations = concatMap(
|
| 200 |
+
lambda a: [] if a.annotation is None else a.annotation.alias_set,
|
| 201 |
+
func.arguments.flat_all,
|
| 202 |
+
)
|
| 203 |
+
valid_annotations = [x for x in string.ascii_lowercase if x not in used_annotations]
|
| 204 |
+
|
| 205 |
+
all_rets_are_tensors = all(r.type == BaseType(BaseTy.Tensor) for r in func.returns)
|
| 206 |
+
|
| 207 |
+
new_out_args: list[Argument] = []
|
| 208 |
+
# The end result of new_returns is that:
|
| 209 |
+
# - If every return is a plain tensor, then the new returns == the old returns, but with the out= alias annotations added.
|
| 210 |
+
# - Otherwise, none of the out arguments show up in the returns (and we're only left with non-tensor-like returns, if any).
|
| 211 |
+
new_returns: list[Return] = []
|
| 212 |
+
for i, r in enumerate(func.returns):
|
| 213 |
+
if r.type.is_tensor_like():
|
| 214 |
+
new_out = Argument(
|
| 215 |
+
name="out" if len(func.returns) == 1 else f"out{i}",
|
| 216 |
+
type=r.type,
|
| 217 |
+
default=None,
|
| 218 |
+
annotation=Annotation.parse(f"{valid_annotations[i]}!"),
|
| 219 |
+
)
|
| 220 |
+
new_out_args.append(new_out)
|
| 221 |
+
if all_rets_are_tensors:
|
| 222 |
+
# The convention for out= schemas is that they only return their out arguments
|
| 223 |
+
# if the return is a plain Tensor (or if it's a tuple of plain Tensors)
|
| 224 |
+
new_ret = Return(
|
| 225 |
+
name=None, type=new_out.type, annotation=new_out.annotation
|
| 226 |
+
)
|
| 227 |
+
new_returns.append(new_ret)
|
| 228 |
+
else:
|
| 229 |
+
new_returns.append(r)
|
| 230 |
+
return new_returns, new_out_args
|
| 231 |
+
|
| 232 |
+
|
| 233 |
+
# Helper function: given a mutable FunctionSchema, generate its corresponding out= variant
|
| 234 |
+
# Example before:
|
| 235 |
+
# _fused_moving_avg_obs_fq_helper(Tensor self, Tensor observer_on, Tensor fake_quant_on, Tensor(a!) running_min, Tensor(b!) running_max, Tensor(c!) scale, Tensor(d!) zero_point, float averaging_const, int quant_min, int quant_max, int ch_axis, bool per_row_fake_quant=False, bool symmetric_quant=False) -> (Tensor output, Tensor mask) # noqa: B950
|
| 236 |
+
# Example after:
|
| 237 |
+
# _fused_moving_avg_obs_fq_helper._out(Tensor self, Tensor observer_on, Tensor fake_quant_on, Tensor(a!) running_min, Tensor(b!) running_max, Tensor(c!) scale, Tensor(d!) zero_point, float averaging_const, int quant_min, int quant_max, int ch_axis, bool per_row_fake_quant=False, bool symmetric_quant=False, *, Tensor(e!) out0, Tensor(f!) out1) -> (Tensor(e!), Tensor(f!)) # noqa: B950
|
| 238 |
+
def mutable_to_out_signature(func: FunctionSchema) -> FunctionSchema:
|
| 239 |
+
# Generating an out= schema from a mutable schema.
|
| 240 |
+
assert func.kind() == SchemaKind.mutable
|
| 241 |
+
# The new out= schema has:
|
| 242 |
+
# - Any non-aliased tensor-like returns are converted to mutable, aliased out= arguments
|
| 243 |
+
# (if the argument is a tensor then we also return it for method chaining,
|
| 244 |
+
# otherwise we return nothing)
|
| 245 |
+
# - an "out" overload name
|
| 246 |
+
#
|
| 247 |
+
# Note that:
|
| 248 |
+
# (1) This also means that we can *only* generate an out= variant from a mutable schema
|
| 249 |
+
# if the mutable schema has at least one tensor-like non-aliasing return.
|
| 250 |
+
# (2) The generated out= variant still has mutable positional arguments,
|
| 251 |
+
# but if necessary we could probably add another out= variant that also
|
| 252 |
+
# functionalizes the mutable arguments (a functional_out variant)
|
| 253 |
+
|
| 254 |
+
new_returns, new_out_args = generate_out_args_from_schema(func)
|
| 255 |
+
|
| 256 |
+
return FunctionSchema(
|
| 257 |
+
name=func.name.remove_inplace().with_overload(
|
| 258 |
+
get_expected_out_variant_overload_name(func.name.overload_name)
|
| 259 |
+
),
|
| 260 |
+
arguments=func.arguments.with_out_args(new_out_args),
|
| 261 |
+
returns=tuple(new_returns),
|
| 262 |
+
)
|
| 263 |
+
|
| 264 |
+
|
| 265 |
+
# This function, given function of one SchemaKind, as well as a target SchemaKind,
|
| 266 |
+
# generates a new NativeFunction with the same properties, but using the target SchemaKind.
|
| 267 |
+
# We only actually generate functions for either functional or out= SchemaKinds.
|
| 268 |
+
# This function returns a tuple, with:
|
| 269 |
+
# - The generated NativeFunction
|
| 270 |
+
# - a dictionary of `BackendIndex` objects, describing which dispatch keys
|
| 271 |
+
# we will generate kernels for, for the new NativeFunction.
|
| 272 |
+
# Details are in the function, but we only generate composite kernels (in some cases) today.
|
| 273 |
+
def generate_function(
|
| 274 |
+
f: NativeFunction, k: SchemaKind
|
| 275 |
+
) -> tuple[NativeFunction, dict[DispatchKey, dict[OperatorName, BackendMetadata]]]:
|
| 276 |
+
from torchgen.api import cpp
|
| 277 |
+
|
| 278 |
+
if k == SchemaKind.functional:
|
| 279 |
+
assert f.func.kind() != SchemaKind.functional
|
| 280 |
+
# The new "functional" NativeFunction has:
|
| 281 |
+
# - any mutable arguments have been converted into (immutable) returns.
|
| 282 |
+
# (if a mutable argument was not also a return, it gets converted to one)
|
| 283 |
+
# - "_functional" appended to the base name, ONLY IF this op has a mutable variant.
|
| 284 |
+
# See Note [Overload Ambiguity With Functional Variants]
|
| 285 |
+
# The default grouping logic in signature() actually already does this,
|
| 286 |
+
# so we can piggy-back off it (but we still want return names)
|
| 287 |
+
func = f.func.signature(keep_return_names=True).with_name(
|
| 288 |
+
OperatorName(
|
| 289 |
+
name=BaseOperatorName(
|
| 290 |
+
base=f.func.name.name.base,
|
| 291 |
+
inplace=False,
|
| 292 |
+
dunder_method=f.func.name.name.dunder_method,
|
| 293 |
+
# See Note [Overload Ambiguity With Functional Variants]
|
| 294 |
+
functional_overload=f.func.kind() == SchemaKind.mutable,
|
| 295 |
+
),
|
| 296 |
+
overload_name=f.func.name.overload_name,
|
| 297 |
+
)
|
| 298 |
+
)
|
| 299 |
+
elif k == SchemaKind.out:
|
| 300 |
+
# We generate out= ops mostly just so that we can pair up NativeFunctions into groups easily,
|
| 301 |
+
# but at least today, there is no good reason to actually use them.
|
| 302 |
+
# we'll generate a dispatcher entry for them, but won't actually register any kernels for them.
|
| 303 |
+
if f.func.kind() == SchemaKind.inplace:
|
| 304 |
+
func = self_to_out_signature(f.func)
|
| 305 |
+
elif f.func.kind() == SchemaKind.mutable:
|
| 306 |
+
func = mutable_to_out_signature(f.func)
|
| 307 |
+
elif f.func.kind() == SchemaKind.functional:
|
| 308 |
+
func = functional_to_out_signature(f.func)
|
| 309 |
+
else:
|
| 310 |
+
raise AssertionError(
|
| 311 |
+
"We only bother generating out= functions from either inplace or mutable or functional variants"
|
| 312 |
+
)
|
| 313 |
+
else:
|
| 314 |
+
raise AssertionError(
|
| 315 |
+
"We currently only generate either functional or out= NativeFunctions"
|
| 316 |
+
)
|
| 317 |
+
|
| 318 |
+
# Generated kernel naming convention for out: <op_name>_<overload_name>. The reason for this is to
|
| 319 |
+
# disambiguate operator with the same name but different overload name, e.g., `randn.names_out` and
|
| 320 |
+
# `randn.generator_with_names_out`.
|
| 321 |
+
kernel_name = (
|
| 322 |
+
func.name.unambiguous_name()
|
| 323 |
+
if func.kind() == SchemaKind.out
|
| 324 |
+
else cpp.name(func)
|
| 325 |
+
)
|
| 326 |
+
if f.func.has_symint():
|
| 327 |
+
kernel_name += "_symint"
|
| 328 |
+
backend_metadata = {
|
| 329 |
+
DispatchKey.CompositeExplicitAutograd: {
|
| 330 |
+
func.name: BackendMetadata(
|
| 331 |
+
kernel=kernel_name,
|
| 332 |
+
structured=False,
|
| 333 |
+
cpp_namespace=DEFAULT_KERNEL_NAMESPACE,
|
| 334 |
+
)
|
| 335 |
+
}
|
| 336 |
+
}
|
| 337 |
+
tags = {"generated"} | set(
|
| 338 |
+
f.tags & {"nondeterministic_seeded", "view_copy", "pt2_compliant_tag"}
|
| 339 |
+
)
|
| 340 |
+
|
| 341 |
+
return (
|
| 342 |
+
NativeFunction(
|
| 343 |
+
func=func,
|
| 344 |
+
use_const_ref_for_mutable_tensors=f.use_const_ref_for_mutable_tensors,
|
| 345 |
+
# These generated fn's aren't meant to be user friendly- don't generate methods.
|
| 346 |
+
variants={Variant.function},
|
| 347 |
+
structured=False,
|
| 348 |
+
structured_delegate=None,
|
| 349 |
+
structured_inherits=None,
|
| 350 |
+
precomputed=None,
|
| 351 |
+
autogen=[],
|
| 352 |
+
ufunc_inner_loop={},
|
| 353 |
+
manual_kernel_registration=False,
|
| 354 |
+
manual_cpp_binding=False,
|
| 355 |
+
python_module=None,
|
| 356 |
+
category_override=None,
|
| 357 |
+
device_guard=False,
|
| 358 |
+
device_check=DeviceCheckType.NoCheck,
|
| 359 |
+
loc=f.loc,
|
| 360 |
+
cpp_no_default_args=set(),
|
| 361 |
+
is_abstract=f.is_abstract,
|
| 362 |
+
has_composite_implicit_autograd_kernel=False,
|
| 363 |
+
has_composite_implicit_autograd_nested_tensor_kernel=False,
|
| 364 |
+
has_composite_explicit_autograd_kernel=True,
|
| 365 |
+
has_composite_explicit_autograd_non_functional_kernel=False,
|
| 366 |
+
# Every generated NativeFunction gets a "generated" tag, so it's easy to tell
|
| 367 |
+
# which NativeFunction objects did not come directly from native_functions.yaml.
|
| 368 |
+
tags=tags,
|
| 369 |
+
namespace=f.namespace,
|
| 370 |
+
),
|
| 371 |
+
backend_metadata,
|
| 372 |
+
)
|
| 373 |
+
|
| 374 |
+
|
| 375 |
+
# This function is responsible for adding generated NativeFunctions which don't appear
|
| 376 |
+
# explicitly in the codegen.
|
| 377 |
+
# You can inspect the full list of NativeFunctions yourself with the torchgen package, by running
|
| 378 |
+
# torchgen.parse_native_yaml("aten/src/ATen/native/native_functions.yaml", "aten/src/ATen/native/tags.yaml")
|
| 379 |
+
# (Maybe we should make a friendly API for this)
|
| 380 |
+
#
|
| 381 |
+
# Note: this function *mutates* its two inputs,
|
| 382 |
+
# adding the new NativeFunctions / BackendMetadata to them
|
| 383 |
+
def add_generated_native_functions(
|
| 384 |
+
rs: list[NativeFunction],
|
| 385 |
+
indices: dict[DispatchKey, dict[OperatorName, BackendMetadata]],
|
| 386 |
+
) -> None:
|
| 387 |
+
# The main code for generating new NativeFunctions
|
| 388 |
+
# First we group of NativeFunctions by schema kind,
|
| 389 |
+
# then we detect which ones are missing and generate them.
|
| 390 |
+
pre_grouped_native_functions = pre_group_native_functions(rs)
|
| 391 |
+
for d in pre_grouped_native_functions.values():
|
| 392 |
+
has_functional = SchemaKind.functional in d
|
| 393 |
+
has_inplace = SchemaKind.inplace in d
|
| 394 |
+
has_mutable = SchemaKind.mutable in d
|
| 395 |
+
has_out = SchemaKind.out in d
|
| 396 |
+
is_core = any("core" in variant.tags for variant in d.values())
|
| 397 |
+
|
| 398 |
+
# We automatically generate a few native functions that don't exist in the yaml, for a few reasons:
|
| 399 |
+
# (1) If an operator has an inplace/out= variant but no functional variant, we can generate
|
| 400 |
+
# a simple functional variant that the functionalization pass can consume.
|
| 401 |
+
# (2) If an operator has an inplace or functional but no out= variant, we generate an out=
|
| 402 |
+
# variant, mostly so we can easily pair up functions into NativeFunctionsGroup,
|
| 403 |
+
# while maintaining the constraint that the out= variant is "required".
|
| 404 |
+
if has_mutable or has_inplace or has_out or has_functional:
|
| 405 |
+
# Don't bother generating functions trio's for native functions that bypass the dispatcher.
|
| 406 |
+
are_manual = all(f.manual_cpp_binding for f in d.values())
|
| 407 |
+
# Don't bother generating functional + out= variants for view operators
|
| 408 |
+
# set_ is technically an inplace_view, but for now it is treated
|
| 409 |
+
# as a normal inplace op in the codegen
|
| 410 |
+
has_view_ops = any(
|
| 411 |
+
f.is_view_op and str(f.func.name.name) != "set_" for f in d.values()
|
| 412 |
+
)
|
| 413 |
+
# Don't generate the other variants for non-core CompositeImplicitAutograd operators.
|
| 414 |
+
# We could probably do this, but the main benefit of generating the function triplets
|
| 415 |
+
# is for transforms that need them, and transforms don't need to act directly
|
| 416 |
+
# on CompositeImplicitAutograd operators (since we let them decompose).
|
| 417 |
+
are_composite_implicit = all(
|
| 418 |
+
f.has_composite_implicit_autograd_kernel for f in d.values()
|
| 419 |
+
)
|
| 420 |
+
if are_manual or has_view_ops or are_composite_implicit and not is_core:
|
| 421 |
+
continue
|
| 422 |
+
if has_out and len(d.values()) == 1:
|
| 423 |
+
# Note: [Out ops with functional variants that don't get grouped properly]
|
| 424 |
+
# In theory we could validly have an out= operator in native_functions.yaml
|
| 425 |
+
# that has no other variants.
|
| 426 |
+
# But today, all of the operators where that's the case actually do have
|
| 427 |
+
# functional variants, that we are just unable to pair up properly.
|
| 428 |
+
# I think banning this all together is probably safer
|
| 429 |
+
# (you can always add a functional variant yourself if you want to add a new out= operator).
|
| 430 |
+
#
|
| 431 |
+
# We should probably fix the existing cases; this check is to prevent us from adding more over time.
|
| 432 |
+
if (
|
| 433 |
+
str(d[SchemaKind.out].func.name)
|
| 434 |
+
not in OUT_OPS_THAT_DONT_GET_GROUPED_PROPERLY
|
| 435 |
+
):
|
| 436 |
+
raise AssertionError(
|
| 437 |
+
f"Found an out= operator that we could not find any other variants of: {str(d[SchemaKind.out].func)}"
|
| 438 |
+
)
|
| 439 |
+
continue
|
| 440 |
+
|
| 441 |
+
# Some inplace ops that have problematic schemas (that we should fix), which prevent us
|
| 442 |
+
# from generating out= and functional variants
|
| 443 |
+
if (
|
| 444 |
+
has_inplace
|
| 445 |
+
and str(d[SchemaKind.inplace].func.name)
|
| 446 |
+
in INPLACE_OPS_THAT_DONT_GET_GROUPED_PROPERLY
|
| 447 |
+
):
|
| 448 |
+
continue
|
| 449 |
+
|
| 450 |
+
base_fn = (
|
| 451 |
+
d[SchemaKind.mutable]
|
| 452 |
+
if has_mutable
|
| 453 |
+
else d[SchemaKind.inplace]
|
| 454 |
+
if has_inplace
|
| 455 |
+
else d[SchemaKind.out]
|
| 456 |
+
if has_out
|
| 457 |
+
else d[SchemaKind.functional]
|
| 458 |
+
)
|
| 459 |
+
|
| 460 |
+
# Note: [Mutable ops that cannot get an out variant]
|
| 461 |
+
# We can only generate an out= variant if either:
|
| 462 |
+
# - the original function has tensor-like returns (since we can convert them to out kwargs)
|
| 463 |
+
# - or it's inplace (since we can convert `self` to an out kwarg)
|
| 464 |
+
# There are only two functions that don't fit this criteria today though,
|
| 465 |
+
# and they both look like they should be fixed to be out= variants,
|
| 466 |
+
# so if feels safer to ban this schema all-together
|
| 467 |
+
base_fn_valid = base_fn.func.kind() == SchemaKind.inplace or any(
|
| 468 |
+
r.type.is_tensor_like() for r in base_fn.func.returns
|
| 469 |
+
)
|
| 470 |
+
# Note: [Loosen the assertion that all functional should have out variant]
|
| 471 |
+
# By design all functional operators should have our variants. The needs_out check
|
| 472 |
+
# is loosening this requirement, changing it to only generate out variant if there's
|
| 473 |
+
# an `autogen` block in the native function, in the long run it should be removed.
|
| 474 |
+
# FIXME: Remove this after figuring out CI job failures related to min, max, mean
|
| 475 |
+
needs_out = any("out" in str(op_name) for op_name in base_fn.autogen)
|
| 476 |
+
gets_out_variant = not has_out and base_fn_valid and needs_out
|
| 477 |
+
if not has_out and not base_fn_valid:
|
| 478 |
+
if (
|
| 479 |
+
str(base_fn.func.name)
|
| 480 |
+
not in MUTABLE_OPS_THAT_CANNOT_GET_AN_OUT_VARIANT
|
| 481 |
+
and str(base_fn.func.name)
|
| 482 |
+
not in FUNCTIONAL_OPS_THAT_CANNOT_GET_AN_OUT_VARIANT
|
| 483 |
+
):
|
| 484 |
+
raise AssertionError(
|
| 485 |
+
f"""Found an operator that we could not generate an out= variant for: {str(base_fn.func)}.
|
| 486 |
+
This type of operators don't have tensor-like return, making it difficult to generate a proper out= variant. If
|
| 487 |
+
out= variant is not needed, please add the function name into FUNCTIONAL_OPS_THAT_CANNOT_GET_AN_OUT_VARIANT list."""
|
| 488 |
+
)
|
| 489 |
+
|
| 490 |
+
# Generate an out= variant
|
| 491 |
+
if gets_out_variant:
|
| 492 |
+
fn, metadata = generate_function(base_fn, SchemaKind.out)
|
| 493 |
+
d[SchemaKind.out] = fn
|
| 494 |
+
BackendIndex.grow_index(indices, metadata)
|
| 495 |
+
rs.append(fn)
|
| 496 |
+
|
| 497 |
+
# Generate a functional variant, but only do it if the operator got an out= variant
|
| 498 |
+
# (Functional variants are only useful if we can group up the variants,
|
| 499 |
+
# which we can only do if they have an out= variant)
|
| 500 |
+
if not has_functional and (has_out or gets_out_variant):
|
| 501 |
+
fn, metadata = generate_function(base_fn, SchemaKind.functional)
|
| 502 |
+
d[SchemaKind.functional] = fn
|
| 503 |
+
BackendIndex.grow_index(indices, metadata)
|
| 504 |
+
rs.append(fn)
|
| 505 |
+
|
| 506 |
+
|
| 507 |
+
def return_str(rets: tuple[Return, ...], names: list[str]) -> str:
|
| 508 |
+
assert len(rets) == len(names)
|
| 509 |
+
if len(rets) == 0:
|
| 510 |
+
return ""
|
| 511 |
+
elif len(rets) == 1:
|
| 512 |
+
return f"return {names[0]};"
|
| 513 |
+
else:
|
| 514 |
+
return f"return {dispatcher.returns_type(rets).cpp_type()}({', '.join(names)});"
|
| 515 |
+
|
| 516 |
+
|
| 517 |
+
# Given a function, and the name of a variable corresponding to the output of that function,
|
| 518 |
+
# gather up all of the individual returns that are not aliased
|
| 519 |
+
def gather_nonaliased_inner_rets(func: FunctionSchema, out_var: str) -> list[str]:
|
| 520 |
+
aliased_rets = func.aliased_return_names()
|
| 521 |
+
non_aliased_names = []
|
| 522 |
+
is_out_var_a_tuple = len(func.returns) > 1
|
| 523 |
+
for i, r in enumerate(aliased_rets):
|
| 524 |
+
if r is None:
|
| 525 |
+
non_aliased_names.append(
|
| 526 |
+
f"std::get<{i}>({out_var})" if is_out_var_a_tuple else out_var
|
| 527 |
+
)
|
| 528 |
+
return non_aliased_names
|
| 529 |
+
|
| 530 |
+
|
| 531 |
+
# Generates functional kernels in terms of their inplace.mutable counterparts.
|
| 532 |
+
# We only do this for "generated" NativeFunctions
|
| 533 |
+
@with_native_function
|
| 534 |
+
def gen_composite_functional_kernel(g: NativeFunctionsGroup) -> str | None:
|
| 535 |
+
# We should only be generating these for code-generated NativeFunctions
|
| 536 |
+
if "generated" not in g.functional.tags:
|
| 537 |
+
return None
|
| 538 |
+
# And we always write the kernel for a generated op in terms of a non-generated op.
|
| 539 |
+
if g.inplace is not None and "generated" not in g.inplace.tags:
|
| 540 |
+
target_f = g.inplace
|
| 541 |
+
elif g.mutable is not None and "generated" not in g.mutable.tags:
|
| 542 |
+
target_f = g.mutable
|
| 543 |
+
else:
|
| 544 |
+
# We should be guaranteed to have a valid inplace/mutable variant to call into.
|
| 545 |
+
# See Note: [Mutable Ops Not Using Functionalization]
|
| 546 |
+
raise AssertionError(str(g.functional.func))
|
| 547 |
+
|
| 548 |
+
sig = DispatcherSignature(g.functional.func)
|
| 549 |
+
target_sig = DispatcherSignature(target_f.func)
|
| 550 |
+
|
| 551 |
+
context: list[Binding | Expr] = []
|
| 552 |
+
clone_mutable_inputs = []
|
| 553 |
+
cloned_return_names = []
|
| 554 |
+
# We can't just directly pass all of the arguments from the functional op into the mutating op.
|
| 555 |
+
# We need to check for which inputs to the mutating operator are mutable,
|
| 556 |
+
# and clone those inputs first.
|
| 557 |
+
for a_curr, a_tgt in zip(
|
| 558 |
+
dispatcher.jit_arguments(g.functional.func),
|
| 559 |
+
dispatcher.jit_arguments(target_f.func),
|
| 560 |
+
):
|
| 561 |
+
if a_tgt.annotation is not None and a_tgt.annotation.is_write:
|
| 562 |
+
clone_mutable_inputs.append(
|
| 563 |
+
f"auto {a_curr.name}_clone = clone_arg({a_curr.name});"
|
| 564 |
+
)
|
| 565 |
+
context.append(
|
| 566 |
+
Expr(
|
| 567 |
+
expr=f"{a_curr.name}_clone",
|
| 568 |
+
type=dispatcher.argument_type(a_curr, binds=a_curr.name),
|
| 569 |
+
)
|
| 570 |
+
)
|
| 571 |
+
# Invariant: mutable arguments on the inner mutable op are always returns on the functional op.
|
| 572 |
+
cloned_return_names.append(f"{a_curr.name}_clone")
|
| 573 |
+
else:
|
| 574 |
+
context.append(dispatcher.argument(a_curr))
|
| 575 |
+
exprs = ", ".join([e.expr for e in translate(context, target_sig.arguments())])
|
| 576 |
+
|
| 577 |
+
out_name = "output"
|
| 578 |
+
maybe_assign = f"auto {out_name} = " if len(target_f.func.returns) > 0 else ""
|
| 579 |
+
inner_return_names = gather_nonaliased_inner_rets(target_f.func, out_name)
|
| 580 |
+
ret_str = return_str(
|
| 581 |
+
g.functional.func.returns, inner_return_names + cloned_return_names
|
| 582 |
+
)
|
| 583 |
+
|
| 584 |
+
clone_mutable_inputs_str = "\n".join(clone_mutable_inputs)
|
| 585 |
+
return f"""
|
| 586 |
+
{sig.defn(name=sig.name() + ("_symint" if g.out.func.has_symint() else ""))} {{
|
| 587 |
+
{clone_mutable_inputs_str}
|
| 588 |
+
{maybe_assign}at::_ops::{target_f.func.name.unambiguous_name()}::call({exprs});
|
| 589 |
+
{ret_str}
|
| 590 |
+
}}
|
| 591 |
+
"""
|
| 592 |
+
|
| 593 |
+
|
| 594 |
+
# Generates out= kernels in terms of their functional counterparts.
|
| 595 |
+
# We only do this for "generated" NativeFunctions
|
| 596 |
+
@with_native_function
|
| 597 |
+
def gen_composite_out_kernel(g: NativeFunctionsGroup) -> str | None:
|
| 598 |
+
# We should only be generating these for code-generated NativeFunctions
|
| 599 |
+
if "generated" not in g.out.tags:
|
| 600 |
+
return None
|
| 601 |
+
# And we always write the kernel for the out= op in terms of the functional.
|
| 602 |
+
# Note that the functional op might have also been generated, but we don't have to
|
| 603 |
+
# worry about cycles, because the generated functional kernels are always implemented
|
| 604 |
+
# in terms of non-generated kernels (see gen_composite_functional_kernel).
|
| 605 |
+
|
| 606 |
+
sig = DispatcherSignature(g.out.func)
|
| 607 |
+
target_sig = DispatcherSignature(g.functional.func)
|
| 608 |
+
|
| 609 |
+
exprs = ", ".join(
|
| 610 |
+
[e.expr for e in translate(sig.arguments(), target_sig.arguments())]
|
| 611 |
+
)
|
| 612 |
+
|
| 613 |
+
copy_outs = []
|
| 614 |
+
out_name = "tmp_output"
|
| 615 |
+
for i, out_arg in enumerate(g.out.func.arguments.out):
|
| 616 |
+
functional_return_name = (
|
| 617 |
+
out_name
|
| 618 |
+
if len(g.functional.func.returns) == 1
|
| 619 |
+
else f"std::get<{i}>({out_name})"
|
| 620 |
+
)
|
| 621 |
+
copy_outs.append(
|
| 622 |
+
f"""\
|
| 623 |
+
resize_out_helper({out_arg.name}, {functional_return_name});
|
| 624 |
+
copy_arg({out_arg.name}, {functional_return_name});"""
|
| 625 |
+
)
|
| 626 |
+
|
| 627 |
+
rets = []
|
| 628 |
+
# For each return arg in the calling (out=) operator,
|
| 629 |
+
# If it corresponds to an aliased input, return the input.
|
| 630 |
+
# Otherwise, return the corresponding output from calling the functional operator.
|
| 631 |
+
for i, ret_name in enumerate(g.out.func.aliased_return_names()):
|
| 632 |
+
if ret_name is not None:
|
| 633 |
+
rets.append(ret_name)
|
| 634 |
+
else:
|
| 635 |
+
functional_return_name = (
|
| 636 |
+
out_name
|
| 637 |
+
if len(g.functional.func.returns) == 1
|
| 638 |
+
else f"std::get<{i}>({out_name})"
|
| 639 |
+
)
|
| 640 |
+
rets.append(functional_return_name)
|
| 641 |
+
|
| 642 |
+
copy_outs_str = "\n".join(copy_outs)
|
| 643 |
+
|
| 644 |
+
# Kernel name needs to follow the naming convention defined in `generate_function()`
|
| 645 |
+
return f"""
|
| 646 |
+
{sig.defn(name=g.out.func.name.unambiguous_name() + ("_symint" if g.out.func.has_symint() else ""))} {{
|
| 647 |
+
auto {out_name} = at::_ops::{g.functional.func.name.unambiguous_name()}::call({exprs});
|
| 648 |
+
{copy_outs_str}
|
| 649 |
+
{return_str(g.out.func.returns, rets)}
|
| 650 |
+
}}
|
| 651 |
+
"""
|
normalizer.exe
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:655847268f9ff668e36268319e9e202056c33038014bec44346f031db40ac9b5
|
| 3 |
+
size 108450
|
numpy-2.0.2-cp312-cp312-win_amd64.whl
ADDED
|
File without changes
|
numpy-config.exe
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:980d55316535217f8748a295df5784e4eb26100a1110bae9a110492242fe7c34
|
| 3 |
+
size 108440
|
pip.exe
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:6bf6f1c22184613cb885fc8cadd6e9407d20d1ebcafec3282fbd1a52c25210a8
|
| 3 |
+
size 108445
|
pip3.12.exe
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:6bf6f1c22184613cb885fc8cadd6e9407d20d1ebcafec3282fbd1a52c25210a8
|
| 3 |
+
size 108445
|
pip3.exe
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:6bf6f1c22184613cb885fc8cadd6e9407d20d1ebcafec3282fbd1a52c25210a8
|
| 3 |
+
size 108445
|
pygmentize.exe
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:be176558d7b05f8849604d14f1885243e5c96fd78711fdccd01ef12014e41a1b
|
| 3 |
+
size 108439
|
python.exe
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:3470f7919170d235d7e6079691462c4b217745ec67ee612e745730e46d98f238
|
| 3 |
+
size 270104
|
pythonw.exe
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:dbb5bf39746dadaf2d2513da6746ed61a04b493289272dbed72923eabfd552f8
|
| 3 |
+
size 258840
|
saved_model_cli.exe
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:abeeaed0c37d09ad4bb9710ee2af44fd6eb08053cd4b4528e7ca6f5c1a709ee6
|
| 3 |
+
size 108462
|
six.py
ADDED
|
@@ -0,0 +1,1003 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) 2010-2024 Benjamin Peterson
|
| 2 |
+
#
|
| 3 |
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
| 4 |
+
# of this software and associated documentation files (the "Software"), to deal
|
| 5 |
+
# in the Software without restriction, including without limitation the rights
|
| 6 |
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
| 7 |
+
# copies of the Software, and to permit persons to whom the Software is
|
| 8 |
+
# furnished to do so, subject to the following conditions:
|
| 9 |
+
#
|
| 10 |
+
# The above copyright notice and this permission notice shall be included in all
|
| 11 |
+
# copies or substantial portions of the Software.
|
| 12 |
+
#
|
| 13 |
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
| 14 |
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
| 15 |
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
| 16 |
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
| 17 |
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
| 18 |
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
| 19 |
+
# SOFTWARE.
|
| 20 |
+
|
| 21 |
+
"""Utilities for writing code that runs on Python 2 and 3"""
|
| 22 |
+
|
| 23 |
+
from __future__ import absolute_import
|
| 24 |
+
|
| 25 |
+
import functools
|
| 26 |
+
import itertools
|
| 27 |
+
import operator
|
| 28 |
+
import sys
|
| 29 |
+
import types
|
| 30 |
+
|
| 31 |
+
__author__ = "Benjamin Peterson <[email protected]>"
|
| 32 |
+
__version__ = "1.17.0"
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
# Useful for very coarse version differentiation.
|
| 36 |
+
PY2 = sys.version_info[0] == 2
|
| 37 |
+
PY3 = sys.version_info[0] == 3
|
| 38 |
+
PY34 = sys.version_info[0:2] >= (3, 4)
|
| 39 |
+
|
| 40 |
+
if PY3:
|
| 41 |
+
string_types = str,
|
| 42 |
+
integer_types = int,
|
| 43 |
+
class_types = type,
|
| 44 |
+
text_type = str
|
| 45 |
+
binary_type = bytes
|
| 46 |
+
|
| 47 |
+
MAXSIZE = sys.maxsize
|
| 48 |
+
else:
|
| 49 |
+
string_types = basestring,
|
| 50 |
+
integer_types = (int, long)
|
| 51 |
+
class_types = (type, types.ClassType)
|
| 52 |
+
text_type = unicode
|
| 53 |
+
binary_type = str
|
| 54 |
+
|
| 55 |
+
if sys.platform.startswith("java"):
|
| 56 |
+
# Jython always uses 32 bits.
|
| 57 |
+
MAXSIZE = int((1 << 31) - 1)
|
| 58 |
+
else:
|
| 59 |
+
# It's possible to have sizeof(long) != sizeof(Py_ssize_t).
|
| 60 |
+
class X(object):
|
| 61 |
+
|
| 62 |
+
def __len__(self):
|
| 63 |
+
return 1 << 31
|
| 64 |
+
try:
|
| 65 |
+
len(X())
|
| 66 |
+
except OverflowError:
|
| 67 |
+
# 32-bit
|
| 68 |
+
MAXSIZE = int((1 << 31) - 1)
|
| 69 |
+
else:
|
| 70 |
+
# 64-bit
|
| 71 |
+
MAXSIZE = int((1 << 63) - 1)
|
| 72 |
+
del X
|
| 73 |
+
|
| 74 |
+
if PY34:
|
| 75 |
+
from importlib.util import spec_from_loader
|
| 76 |
+
else:
|
| 77 |
+
spec_from_loader = None
|
| 78 |
+
|
| 79 |
+
|
| 80 |
+
def _add_doc(func, doc):
|
| 81 |
+
"""Add documentation to a function."""
|
| 82 |
+
func.__doc__ = doc
|
| 83 |
+
|
| 84 |
+
|
| 85 |
+
def _import_module(name):
|
| 86 |
+
"""Import module, returning the module after the last dot."""
|
| 87 |
+
__import__(name)
|
| 88 |
+
return sys.modules[name]
|
| 89 |
+
|
| 90 |
+
|
| 91 |
+
class _LazyDescr(object):
|
| 92 |
+
|
| 93 |
+
def __init__(self, name):
|
| 94 |
+
self.name = name
|
| 95 |
+
|
| 96 |
+
def __get__(self, obj, tp):
|
| 97 |
+
result = self._resolve()
|
| 98 |
+
setattr(obj, self.name, result) # Invokes __set__.
|
| 99 |
+
try:
|
| 100 |
+
# This is a bit ugly, but it avoids running this again by
|
| 101 |
+
# removing this descriptor.
|
| 102 |
+
delattr(obj.__class__, self.name)
|
| 103 |
+
except AttributeError:
|
| 104 |
+
pass
|
| 105 |
+
return result
|
| 106 |
+
|
| 107 |
+
|
| 108 |
+
class MovedModule(_LazyDescr):
|
| 109 |
+
|
| 110 |
+
def __init__(self, name, old, new=None):
|
| 111 |
+
super(MovedModule, self).__init__(name)
|
| 112 |
+
if PY3:
|
| 113 |
+
if new is None:
|
| 114 |
+
new = name
|
| 115 |
+
self.mod = new
|
| 116 |
+
else:
|
| 117 |
+
self.mod = old
|
| 118 |
+
|
| 119 |
+
def _resolve(self):
|
| 120 |
+
return _import_module(self.mod)
|
| 121 |
+
|
| 122 |
+
def __getattr__(self, attr):
|
| 123 |
+
_module = self._resolve()
|
| 124 |
+
value = getattr(_module, attr)
|
| 125 |
+
setattr(self, attr, value)
|
| 126 |
+
return value
|
| 127 |
+
|
| 128 |
+
|
| 129 |
+
class _LazyModule(types.ModuleType):
|
| 130 |
+
|
| 131 |
+
def __init__(self, name):
|
| 132 |
+
super(_LazyModule, self).__init__(name)
|
| 133 |
+
self.__doc__ = self.__class__.__doc__
|
| 134 |
+
|
| 135 |
+
def __dir__(self):
|
| 136 |
+
attrs = ["__doc__", "__name__"]
|
| 137 |
+
attrs += [attr.name for attr in self._moved_attributes]
|
| 138 |
+
return attrs
|
| 139 |
+
|
| 140 |
+
# Subclasses should override this
|
| 141 |
+
_moved_attributes = []
|
| 142 |
+
|
| 143 |
+
|
| 144 |
+
class MovedAttribute(_LazyDescr):
|
| 145 |
+
|
| 146 |
+
def __init__(self, name, old_mod, new_mod, old_attr=None, new_attr=None):
|
| 147 |
+
super(MovedAttribute, self).__init__(name)
|
| 148 |
+
if PY3:
|
| 149 |
+
if new_mod is None:
|
| 150 |
+
new_mod = name
|
| 151 |
+
self.mod = new_mod
|
| 152 |
+
if new_attr is None:
|
| 153 |
+
if old_attr is None:
|
| 154 |
+
new_attr = name
|
| 155 |
+
else:
|
| 156 |
+
new_attr = old_attr
|
| 157 |
+
self.attr = new_attr
|
| 158 |
+
else:
|
| 159 |
+
self.mod = old_mod
|
| 160 |
+
if old_attr is None:
|
| 161 |
+
old_attr = name
|
| 162 |
+
self.attr = old_attr
|
| 163 |
+
|
| 164 |
+
def _resolve(self):
|
| 165 |
+
module = _import_module(self.mod)
|
| 166 |
+
return getattr(module, self.attr)
|
| 167 |
+
|
| 168 |
+
|
| 169 |
+
class _SixMetaPathImporter(object):
|
| 170 |
+
|
| 171 |
+
"""
|
| 172 |
+
A meta path importer to import six.moves and its submodules.
|
| 173 |
+
|
| 174 |
+
This class implements a PEP302 finder and loader. It should be compatible
|
| 175 |
+
with Python 2.5 and all existing versions of Python3
|
| 176 |
+
"""
|
| 177 |
+
|
| 178 |
+
def __init__(self, six_module_name):
|
| 179 |
+
self.name = six_module_name
|
| 180 |
+
self.known_modules = {}
|
| 181 |
+
|
| 182 |
+
def _add_module(self, mod, *fullnames):
|
| 183 |
+
for fullname in fullnames:
|
| 184 |
+
self.known_modules[self.name + "." + fullname] = mod
|
| 185 |
+
|
| 186 |
+
def _get_module(self, fullname):
|
| 187 |
+
return self.known_modules[self.name + "." + fullname]
|
| 188 |
+
|
| 189 |
+
def find_module(self, fullname, path=None):
|
| 190 |
+
if fullname in self.known_modules:
|
| 191 |
+
return self
|
| 192 |
+
return None
|
| 193 |
+
|
| 194 |
+
def find_spec(self, fullname, path, target=None):
|
| 195 |
+
if fullname in self.known_modules:
|
| 196 |
+
return spec_from_loader(fullname, self)
|
| 197 |
+
return None
|
| 198 |
+
|
| 199 |
+
def __get_module(self, fullname):
|
| 200 |
+
try:
|
| 201 |
+
return self.known_modules[fullname]
|
| 202 |
+
except KeyError:
|
| 203 |
+
raise ImportError("This loader does not know module " + fullname)
|
| 204 |
+
|
| 205 |
+
def load_module(self, fullname):
|
| 206 |
+
try:
|
| 207 |
+
# in case of a reload
|
| 208 |
+
return sys.modules[fullname]
|
| 209 |
+
except KeyError:
|
| 210 |
+
pass
|
| 211 |
+
mod = self.__get_module(fullname)
|
| 212 |
+
if isinstance(mod, MovedModule):
|
| 213 |
+
mod = mod._resolve()
|
| 214 |
+
else:
|
| 215 |
+
mod.__loader__ = self
|
| 216 |
+
sys.modules[fullname] = mod
|
| 217 |
+
return mod
|
| 218 |
+
|
| 219 |
+
def is_package(self, fullname):
|
| 220 |
+
"""
|
| 221 |
+
Return true, if the named module is a package.
|
| 222 |
+
|
| 223 |
+
We need this method to get correct spec objects with
|
| 224 |
+
Python 3.4 (see PEP451)
|
| 225 |
+
"""
|
| 226 |
+
return hasattr(self.__get_module(fullname), "__path__")
|
| 227 |
+
|
| 228 |
+
def get_code(self, fullname):
|
| 229 |
+
"""Return None
|
| 230 |
+
|
| 231 |
+
Required, if is_package is implemented"""
|
| 232 |
+
self.__get_module(fullname) # eventually raises ImportError
|
| 233 |
+
return None
|
| 234 |
+
get_source = get_code # same as get_code
|
| 235 |
+
|
| 236 |
+
def create_module(self, spec):
|
| 237 |
+
return self.load_module(spec.name)
|
| 238 |
+
|
| 239 |
+
def exec_module(self, module):
|
| 240 |
+
pass
|
| 241 |
+
|
| 242 |
+
_importer = _SixMetaPathImporter(__name__)
|
| 243 |
+
|
| 244 |
+
|
| 245 |
+
class _MovedItems(_LazyModule):
|
| 246 |
+
|
| 247 |
+
"""Lazy loading of moved objects"""
|
| 248 |
+
__path__ = [] # mark as package
|
| 249 |
+
|
| 250 |
+
|
| 251 |
+
_moved_attributes = [
|
| 252 |
+
MovedAttribute("cStringIO", "cStringIO", "io", "StringIO"),
|
| 253 |
+
MovedAttribute("filter", "itertools", "builtins", "ifilter", "filter"),
|
| 254 |
+
MovedAttribute("filterfalse", "itertools", "itertools", "ifilterfalse", "filterfalse"),
|
| 255 |
+
MovedAttribute("input", "__builtin__", "builtins", "raw_input", "input"),
|
| 256 |
+
MovedAttribute("intern", "__builtin__", "sys"),
|
| 257 |
+
MovedAttribute("map", "itertools", "builtins", "imap", "map"),
|
| 258 |
+
MovedAttribute("getcwd", "os", "os", "getcwdu", "getcwd"),
|
| 259 |
+
MovedAttribute("getcwdb", "os", "os", "getcwd", "getcwdb"),
|
| 260 |
+
MovedAttribute("getoutput", "commands", "subprocess"),
|
| 261 |
+
MovedAttribute("range", "__builtin__", "builtins", "xrange", "range"),
|
| 262 |
+
MovedAttribute("reload_module", "__builtin__", "importlib" if PY34 else "imp", "reload"),
|
| 263 |
+
MovedAttribute("reduce", "__builtin__", "functools"),
|
| 264 |
+
MovedAttribute("shlex_quote", "pipes", "shlex", "quote"),
|
| 265 |
+
MovedAttribute("StringIO", "StringIO", "io"),
|
| 266 |
+
MovedAttribute("UserDict", "UserDict", "collections", "IterableUserDict", "UserDict"),
|
| 267 |
+
MovedAttribute("UserList", "UserList", "collections"),
|
| 268 |
+
MovedAttribute("UserString", "UserString", "collections"),
|
| 269 |
+
MovedAttribute("xrange", "__builtin__", "builtins", "xrange", "range"),
|
| 270 |
+
MovedAttribute("zip", "itertools", "builtins", "izip", "zip"),
|
| 271 |
+
MovedAttribute("zip_longest", "itertools", "itertools", "izip_longest", "zip_longest"),
|
| 272 |
+
MovedModule("builtins", "__builtin__"),
|
| 273 |
+
MovedModule("configparser", "ConfigParser"),
|
| 274 |
+
MovedModule("collections_abc", "collections", "collections.abc" if sys.version_info >= (3, 3) else "collections"),
|
| 275 |
+
MovedModule("copyreg", "copy_reg"),
|
| 276 |
+
MovedModule("dbm_gnu", "gdbm", "dbm.gnu"),
|
| 277 |
+
MovedModule("dbm_ndbm", "dbm", "dbm.ndbm"),
|
| 278 |
+
MovedModule("_dummy_thread", "dummy_thread", "_dummy_thread" if sys.version_info < (3, 9) else "_thread"),
|
| 279 |
+
MovedModule("http_cookiejar", "cookielib", "http.cookiejar"),
|
| 280 |
+
MovedModule("http_cookies", "Cookie", "http.cookies"),
|
| 281 |
+
MovedModule("html_entities", "htmlentitydefs", "html.entities"),
|
| 282 |
+
MovedModule("html_parser", "HTMLParser", "html.parser"),
|
| 283 |
+
MovedModule("http_client", "httplib", "http.client"),
|
| 284 |
+
MovedModule("email_mime_base", "email.MIMEBase", "email.mime.base"),
|
| 285 |
+
MovedModule("email_mime_image", "email.MIMEImage", "email.mime.image"),
|
| 286 |
+
MovedModule("email_mime_multipart", "email.MIMEMultipart", "email.mime.multipart"),
|
| 287 |
+
MovedModule("email_mime_nonmultipart", "email.MIMENonMultipart", "email.mime.nonmultipart"),
|
| 288 |
+
MovedModule("email_mime_text", "email.MIMEText", "email.mime.text"),
|
| 289 |
+
MovedModule("BaseHTTPServer", "BaseHTTPServer", "http.server"),
|
| 290 |
+
MovedModule("CGIHTTPServer", "CGIHTTPServer", "http.server"),
|
| 291 |
+
MovedModule("SimpleHTTPServer", "SimpleHTTPServer", "http.server"),
|
| 292 |
+
MovedModule("cPickle", "cPickle", "pickle"),
|
| 293 |
+
MovedModule("queue", "Queue"),
|
| 294 |
+
MovedModule("reprlib", "repr"),
|
| 295 |
+
MovedModule("socketserver", "SocketServer"),
|
| 296 |
+
MovedModule("_thread", "thread", "_thread"),
|
| 297 |
+
MovedModule("tkinter", "Tkinter"),
|
| 298 |
+
MovedModule("tkinter_dialog", "Dialog", "tkinter.dialog"),
|
| 299 |
+
MovedModule("tkinter_filedialog", "FileDialog", "tkinter.filedialog"),
|
| 300 |
+
MovedModule("tkinter_scrolledtext", "ScrolledText", "tkinter.scrolledtext"),
|
| 301 |
+
MovedModule("tkinter_simpledialog", "SimpleDialog", "tkinter.simpledialog"),
|
| 302 |
+
MovedModule("tkinter_tix", "Tix", "tkinter.tix"),
|
| 303 |
+
MovedModule("tkinter_ttk", "ttk", "tkinter.ttk"),
|
| 304 |
+
MovedModule("tkinter_constants", "Tkconstants", "tkinter.constants"),
|
| 305 |
+
MovedModule("tkinter_dnd", "Tkdnd", "tkinter.dnd"),
|
| 306 |
+
MovedModule("tkinter_colorchooser", "tkColorChooser",
|
| 307 |
+
"tkinter.colorchooser"),
|
| 308 |
+
MovedModule("tkinter_commondialog", "tkCommonDialog",
|
| 309 |
+
"tkinter.commondialog"),
|
| 310 |
+
MovedModule("tkinter_tkfiledialog", "tkFileDialog", "tkinter.filedialog"),
|
| 311 |
+
MovedModule("tkinter_font", "tkFont", "tkinter.font"),
|
| 312 |
+
MovedModule("tkinter_messagebox", "tkMessageBox", "tkinter.messagebox"),
|
| 313 |
+
MovedModule("tkinter_tksimpledialog", "tkSimpleDialog",
|
| 314 |
+
"tkinter.simpledialog"),
|
| 315 |
+
MovedModule("urllib_parse", __name__ + ".moves.urllib_parse", "urllib.parse"),
|
| 316 |
+
MovedModule("urllib_error", __name__ + ".moves.urllib_error", "urllib.error"),
|
| 317 |
+
MovedModule("urllib", __name__ + ".moves.urllib", __name__ + ".moves.urllib"),
|
| 318 |
+
MovedModule("urllib_robotparser", "robotparser", "urllib.robotparser"),
|
| 319 |
+
MovedModule("xmlrpc_client", "xmlrpclib", "xmlrpc.client"),
|
| 320 |
+
MovedModule("xmlrpc_server", "SimpleXMLRPCServer", "xmlrpc.server"),
|
| 321 |
+
]
|
| 322 |
+
# Add windows specific modules.
|
| 323 |
+
if sys.platform == "win32":
|
| 324 |
+
_moved_attributes += [
|
| 325 |
+
MovedModule("winreg", "_winreg"),
|
| 326 |
+
]
|
| 327 |
+
|
| 328 |
+
for attr in _moved_attributes:
|
| 329 |
+
setattr(_MovedItems, attr.name, attr)
|
| 330 |
+
if isinstance(attr, MovedModule):
|
| 331 |
+
_importer._add_module(attr, "moves." + attr.name)
|
| 332 |
+
del attr
|
| 333 |
+
|
| 334 |
+
_MovedItems._moved_attributes = _moved_attributes
|
| 335 |
+
|
| 336 |
+
moves = _MovedItems(__name__ + ".moves")
|
| 337 |
+
_importer._add_module(moves, "moves")
|
| 338 |
+
|
| 339 |
+
|
| 340 |
+
class Module_six_moves_urllib_parse(_LazyModule):
|
| 341 |
+
|
| 342 |
+
"""Lazy loading of moved objects in six.moves.urllib_parse"""
|
| 343 |
+
|
| 344 |
+
|
| 345 |
+
_urllib_parse_moved_attributes = [
|
| 346 |
+
MovedAttribute("ParseResult", "urlparse", "urllib.parse"),
|
| 347 |
+
MovedAttribute("SplitResult", "urlparse", "urllib.parse"),
|
| 348 |
+
MovedAttribute("parse_qs", "urlparse", "urllib.parse"),
|
| 349 |
+
MovedAttribute("parse_qsl", "urlparse", "urllib.parse"),
|
| 350 |
+
MovedAttribute("urldefrag", "urlparse", "urllib.parse"),
|
| 351 |
+
MovedAttribute("urljoin", "urlparse", "urllib.parse"),
|
| 352 |
+
MovedAttribute("urlparse", "urlparse", "urllib.parse"),
|
| 353 |
+
MovedAttribute("urlsplit", "urlparse", "urllib.parse"),
|
| 354 |
+
MovedAttribute("urlunparse", "urlparse", "urllib.parse"),
|
| 355 |
+
MovedAttribute("urlunsplit", "urlparse", "urllib.parse"),
|
| 356 |
+
MovedAttribute("quote", "urllib", "urllib.parse"),
|
| 357 |
+
MovedAttribute("quote_plus", "urllib", "urllib.parse"),
|
| 358 |
+
MovedAttribute("unquote", "urllib", "urllib.parse"),
|
| 359 |
+
MovedAttribute("unquote_plus", "urllib", "urllib.parse"),
|
| 360 |
+
MovedAttribute("unquote_to_bytes", "urllib", "urllib.parse", "unquote", "unquote_to_bytes"),
|
| 361 |
+
MovedAttribute("urlencode", "urllib", "urllib.parse"),
|
| 362 |
+
MovedAttribute("splitquery", "urllib", "urllib.parse"),
|
| 363 |
+
MovedAttribute("splittag", "urllib", "urllib.parse"),
|
| 364 |
+
MovedAttribute("splituser", "urllib", "urllib.parse"),
|
| 365 |
+
MovedAttribute("splitvalue", "urllib", "urllib.parse"),
|
| 366 |
+
MovedAttribute("uses_fragment", "urlparse", "urllib.parse"),
|
| 367 |
+
MovedAttribute("uses_netloc", "urlparse", "urllib.parse"),
|
| 368 |
+
MovedAttribute("uses_params", "urlparse", "urllib.parse"),
|
| 369 |
+
MovedAttribute("uses_query", "urlparse", "urllib.parse"),
|
| 370 |
+
MovedAttribute("uses_relative", "urlparse", "urllib.parse"),
|
| 371 |
+
]
|
| 372 |
+
for attr in _urllib_parse_moved_attributes:
|
| 373 |
+
setattr(Module_six_moves_urllib_parse, attr.name, attr)
|
| 374 |
+
del attr
|
| 375 |
+
|
| 376 |
+
Module_six_moves_urllib_parse._moved_attributes = _urllib_parse_moved_attributes
|
| 377 |
+
|
| 378 |
+
_importer._add_module(Module_six_moves_urllib_parse(__name__ + ".moves.urllib_parse"),
|
| 379 |
+
"moves.urllib_parse", "moves.urllib.parse")
|
| 380 |
+
|
| 381 |
+
|
| 382 |
+
class Module_six_moves_urllib_error(_LazyModule):
|
| 383 |
+
|
| 384 |
+
"""Lazy loading of moved objects in six.moves.urllib_error"""
|
| 385 |
+
|
| 386 |
+
|
| 387 |
+
_urllib_error_moved_attributes = [
|
| 388 |
+
MovedAttribute("URLError", "urllib2", "urllib.error"),
|
| 389 |
+
MovedAttribute("HTTPError", "urllib2", "urllib.error"),
|
| 390 |
+
MovedAttribute("ContentTooShortError", "urllib", "urllib.error"),
|
| 391 |
+
]
|
| 392 |
+
for attr in _urllib_error_moved_attributes:
|
| 393 |
+
setattr(Module_six_moves_urllib_error, attr.name, attr)
|
| 394 |
+
del attr
|
| 395 |
+
|
| 396 |
+
Module_six_moves_urllib_error._moved_attributes = _urllib_error_moved_attributes
|
| 397 |
+
|
| 398 |
+
_importer._add_module(Module_six_moves_urllib_error(__name__ + ".moves.urllib.error"),
|
| 399 |
+
"moves.urllib_error", "moves.urllib.error")
|
| 400 |
+
|
| 401 |
+
|
| 402 |
+
class Module_six_moves_urllib_request(_LazyModule):
|
| 403 |
+
|
| 404 |
+
"""Lazy loading of moved objects in six.moves.urllib_request"""
|
| 405 |
+
|
| 406 |
+
|
| 407 |
+
_urllib_request_moved_attributes = [
|
| 408 |
+
MovedAttribute("urlopen", "urllib2", "urllib.request"),
|
| 409 |
+
MovedAttribute("install_opener", "urllib2", "urllib.request"),
|
| 410 |
+
MovedAttribute("build_opener", "urllib2", "urllib.request"),
|
| 411 |
+
MovedAttribute("pathname2url", "urllib", "urllib.request"),
|
| 412 |
+
MovedAttribute("url2pathname", "urllib", "urllib.request"),
|
| 413 |
+
MovedAttribute("getproxies", "urllib", "urllib.request"),
|
| 414 |
+
MovedAttribute("Request", "urllib2", "urllib.request"),
|
| 415 |
+
MovedAttribute("OpenerDirector", "urllib2", "urllib.request"),
|
| 416 |
+
MovedAttribute("HTTPDefaultErrorHandler", "urllib2", "urllib.request"),
|
| 417 |
+
MovedAttribute("HTTPRedirectHandler", "urllib2", "urllib.request"),
|
| 418 |
+
MovedAttribute("HTTPCookieProcessor", "urllib2", "urllib.request"),
|
| 419 |
+
MovedAttribute("ProxyHandler", "urllib2", "urllib.request"),
|
| 420 |
+
MovedAttribute("BaseHandler", "urllib2", "urllib.request"),
|
| 421 |
+
MovedAttribute("HTTPPasswordMgr", "urllib2", "urllib.request"),
|
| 422 |
+
MovedAttribute("HTTPPasswordMgrWithDefaultRealm", "urllib2", "urllib.request"),
|
| 423 |
+
MovedAttribute("AbstractBasicAuthHandler", "urllib2", "urllib.request"),
|
| 424 |
+
MovedAttribute("HTTPBasicAuthHandler", "urllib2", "urllib.request"),
|
| 425 |
+
MovedAttribute("ProxyBasicAuthHandler", "urllib2", "urllib.request"),
|
| 426 |
+
MovedAttribute("AbstractDigestAuthHandler", "urllib2", "urllib.request"),
|
| 427 |
+
MovedAttribute("HTTPDigestAuthHandler", "urllib2", "urllib.request"),
|
| 428 |
+
MovedAttribute("ProxyDigestAuthHandler", "urllib2", "urllib.request"),
|
| 429 |
+
MovedAttribute("HTTPHandler", "urllib2", "urllib.request"),
|
| 430 |
+
MovedAttribute("HTTPSHandler", "urllib2", "urllib.request"),
|
| 431 |
+
MovedAttribute("FileHandler", "urllib2", "urllib.request"),
|
| 432 |
+
MovedAttribute("FTPHandler", "urllib2", "urllib.request"),
|
| 433 |
+
MovedAttribute("CacheFTPHandler", "urllib2", "urllib.request"),
|
| 434 |
+
MovedAttribute("UnknownHandler", "urllib2", "urllib.request"),
|
| 435 |
+
MovedAttribute("HTTPErrorProcessor", "urllib2", "urllib.request"),
|
| 436 |
+
MovedAttribute("urlretrieve", "urllib", "urllib.request"),
|
| 437 |
+
MovedAttribute("urlcleanup", "urllib", "urllib.request"),
|
| 438 |
+
MovedAttribute("proxy_bypass", "urllib", "urllib.request"),
|
| 439 |
+
MovedAttribute("parse_http_list", "urllib2", "urllib.request"),
|
| 440 |
+
MovedAttribute("parse_keqv_list", "urllib2", "urllib.request"),
|
| 441 |
+
]
|
| 442 |
+
if sys.version_info[:2] < (3, 14):
|
| 443 |
+
_urllib_request_moved_attributes.extend(
|
| 444 |
+
[
|
| 445 |
+
MovedAttribute("URLopener", "urllib", "urllib.request"),
|
| 446 |
+
MovedAttribute("FancyURLopener", "urllib", "urllib.request"),
|
| 447 |
+
]
|
| 448 |
+
)
|
| 449 |
+
for attr in _urllib_request_moved_attributes:
|
| 450 |
+
setattr(Module_six_moves_urllib_request, attr.name, attr)
|
| 451 |
+
del attr
|
| 452 |
+
|
| 453 |
+
Module_six_moves_urllib_request._moved_attributes = _urllib_request_moved_attributes
|
| 454 |
+
|
| 455 |
+
_importer._add_module(Module_six_moves_urllib_request(__name__ + ".moves.urllib.request"),
|
| 456 |
+
"moves.urllib_request", "moves.urllib.request")
|
| 457 |
+
|
| 458 |
+
|
| 459 |
+
class Module_six_moves_urllib_response(_LazyModule):
|
| 460 |
+
|
| 461 |
+
"""Lazy loading of moved objects in six.moves.urllib_response"""
|
| 462 |
+
|
| 463 |
+
|
| 464 |
+
_urllib_response_moved_attributes = [
|
| 465 |
+
MovedAttribute("addbase", "urllib", "urllib.response"),
|
| 466 |
+
MovedAttribute("addclosehook", "urllib", "urllib.response"),
|
| 467 |
+
MovedAttribute("addinfo", "urllib", "urllib.response"),
|
| 468 |
+
MovedAttribute("addinfourl", "urllib", "urllib.response"),
|
| 469 |
+
]
|
| 470 |
+
for attr in _urllib_response_moved_attributes:
|
| 471 |
+
setattr(Module_six_moves_urllib_response, attr.name, attr)
|
| 472 |
+
del attr
|
| 473 |
+
|
| 474 |
+
Module_six_moves_urllib_response._moved_attributes = _urllib_response_moved_attributes
|
| 475 |
+
|
| 476 |
+
_importer._add_module(Module_six_moves_urllib_response(__name__ + ".moves.urllib.response"),
|
| 477 |
+
"moves.urllib_response", "moves.urllib.response")
|
| 478 |
+
|
| 479 |
+
|
| 480 |
+
class Module_six_moves_urllib_robotparser(_LazyModule):
|
| 481 |
+
|
| 482 |
+
"""Lazy loading of moved objects in six.moves.urllib_robotparser"""
|
| 483 |
+
|
| 484 |
+
|
| 485 |
+
_urllib_robotparser_moved_attributes = [
|
| 486 |
+
MovedAttribute("RobotFileParser", "robotparser", "urllib.robotparser"),
|
| 487 |
+
]
|
| 488 |
+
for attr in _urllib_robotparser_moved_attributes:
|
| 489 |
+
setattr(Module_six_moves_urllib_robotparser, attr.name, attr)
|
| 490 |
+
del attr
|
| 491 |
+
|
| 492 |
+
Module_six_moves_urllib_robotparser._moved_attributes = _urllib_robotparser_moved_attributes
|
| 493 |
+
|
| 494 |
+
_importer._add_module(Module_six_moves_urllib_robotparser(__name__ + ".moves.urllib.robotparser"),
|
| 495 |
+
"moves.urllib_robotparser", "moves.urllib.robotparser")
|
| 496 |
+
|
| 497 |
+
|
| 498 |
+
class Module_six_moves_urllib(types.ModuleType):
|
| 499 |
+
|
| 500 |
+
"""Create a six.moves.urllib namespace that resembles the Python 3 namespace"""
|
| 501 |
+
__path__ = [] # mark as package
|
| 502 |
+
parse = _importer._get_module("moves.urllib_parse")
|
| 503 |
+
error = _importer._get_module("moves.urllib_error")
|
| 504 |
+
request = _importer._get_module("moves.urllib_request")
|
| 505 |
+
response = _importer._get_module("moves.urllib_response")
|
| 506 |
+
robotparser = _importer._get_module("moves.urllib_robotparser")
|
| 507 |
+
|
| 508 |
+
def __dir__(self):
|
| 509 |
+
return ['parse', 'error', 'request', 'response', 'robotparser']
|
| 510 |
+
|
| 511 |
+
_importer._add_module(Module_six_moves_urllib(__name__ + ".moves.urllib"),
|
| 512 |
+
"moves.urllib")
|
| 513 |
+
|
| 514 |
+
|
| 515 |
+
def add_move(move):
|
| 516 |
+
"""Add an item to six.moves."""
|
| 517 |
+
setattr(_MovedItems, move.name, move)
|
| 518 |
+
|
| 519 |
+
|
| 520 |
+
def remove_move(name):
|
| 521 |
+
"""Remove item from six.moves."""
|
| 522 |
+
try:
|
| 523 |
+
delattr(_MovedItems, name)
|
| 524 |
+
except AttributeError:
|
| 525 |
+
try:
|
| 526 |
+
del moves.__dict__[name]
|
| 527 |
+
except KeyError:
|
| 528 |
+
raise AttributeError("no such move, %r" % (name,))
|
| 529 |
+
|
| 530 |
+
|
| 531 |
+
if PY3:
|
| 532 |
+
_meth_func = "__func__"
|
| 533 |
+
_meth_self = "__self__"
|
| 534 |
+
|
| 535 |
+
_func_closure = "__closure__"
|
| 536 |
+
_func_code = "__code__"
|
| 537 |
+
_func_defaults = "__defaults__"
|
| 538 |
+
_func_globals = "__globals__"
|
| 539 |
+
else:
|
| 540 |
+
_meth_func = "im_func"
|
| 541 |
+
_meth_self = "im_self"
|
| 542 |
+
|
| 543 |
+
_func_closure = "func_closure"
|
| 544 |
+
_func_code = "func_code"
|
| 545 |
+
_func_defaults = "func_defaults"
|
| 546 |
+
_func_globals = "func_globals"
|
| 547 |
+
|
| 548 |
+
|
| 549 |
+
try:
|
| 550 |
+
advance_iterator = next
|
| 551 |
+
except NameError:
|
| 552 |
+
def advance_iterator(it):
|
| 553 |
+
return it.next()
|
| 554 |
+
next = advance_iterator
|
| 555 |
+
|
| 556 |
+
|
| 557 |
+
try:
|
| 558 |
+
callable = callable
|
| 559 |
+
except NameError:
|
| 560 |
+
def callable(obj):
|
| 561 |
+
return any("__call__" in klass.__dict__ for klass in type(obj).__mro__)
|
| 562 |
+
|
| 563 |
+
|
| 564 |
+
if PY3:
|
| 565 |
+
def get_unbound_function(unbound):
|
| 566 |
+
return unbound
|
| 567 |
+
|
| 568 |
+
create_bound_method = types.MethodType
|
| 569 |
+
|
| 570 |
+
def create_unbound_method(func, cls):
|
| 571 |
+
return func
|
| 572 |
+
|
| 573 |
+
Iterator = object
|
| 574 |
+
else:
|
| 575 |
+
def get_unbound_function(unbound):
|
| 576 |
+
return unbound.im_func
|
| 577 |
+
|
| 578 |
+
def create_bound_method(func, obj):
|
| 579 |
+
return types.MethodType(func, obj, obj.__class__)
|
| 580 |
+
|
| 581 |
+
def create_unbound_method(func, cls):
|
| 582 |
+
return types.MethodType(func, None, cls)
|
| 583 |
+
|
| 584 |
+
class Iterator(object):
|
| 585 |
+
|
| 586 |
+
def next(self):
|
| 587 |
+
return type(self).__next__(self)
|
| 588 |
+
|
| 589 |
+
callable = callable
|
| 590 |
+
_add_doc(get_unbound_function,
|
| 591 |
+
"""Get the function out of a possibly unbound function""")
|
| 592 |
+
|
| 593 |
+
|
| 594 |
+
get_method_function = operator.attrgetter(_meth_func)
|
| 595 |
+
get_method_self = operator.attrgetter(_meth_self)
|
| 596 |
+
get_function_closure = operator.attrgetter(_func_closure)
|
| 597 |
+
get_function_code = operator.attrgetter(_func_code)
|
| 598 |
+
get_function_defaults = operator.attrgetter(_func_defaults)
|
| 599 |
+
get_function_globals = operator.attrgetter(_func_globals)
|
| 600 |
+
|
| 601 |
+
|
| 602 |
+
if PY3:
|
| 603 |
+
def iterkeys(d, **kw):
|
| 604 |
+
return iter(d.keys(**kw))
|
| 605 |
+
|
| 606 |
+
def itervalues(d, **kw):
|
| 607 |
+
return iter(d.values(**kw))
|
| 608 |
+
|
| 609 |
+
def iteritems(d, **kw):
|
| 610 |
+
return iter(d.items(**kw))
|
| 611 |
+
|
| 612 |
+
def iterlists(d, **kw):
|
| 613 |
+
return iter(d.lists(**kw))
|
| 614 |
+
|
| 615 |
+
viewkeys = operator.methodcaller("keys")
|
| 616 |
+
|
| 617 |
+
viewvalues = operator.methodcaller("values")
|
| 618 |
+
|
| 619 |
+
viewitems = operator.methodcaller("items")
|
| 620 |
+
else:
|
| 621 |
+
def iterkeys(d, **kw):
|
| 622 |
+
return d.iterkeys(**kw)
|
| 623 |
+
|
| 624 |
+
def itervalues(d, **kw):
|
| 625 |
+
return d.itervalues(**kw)
|
| 626 |
+
|
| 627 |
+
def iteritems(d, **kw):
|
| 628 |
+
return d.iteritems(**kw)
|
| 629 |
+
|
| 630 |
+
def iterlists(d, **kw):
|
| 631 |
+
return d.iterlists(**kw)
|
| 632 |
+
|
| 633 |
+
viewkeys = operator.methodcaller("viewkeys")
|
| 634 |
+
|
| 635 |
+
viewvalues = operator.methodcaller("viewvalues")
|
| 636 |
+
|
| 637 |
+
viewitems = operator.methodcaller("viewitems")
|
| 638 |
+
|
| 639 |
+
_add_doc(iterkeys, "Return an iterator over the keys of a dictionary.")
|
| 640 |
+
_add_doc(itervalues, "Return an iterator over the values of a dictionary.")
|
| 641 |
+
_add_doc(iteritems,
|
| 642 |
+
"Return an iterator over the (key, value) pairs of a dictionary.")
|
| 643 |
+
_add_doc(iterlists,
|
| 644 |
+
"Return an iterator over the (key, [values]) pairs of a dictionary.")
|
| 645 |
+
|
| 646 |
+
|
| 647 |
+
if PY3:
|
| 648 |
+
def b(s):
|
| 649 |
+
return s.encode("latin-1")
|
| 650 |
+
|
| 651 |
+
def u(s):
|
| 652 |
+
return s
|
| 653 |
+
unichr = chr
|
| 654 |
+
import struct
|
| 655 |
+
int2byte = struct.Struct(">B").pack
|
| 656 |
+
del struct
|
| 657 |
+
byte2int = operator.itemgetter(0)
|
| 658 |
+
indexbytes = operator.getitem
|
| 659 |
+
iterbytes = iter
|
| 660 |
+
import io
|
| 661 |
+
StringIO = io.StringIO
|
| 662 |
+
BytesIO = io.BytesIO
|
| 663 |
+
del io
|
| 664 |
+
_assertCountEqual = "assertCountEqual"
|
| 665 |
+
if sys.version_info[1] <= 1:
|
| 666 |
+
_assertRaisesRegex = "assertRaisesRegexp"
|
| 667 |
+
_assertRegex = "assertRegexpMatches"
|
| 668 |
+
_assertNotRegex = "assertNotRegexpMatches"
|
| 669 |
+
else:
|
| 670 |
+
_assertRaisesRegex = "assertRaisesRegex"
|
| 671 |
+
_assertRegex = "assertRegex"
|
| 672 |
+
_assertNotRegex = "assertNotRegex"
|
| 673 |
+
else:
|
| 674 |
+
def b(s):
|
| 675 |
+
return s
|
| 676 |
+
# Workaround for standalone backslash
|
| 677 |
+
|
| 678 |
+
def u(s):
|
| 679 |
+
return unicode(s.replace(r'\\', r'\\\\'), "unicode_escape")
|
| 680 |
+
unichr = unichr
|
| 681 |
+
int2byte = chr
|
| 682 |
+
|
| 683 |
+
def byte2int(bs):
|
| 684 |
+
return ord(bs[0])
|
| 685 |
+
|
| 686 |
+
def indexbytes(buf, i):
|
| 687 |
+
return ord(buf[i])
|
| 688 |
+
iterbytes = functools.partial(itertools.imap, ord)
|
| 689 |
+
import StringIO
|
| 690 |
+
StringIO = BytesIO = StringIO.StringIO
|
| 691 |
+
_assertCountEqual = "assertItemsEqual"
|
| 692 |
+
_assertRaisesRegex = "assertRaisesRegexp"
|
| 693 |
+
_assertRegex = "assertRegexpMatches"
|
| 694 |
+
_assertNotRegex = "assertNotRegexpMatches"
|
| 695 |
+
_add_doc(b, """Byte literal""")
|
| 696 |
+
_add_doc(u, """Text literal""")
|
| 697 |
+
|
| 698 |
+
|
| 699 |
+
def assertCountEqual(self, *args, **kwargs):
|
| 700 |
+
return getattr(self, _assertCountEqual)(*args, **kwargs)
|
| 701 |
+
|
| 702 |
+
|
| 703 |
+
def assertRaisesRegex(self, *args, **kwargs):
|
| 704 |
+
return getattr(self, _assertRaisesRegex)(*args, **kwargs)
|
| 705 |
+
|
| 706 |
+
|
| 707 |
+
def assertRegex(self, *args, **kwargs):
|
| 708 |
+
return getattr(self, _assertRegex)(*args, **kwargs)
|
| 709 |
+
|
| 710 |
+
|
| 711 |
+
def assertNotRegex(self, *args, **kwargs):
|
| 712 |
+
return getattr(self, _assertNotRegex)(*args, **kwargs)
|
| 713 |
+
|
| 714 |
+
|
| 715 |
+
if PY3:
|
| 716 |
+
exec_ = getattr(moves.builtins, "exec")
|
| 717 |
+
|
| 718 |
+
def reraise(tp, value, tb=None):
|
| 719 |
+
try:
|
| 720 |
+
if value is None:
|
| 721 |
+
value = tp()
|
| 722 |
+
if value.__traceback__ is not tb:
|
| 723 |
+
raise value.with_traceback(tb)
|
| 724 |
+
raise value
|
| 725 |
+
finally:
|
| 726 |
+
value = None
|
| 727 |
+
tb = None
|
| 728 |
+
|
| 729 |
+
else:
|
| 730 |
+
def exec_(_code_, _globs_=None, _locs_=None):
|
| 731 |
+
"""Execute code in a namespace."""
|
| 732 |
+
if _globs_ is None:
|
| 733 |
+
frame = sys._getframe(1)
|
| 734 |
+
_globs_ = frame.f_globals
|
| 735 |
+
if _locs_ is None:
|
| 736 |
+
_locs_ = frame.f_locals
|
| 737 |
+
del frame
|
| 738 |
+
elif _locs_ is None:
|
| 739 |
+
_locs_ = _globs_
|
| 740 |
+
exec("""exec _code_ in _globs_, _locs_""")
|
| 741 |
+
|
| 742 |
+
exec_("""def reraise(tp, value, tb=None):
|
| 743 |
+
try:
|
| 744 |
+
raise tp, value, tb
|
| 745 |
+
finally:
|
| 746 |
+
tb = None
|
| 747 |
+
""")
|
| 748 |
+
|
| 749 |
+
|
| 750 |
+
if sys.version_info[:2] > (3,):
|
| 751 |
+
exec_("""def raise_from(value, from_value):
|
| 752 |
+
try:
|
| 753 |
+
raise value from from_value
|
| 754 |
+
finally:
|
| 755 |
+
value = None
|
| 756 |
+
""")
|
| 757 |
+
else:
|
| 758 |
+
def raise_from(value, from_value):
|
| 759 |
+
raise value
|
| 760 |
+
|
| 761 |
+
|
| 762 |
+
print_ = getattr(moves.builtins, "print", None)
|
| 763 |
+
if print_ is None:
|
| 764 |
+
def print_(*args, **kwargs):
|
| 765 |
+
"""The new-style print function for Python 2.4 and 2.5."""
|
| 766 |
+
fp = kwargs.pop("file", sys.stdout)
|
| 767 |
+
if fp is None:
|
| 768 |
+
return
|
| 769 |
+
|
| 770 |
+
def write(data):
|
| 771 |
+
if not isinstance(data, basestring):
|
| 772 |
+
data = str(data)
|
| 773 |
+
# If the file has an encoding, encode unicode with it.
|
| 774 |
+
if (isinstance(fp, file) and
|
| 775 |
+
isinstance(data, unicode) and
|
| 776 |
+
fp.encoding is not None):
|
| 777 |
+
errors = getattr(fp, "errors", None)
|
| 778 |
+
if errors is None:
|
| 779 |
+
errors = "strict"
|
| 780 |
+
data = data.encode(fp.encoding, errors)
|
| 781 |
+
fp.write(data)
|
| 782 |
+
want_unicode = False
|
| 783 |
+
sep = kwargs.pop("sep", None)
|
| 784 |
+
if sep is not None:
|
| 785 |
+
if isinstance(sep, unicode):
|
| 786 |
+
want_unicode = True
|
| 787 |
+
elif not isinstance(sep, str):
|
| 788 |
+
raise TypeError("sep must be None or a string")
|
| 789 |
+
end = kwargs.pop("end", None)
|
| 790 |
+
if end is not None:
|
| 791 |
+
if isinstance(end, unicode):
|
| 792 |
+
want_unicode = True
|
| 793 |
+
elif not isinstance(end, str):
|
| 794 |
+
raise TypeError("end must be None or a string")
|
| 795 |
+
if kwargs:
|
| 796 |
+
raise TypeError("invalid keyword arguments to print()")
|
| 797 |
+
if not want_unicode:
|
| 798 |
+
for arg in args:
|
| 799 |
+
if isinstance(arg, unicode):
|
| 800 |
+
want_unicode = True
|
| 801 |
+
break
|
| 802 |
+
if want_unicode:
|
| 803 |
+
newline = unicode("\n")
|
| 804 |
+
space = unicode(" ")
|
| 805 |
+
else:
|
| 806 |
+
newline = "\n"
|
| 807 |
+
space = " "
|
| 808 |
+
if sep is None:
|
| 809 |
+
sep = space
|
| 810 |
+
if end is None:
|
| 811 |
+
end = newline
|
| 812 |
+
for i, arg in enumerate(args):
|
| 813 |
+
if i:
|
| 814 |
+
write(sep)
|
| 815 |
+
write(arg)
|
| 816 |
+
write(end)
|
| 817 |
+
if sys.version_info[:2] < (3, 3):
|
| 818 |
+
_print = print_
|
| 819 |
+
|
| 820 |
+
def print_(*args, **kwargs):
|
| 821 |
+
fp = kwargs.get("file", sys.stdout)
|
| 822 |
+
flush = kwargs.pop("flush", False)
|
| 823 |
+
_print(*args, **kwargs)
|
| 824 |
+
if flush and fp is not None:
|
| 825 |
+
fp.flush()
|
| 826 |
+
|
| 827 |
+
_add_doc(reraise, """Reraise an exception.""")
|
| 828 |
+
|
| 829 |
+
if sys.version_info[0:2] < (3, 4):
|
| 830 |
+
# This does exactly the same what the :func:`py3:functools.update_wrapper`
|
| 831 |
+
# function does on Python versions after 3.2. It sets the ``__wrapped__``
|
| 832 |
+
# attribute on ``wrapper`` object and it doesn't raise an error if any of
|
| 833 |
+
# the attributes mentioned in ``assigned`` and ``updated`` are missing on
|
| 834 |
+
# ``wrapped`` object.
|
| 835 |
+
def _update_wrapper(wrapper, wrapped,
|
| 836 |
+
assigned=functools.WRAPPER_ASSIGNMENTS,
|
| 837 |
+
updated=functools.WRAPPER_UPDATES):
|
| 838 |
+
for attr in assigned:
|
| 839 |
+
try:
|
| 840 |
+
value = getattr(wrapped, attr)
|
| 841 |
+
except AttributeError:
|
| 842 |
+
continue
|
| 843 |
+
else:
|
| 844 |
+
setattr(wrapper, attr, value)
|
| 845 |
+
for attr in updated:
|
| 846 |
+
getattr(wrapper, attr).update(getattr(wrapped, attr, {}))
|
| 847 |
+
wrapper.__wrapped__ = wrapped
|
| 848 |
+
return wrapper
|
| 849 |
+
_update_wrapper.__doc__ = functools.update_wrapper.__doc__
|
| 850 |
+
|
| 851 |
+
def wraps(wrapped, assigned=functools.WRAPPER_ASSIGNMENTS,
|
| 852 |
+
updated=functools.WRAPPER_UPDATES):
|
| 853 |
+
return functools.partial(_update_wrapper, wrapped=wrapped,
|
| 854 |
+
assigned=assigned, updated=updated)
|
| 855 |
+
wraps.__doc__ = functools.wraps.__doc__
|
| 856 |
+
|
| 857 |
+
else:
|
| 858 |
+
wraps = functools.wraps
|
| 859 |
+
|
| 860 |
+
|
| 861 |
+
def with_metaclass(meta, *bases):
|
| 862 |
+
"""Create a base class with a metaclass."""
|
| 863 |
+
# This requires a bit of explanation: the basic idea is to make a dummy
|
| 864 |
+
# metaclass for one level of class instantiation that replaces itself with
|
| 865 |
+
# the actual metaclass.
|
| 866 |
+
class metaclass(type):
|
| 867 |
+
|
| 868 |
+
def __new__(cls, name, this_bases, d):
|
| 869 |
+
if sys.version_info[:2] >= (3, 7):
|
| 870 |
+
# This version introduced PEP 560 that requires a bit
|
| 871 |
+
# of extra care (we mimic what is done by __build_class__).
|
| 872 |
+
resolved_bases = types.resolve_bases(bases)
|
| 873 |
+
if resolved_bases is not bases:
|
| 874 |
+
d['__orig_bases__'] = bases
|
| 875 |
+
else:
|
| 876 |
+
resolved_bases = bases
|
| 877 |
+
return meta(name, resolved_bases, d)
|
| 878 |
+
|
| 879 |
+
@classmethod
|
| 880 |
+
def __prepare__(cls, name, this_bases):
|
| 881 |
+
return meta.__prepare__(name, bases)
|
| 882 |
+
return type.__new__(metaclass, 'temporary_class', (), {})
|
| 883 |
+
|
| 884 |
+
|
| 885 |
+
def add_metaclass(metaclass):
|
| 886 |
+
"""Class decorator for creating a class with a metaclass."""
|
| 887 |
+
def wrapper(cls):
|
| 888 |
+
orig_vars = cls.__dict__.copy()
|
| 889 |
+
slots = orig_vars.get('__slots__')
|
| 890 |
+
if slots is not None:
|
| 891 |
+
if isinstance(slots, str):
|
| 892 |
+
slots = [slots]
|
| 893 |
+
for slots_var in slots:
|
| 894 |
+
orig_vars.pop(slots_var)
|
| 895 |
+
orig_vars.pop('__dict__', None)
|
| 896 |
+
orig_vars.pop('__weakref__', None)
|
| 897 |
+
if hasattr(cls, '__qualname__'):
|
| 898 |
+
orig_vars['__qualname__'] = cls.__qualname__
|
| 899 |
+
return metaclass(cls.__name__, cls.__bases__, orig_vars)
|
| 900 |
+
return wrapper
|
| 901 |
+
|
| 902 |
+
|
| 903 |
+
def ensure_binary(s, encoding='utf-8', errors='strict'):
|
| 904 |
+
"""Coerce **s** to six.binary_type.
|
| 905 |
+
|
| 906 |
+
For Python 2:
|
| 907 |
+
- `unicode` -> encoded to `str`
|
| 908 |
+
- `str` -> `str`
|
| 909 |
+
|
| 910 |
+
For Python 3:
|
| 911 |
+
- `str` -> encoded to `bytes`
|
| 912 |
+
- `bytes` -> `bytes`
|
| 913 |
+
"""
|
| 914 |
+
if isinstance(s, binary_type):
|
| 915 |
+
return s
|
| 916 |
+
if isinstance(s, text_type):
|
| 917 |
+
return s.encode(encoding, errors)
|
| 918 |
+
raise TypeError("not expecting type '%s'" % type(s))
|
| 919 |
+
|
| 920 |
+
|
| 921 |
+
def ensure_str(s, encoding='utf-8', errors='strict'):
|
| 922 |
+
"""Coerce *s* to `str`.
|
| 923 |
+
|
| 924 |
+
For Python 2:
|
| 925 |
+
- `unicode` -> encoded to `str`
|
| 926 |
+
- `str` -> `str`
|
| 927 |
+
|
| 928 |
+
For Python 3:
|
| 929 |
+
- `str` -> `str`
|
| 930 |
+
- `bytes` -> decoded to `str`
|
| 931 |
+
"""
|
| 932 |
+
# Optimization: Fast return for the common case.
|
| 933 |
+
if type(s) is str:
|
| 934 |
+
return s
|
| 935 |
+
if PY2 and isinstance(s, text_type):
|
| 936 |
+
return s.encode(encoding, errors)
|
| 937 |
+
elif PY3 and isinstance(s, binary_type):
|
| 938 |
+
return s.decode(encoding, errors)
|
| 939 |
+
elif not isinstance(s, (text_type, binary_type)):
|
| 940 |
+
raise TypeError("not expecting type '%s'" % type(s))
|
| 941 |
+
return s
|
| 942 |
+
|
| 943 |
+
|
| 944 |
+
def ensure_text(s, encoding='utf-8', errors='strict'):
|
| 945 |
+
"""Coerce *s* to six.text_type.
|
| 946 |
+
|
| 947 |
+
For Python 2:
|
| 948 |
+
- `unicode` -> `unicode`
|
| 949 |
+
- `str` -> `unicode`
|
| 950 |
+
|
| 951 |
+
For Python 3:
|
| 952 |
+
- `str` -> `str`
|
| 953 |
+
- `bytes` -> decoded to `str`
|
| 954 |
+
"""
|
| 955 |
+
if isinstance(s, binary_type):
|
| 956 |
+
return s.decode(encoding, errors)
|
| 957 |
+
elif isinstance(s, text_type):
|
| 958 |
+
return s
|
| 959 |
+
else:
|
| 960 |
+
raise TypeError("not expecting type '%s'" % type(s))
|
| 961 |
+
|
| 962 |
+
|
| 963 |
+
def python_2_unicode_compatible(klass):
|
| 964 |
+
"""
|
| 965 |
+
A class decorator that defines __unicode__ and __str__ methods under Python 2.
|
| 966 |
+
Under Python 3 it does nothing.
|
| 967 |
+
|
| 968 |
+
To support Python 2 and 3 with a single code base, define a __str__ method
|
| 969 |
+
returning text and apply this decorator to the class.
|
| 970 |
+
"""
|
| 971 |
+
if PY2:
|
| 972 |
+
if '__str__' not in klass.__dict__:
|
| 973 |
+
raise ValueError("@python_2_unicode_compatible cannot be applied "
|
| 974 |
+
"to %s because it doesn't define __str__()." %
|
| 975 |
+
klass.__name__)
|
| 976 |
+
klass.__unicode__ = klass.__str__
|
| 977 |
+
klass.__str__ = lambda self: self.__unicode__().encode('utf-8')
|
| 978 |
+
return klass
|
| 979 |
+
|
| 980 |
+
|
| 981 |
+
# Complete the moves implementation.
|
| 982 |
+
# This code is at the end of this module to speed up module loading.
|
| 983 |
+
# Turn this module into a package.
|
| 984 |
+
__path__ = [] # required for PEP 302 and PEP 451
|
| 985 |
+
__package__ = __name__ # see PEP 366 @ReservedAssignment
|
| 986 |
+
if globals().get("__spec__") is not None:
|
| 987 |
+
__spec__.submodule_search_locations = [] # PEP 451 @UndefinedVariable
|
| 988 |
+
# Remove other six meta path importers, since they cause problems. This can
|
| 989 |
+
# happen if six is removed from sys.modules and then reloaded. (Setuptools does
|
| 990 |
+
# this for some reason.)
|
| 991 |
+
if sys.meta_path:
|
| 992 |
+
for i, importer in enumerate(sys.meta_path):
|
| 993 |
+
# Here's some real nastiness: Another "instance" of the six module might
|
| 994 |
+
# be floating around. Therefore, we can't use isinstance() to check for
|
| 995 |
+
# the six meta path importer, since the other six instance will have
|
| 996 |
+
# inserted an importer with different class.
|
| 997 |
+
if (type(importer).__name__ == "_SixMetaPathImporter" and
|
| 998 |
+
importer.name == __name__):
|
| 999 |
+
del sys.meta_path[i]
|
| 1000 |
+
break
|
| 1001 |
+
del i, importer
|
| 1002 |
+
# Finally, add the importer to the meta path import hook.
|
| 1003 |
+
sys.meta_path.append(_importer)
|