├── .github └── disabled-workflows │ └── branch-sync.yml ├── .gitignore ├── .swift-format ├── BRANCHES.md ├── CODE_OF_CONDUCT.md ├── Contributing.md ├── FAQ.md ├── Installation.md ├── KNOWN_ISSUES.md ├── LICENSE ├── Package.swift ├── README.md ├── RELEASES.md ├── Sources └── SIL │ ├── Bitcode.swift │ ├── BitcodeParser.swift │ ├── BitcodePrinter.swift │ ├── Bitstream.swift │ ├── Parser.swift │ ├── Printer.swift │ ├── SExpr.swift │ ├── SIL.swift │ ├── SILAnalysis.swift │ ├── SILParser.swift │ └── SILPrinter.swift ├── Tests ├── LinuxMain.swift └── SILTests │ ├── BitcodeTests.swift │ ├── BitsTests.swift │ ├── BitstreamTests.swift │ ├── DescriptionTests.swift │ ├── InstructionTests.swift │ ├── ModuleTests.swift │ ├── PrinterTests.swift │ ├── Process.swift │ ├── Resources │ ├── AddFloat.sib │ ├── AddFloat.swift │ ├── AvgPool1D.sil │ ├── AvgPool1D.swift │ └── Example.sexpr │ ├── SExprTests.swift │ ├── SILAnalysisTests.swift │ ├── SILParserTests.swift │ └── TemporaryFile.swift ├── Usage.md ├── docs ├── AutomaticDifferentiation.md ├── DesignOverview.md ├── DifferentiableFunctions.md ├── DifferentiableTypes.md ├── DynamicPropertyIteration.md ├── GraphProgramExtraction.md ├── ParameterOptimization.md ├── PythonInteroperability.md ├── README.md ├── SupportedBackends.md ├── WhySwiftForTensorFlow.md ├── images │ ├── AutomaticDifferentiation-Approaches.png │ ├── AutomaticDifferentiation-Compiler.png │ ├── AutomaticDifferentiation-ReverseAD.png │ ├── DesignOverview-AD.png │ ├── DesignOverview-Pipeline.png │ ├── GraphProgramExtraction-Graph.png │ ├── Installation-XcodePreferences.png │ ├── Usage-Playground.png │ ├── Usage-macOSCatalinaHardenedRuntime.png │ ├── VSCodeSwiftDebug.png │ └── VSCodeSwiftOutline.png └── site │ ├── _book.yaml │ ├── guide │ ├── backends.md │ ├── checkpoints.md │ ├── datasets.md │ ├── debugging_x10.md │ ├── layers.md │ ├── model_summary.md │ ├── overview.md │ ├── python_interoperability.md │ ├── tensors.md │ └── training_loop.md │ └── tutorials │ ├── .gitignore │ ├── Swift_autodiff_sharp_edges.ipynb │ ├── a_swift_tour.ipynb │ ├── custom_differentiation.ipynb │ ├── introducing_x10.ipynb │ ├── model_training_walkthrough.ipynb │ ├── protocol_oriented_generics.ipynb │ ├── python_interoperability.ipynb │ └── raw_tensorflow_operators.ipynb ├── images └── logo.png ├── notebooks ├── blank_swift.ipynb ├── blank_swift_gpu.ipynb ├── blank_swift_tpu.ipynb ├── talk_demos │ ├── Differentiable_Physics.ipynb │ └── Differentiable_Physics_Backup.ipynb └── value_semantics │ ├── 01_value_semantics_in_swift.ipynb │ └── 02_value_semantics_and_autodiff.ipynb ├── proposals ├── LayerProtocolRevision.md ├── OneGraphFunctionPerHostFunction.md ├── README.md └── modern-layer-api │ ├── LayerApiDesignSpace.md │ └── LayerApiPrototypes.md └── utils ├── base-deps-docker ├── Dockerfile └── build.sh └── install-ubuntu1804.sh /.github/disabled-workflows/branch-sync.yml: -------------------------------------------------------------------------------- 1 | name: main branch sync 2 | 3 | on: 4 | push: 5 | branches: 6 | - 'main' 7 | 8 | jobs: 9 | branch_sync: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Checkout Code 13 | uses: actions/checkout@v2 14 | with: 15 | fetch-depth: 0 16 | 17 | - name: Update branch 18 | run: | 19 | git push https://${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }} HEAD:master 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ### SwiftPM ### 2 | Packages 3 | .build/ 4 | xcuserdata 5 | DerivedData/ 6 | *.xcodeproj 7 | *~ 8 | *.vscode 9 | *.idea 10 | 11 | ### MacOS ### 12 | .DS_Store 13 | -------------------------------------------------------------------------------- /.swift-format: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "lineLength": 100, 4 | "indentation": { 5 | "spaces": 4 6 | }, 7 | "maximumBlankLines": 1, 8 | "respectsExistingLineBreaks": true, 9 | "blankLineBetweenMembers": { 10 | "ignoreSingleLineProperties": true 11 | }, 12 | "lineBreakBeforeControlFlowKeywords": false, 13 | "lineBreakBeforeEachArgument": false 14 | } 15 | -------------------------------------------------------------------------------- /BRANCHES.md: -------------------------------------------------------------------------------- 1 | Notebooks on the `main` branch should be compatible with the toolchain that is 2 | released in Colab. 3 | 4 | Make incompatible changes to notebooks in the `nightly-notebooks` branch. We 5 | promote `nightly-notebooks` to `main` when we deploy new toolchains to Colab. 6 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # TensorFlow Code of Conduct 2 | 3 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 4 | 5 | 6 | ## Our Standards 7 | 8 | Examples of behavior that contributes to creating a positive environment include: 9 | 10 | * Using welcoming and inclusive language 11 | * Being respectful of differing viewpoints and experiences 12 | * Gracefully accepting constructive criticism 13 | * Focusing on what is best for the community 14 | * Showing empathy towards other community members 15 | 16 | Examples of unacceptable behavior by participants include: 17 | 18 | * The use of sexualized language or imagery and unwelcome sexual attention or advances 19 | * Trolling, insulting/derogatory comments, and personal or political attacks 20 | * Public or private harassment 21 | * Publishing others' private information, such as a physical or electronic address, without explicit permission 22 | * Conduct which could reasonably be considered inappropriate for the forum in which it occurs. 23 | 24 | All TensorFlow forums and spaces are meant for professional interactions, and any behavior which could reasonably be considered inappropriate in a professional setting is unacceptable. 25 | 26 | 27 | ## Our Responsibilities 28 | 29 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 30 | 31 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 32 | 33 | 34 | ## Scope 35 | 36 | This Code of Conduct applies to all content on tensorflow.org, TensorFlow’s GitHub organization, or any other official TensorFlow web presence allowing for community interactions, as well as at all official TensorFlow events, whether offline or online. 37 | 38 | The Code of Conduct also applies within project spaces and in public spaces whenever an individual is representing TensorFlow or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed or de facto representative at an online or offline event. 39 | 40 | 41 | ## Conflict Resolution 42 | 43 | Conflicts in an open source project can take many forms, from someone having a bad day and using harsh and hurtful language in the issue queue, to more serious instances such as sexist/racist statements or threats of violence, and everything in between. 44 | 45 | If the behavior is threatening or harassing, or for other reasons requires immediate escalation, please see below. 46 | 47 | However, for the vast majority of issues, we aim to empower individuals to first resolve conflicts themselves, asking for help when needed, and only after that fails to escalate further. This approach gives people more control over the outcome of their dispute. 48 | 49 | If you are experiencing or witnessing conflict, we ask you to use the following escalation strategy to address the conflict: 50 | 51 | 1. Address the perceived conflict directly with those involved, preferably in a real-time medium. 52 | 2. If this fails, get a third party (e.g. a mutual friend, and/or someone with background on the issue, but not involved in conflict) to intercede. 53 | 3. If you are still unable to resolve the conflict, and you believe it rises to harassment or another code of conduct violation, report it. 54 | 55 | 56 | ## Reporting Violations 57 | 58 | Violations of the Code of Conduct can be reported to TensorFlow’s Project Stewards, Edd Wilder-James (ewj@google.com) and Sarah Novotny (sarahnovotny@google.com). The Project Steward will determine whether the Code of Conduct was violated, and will issue an appropriate sanction, possibly including a written warning or expulsion from the project, project sponsored spaces, or project forums. We ask that you make a good-faith effort to resolve your conflict via the conflict resolution policy before submitting a report. 59 | 60 | Violations of the Code of Conduct can occur in any setting, even those unrelated to the project. We will only consider complaints about conduct that has occurred within one year of the report. 61 | 62 | 63 | ## Enforcement 64 | 65 | If the Project Stewards receive a report alleging a violation of the Code of Conduct, the Project Stewards will notify the accused of the report, and provide them an opportunity to discuss the report before a sanction is issued. The Project Stewards will do their utmost to keep the reporter anonymous. If the act is ongoing (such as someone engaging in harassment), or involves a threat to anyone's safety (e.g. threats of violence), the Project Stewards may issue sanctions without notice. 66 | 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the Contributor Covenant, version 1.4, available at https://contributor-covenant.org/version/1/4, and includes some aspects of the Geek Feminism Code of Conduct and the Drupal Code of Conduct. 71 | -------------------------------------------------------------------------------- /Contributing.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Everyone is welcome to contribute to Swift for TensorFlow. Contributing doesn't just mean submitting code - there are many different ways to get involved, including reporting bugs, improving documentation, writing models or tutorials, submitting deep learning building blocks, or participating in API and infrastructure design discussions. 4 | 5 | The Swift for TensorFlow community is guided by our [Code of Conduct](https://github.com/tensorflow/swift/blob/main/CODE_OF_CONDUCT.md), which we encourage everybody to read before participating. 6 | 7 | ## Report bugs 8 | 9 | Reporting bugs is a great way for anyone to help improve Swift for TensorFlow. Swift for TensorFlow has a JIRA project on the [bugs.swift.org](https://bugs.swift.org) JIRA instance. To report a bug, [use this issue template](https://bugs.swift.org/secure/CreateIssue.jspa?issuetype=10006&pid=10100). 10 | 11 | Please follow the [Swift project's bug reporting guidelines](https://swift.org/contributing/#reporting-bugs) while reporting bugs. 12 | 13 | ## Improve documentation 14 | 15 | Improving documentation is another great way for anyone to contribute to Swift for TensorFlow. Documentation is located in a few different places: 16 | 17 | * Tutorials and design documents are located at https://github.com/tensorflow/swift. 18 | * API documentation is located at https://www.tensorflow.org/swift/api_docs. Documentation is generated from source code comments in the [TensorFlow standard library](https://github.com/apple/swift/tree/tensorflow/stdlib/public/TensorFlow) and the [deep learning library](https://github.com/tensorflow/swift-apis). 19 | 20 | For small documentation improvements, feel free to send a PR directly to the relevant repository. For bigger changes, you might want to file a JIRA issue or ask on the mailing list before starting, as described in the [code contribution workflow](#code-contribution-workflow). 21 | 22 | ## Starter bugs 23 | 24 | If you are interested in contributing code, but are not sure how to get started, take a look at the [Swift for TensorFlow Starter Bugs](https://bugs.swift.org/issues/?filter=11323). It's a curated list of small, self-contained bugs that are great for diving in and getting a sense of how everything works. If you have any questions about these bugs, feel free to ask in a comment on JIRA or on the [mailing list](https://groups.google.com/a/tensorflow.org/forum/#!forum/swift)! 25 | 26 | Once you are ready to start working on a starter issue, assign it to yourself in JIRA and follow the [code contribution workflow](#code-contribution-workflow). 27 | 28 | ## Add deep learning building blocks 29 | 30 | The [Swift for TensorFlow Deep Learning Library](https://github.com/tensorflow/swift-apis) contains building blocks for deep learning, like layers, loss functions, and optimizers. It's very new, so it's some of standard building blocks. We welcome contributions! 31 | 32 | Follow the [code contribution workflow](#code-contribution-workflow) when contributing building blocks. 33 | 34 | ## Participate in design discussions 35 | 36 | We discuss preliminary feature requests and ideas on the [mailing list](https://groups.google.com/a/tensorflow.org/forum/#!forum/swift). You can participate by sending your own feature requests and ideas to the mailing list, or by commenting on others' feature requests and ideas. 37 | 38 | Once an idea has been fleshed out, the person or people driving it write a proposal and send it as a PR to the [proposals directory](https://github.com/tensorflow/swift/tree/main/proposals). Further discussion happens on that PR, and the PR gets merged if the design gets accepted. You can participate by proposing your own proposals, or by commenting on others' proposals. 39 | 40 | 41 | ## Code contribution workflow 42 | 43 | Before contributing code, make sure you know how to compile and test the repository that you want to contribute to: 44 | 45 | * Swift for TensorFlow Deep Learning Library: [development instructions](https://github.com/tensorflow/swift-apis#development) 46 | * Swift for TensorFlow Compiler: [development instructions](https://github.com/apple/swift/tree/tensorflow#building-swift-for-tensorflow) 47 | 48 | Here is the standard workflow for contributing code to Swift for TensorFlow: 49 | 50 | 1. Coordinate with the community to make sure you're not duplicating work or building something that conflicts with something else. There are a few ways to do this, depending on the size and scope of the change: 51 | - For tiny changes (e.g. fixing typos, clarifying documentation), you can skip this step and jump straight to sending a PR. 52 | - For straightforward changes (e.g. fixing known bugs, adding deep learning building blocks, or implementing features that have already been designed), find an issue in the [Swift for TensorFlow JIRA project](https://bugs.swift.org/projects/TF/issues) and assign it to yourself. This lets others know that you are working on the change, so that they don't waste effort working on the same change. If there is no issue for your change, [file one](#report-bugs). If you decide not to work on a change, please unassign the issue from yourself to give others a chance to work on it. 53 | - For large changes, or changes that affect others (e.g. adding a new feature, or changing an existing API), start a discussion on the [mailing list](https://groups.google.com/a/tensorflow.org/forum/#!forum/swift) before you start working on it. If the change has a large design space, we might recommend having a [design discussion](#participate-in-design-discussions). 54 | 2. Start working on your code, and send a PR to the relevant repository when it's ready for review. Take a look at the [Swift project code contribution guidelines](https://swift.org/contributing/#contributing-code) for tips on how to structure your code and PRs. 55 | 3. A reviewer will take a look at your PR and might ask for some changes. This is an iterative process that continues until the code is ready to be merged. 56 | 4. Once the code is ready to be merged, someone with commit access to the repository will merge it. 57 | 5. Remember to close any JIRA issues related to the change. 58 | -------------------------------------------------------------------------------- /FAQ.md: -------------------------------------------------------------------------------- 1 | # Frequently Asked Questions 2 | 3 | This is a list of questions we frequently get and problems that are often encountered. 4 | Because this project is still in development, we have missing pieces that are commonly 5 | encountered and prefer not to get new issues filed in our bug tracker. 6 | 7 | * [Why Swift?](#why-swift) 8 | * [How can I use Python 3 with the Python module?](#how-can-i-use-python-3-with-the-python-module) 9 | * [\[Mac\] I wrote some code in an Xcode Playground. Why is it frozen/hanging?](https://github.com/tensorflow/swift/blob/main/FAQ.md#mac-i-wrote-some-code-in-an-xcode-playground-why-is-it-frozenhanging) 10 | 11 | ## Why Swift? 12 | 13 | The short answer is that our decision was driven by the needs of the core [Graph Program 14 | Extraction](docs/GraphProgramExtraction.md) compiler transformation that started the whole 15 | project. We have a long document that explains all of this rationale in depth named "[Why 16 | *Swift* for TensorFlow?](docs/WhySwiftForTensorFlow.md)". 17 | 18 | Separate from that, Swift really is a great fit for our purposes, and a very nice language. 19 | 20 | ## How can I use Python 3 with the `Python` module? 21 | 22 | By default, Swift will use the highest version of Python available on your system. 23 | You can dynamically switch Python versions by calling: 24 | 25 | ```swift 26 | PythonLibrary.useVersion(2, 7) 27 | ``` 28 | 29 | On macOS, Python 2.7 comes pre-installed and cannot be modified without disabling SIP. 30 | You can, however, install Python 3 from [Python.org](https://docs.python.org/3/using/mac.html). 31 | 32 | ## [Mac] I wrote some code in an Xcode Playground. Why is it frozen/hanging? 33 | 34 | Xcode Playgrounds are known to be somewhat unstable, unfortunately. 35 | If your Playground appears to hang, please try restarting Xcode or creating a new Playground. 36 | -------------------------------------------------------------------------------- /KNOWN_ISSUES.md: -------------------------------------------------------------------------------- 1 | # Swift for TensorFlow Known Issues 2 | 3 | This is a curated list of Swift for TensorFlow known issues and missing 4 | features. With every release, new issues are added and resolved issues are 5 | updated. 6 | 7 | Legend: 8 | * Notable issues are marked in **bold**. 9 | * Issues under active development are marked with 🚧. 10 | 11 | Please see the [JIRA issue tracker](https://bugs.swift.org/projects/TF/issues) 12 | for a full list of known issues. 13 | 14 | ## Version 0.4 15 | 16 | ### Automatic Differentiation 17 | * [ ] Marking stored properties as `@differentiable` may result in a crash. 18 | ([TF-521]) 19 | * [ ] Marking an intializer as `@differentiable` may result in a SIL 20 | verification error: 21 | `operand of 'apply' doesn't match function input type`. ([TF-526]) 22 | * [ ] Defining custom derivatives for `@_alwaysEmitIntoClient` declarations 23 | results in a crash. ([TF-545]) 24 | * [ ] Differentiating struct initializers may result in a SIL verification 25 | error: `substituted callee type does not match substitutions`. ([TF-546]) 26 | 27 | ## Version 0.3 28 | 29 | ### TensorFlow Library 30 | 31 | * [ ] `Tensor` advanced indexing and striding are not supported on GPU. 32 | 33 | ## Version 0.2 34 | 35 | ### Notebook Environments (Colab and Jupyter) 36 | 37 | * [ ] When a runtime error occurs or when you interrupt cell execution, 38 | resources (e.g. host memory, GPU memory) do not get released. This can 39 | lead to OOMs. ([TF-338]) 40 | * Workaround: Restart the runtime (`Runtime > Restart Runtime` in the Colab 41 | menu bar) to release all the resources. 42 | * [ ] If the last statement on a cell evaluates to a struct that was defined in 43 | the notebook, then you get an error ("use of undeclared type") instead of 44 | seeing the value of the statement. ([TF-125]) 45 | * Workaround: Wrap the last statement in `print()`. 46 | * [ ] Using extensions to conform a type to a protocol (e.g. `extension MyType: 47 | MyProtocol { ... }`), often causes duplicate conformance errors. 48 | ([TF-162]) 49 | * Workaround: Add the conformance in the same cell where the type is defined. 50 | * [ ] If a cell that declares a type executes twice, then it creates two 51 | different types with the same name. Mixing these types can lead to 52 | confusing error messages like `cannot convert value of type 'MyType' to 53 | expected argument type 'MyType'`. ([TF-156]) 54 | * Workaround: Re-run all cells that use the declared type, so that they use the 55 | new type. 56 | * [ ] The autocomplete UI should show types and documentation. It should 57 | position your cursor in the first argument when you complete a function 58 | call. 59 | 60 | ### Swift Standard Library Enhancements 61 | 62 | * [ ] The [`Differentiable`] protocol's `allDifferentiableVariables` requirement 63 | should not have a setter. Do not use this directly through a generic type 64 | with a `Differentiable` conformance constraint. ([TF-208]) 65 | 66 | ### TensorFlow Library 67 | 68 | * [ ] 🚧 **Model checkpointing and serialization APIs are missing.** ([TF-388]) 69 | * [ ] **TensorFlow runtime errors (e.g. shape mismatch errors) do not show useful 70 | source location information or useful stack traces.** ([TF-458]) 71 | 72 | ### TensorFlow Runtime 73 | 74 | * [ ] If you are using a CUDA build and you have an NVIDIA GPU with a compute 75 | capability other than 3.5 or 7.0, then you will experience a ~10 minute 76 | delay the first time you execute a TensorFlow operation, while TensorFlow 77 | compiles kernels for your GPU's compute capability. The program will not 78 | print anything out and it will appear to be frozen. ([TF-461]) 79 | * [ ] There is a long latency when allocating a `Tensor` for the first time on 80 | GPUs. Subsequent allocations are normal speed. ([TF-460]) 81 | * [ ] **TensorFlow runtime errors (e.g. shape mismatch errors) do not show 82 | useful source location information or useful stack traces.** ([TF-458]) 83 | 84 | ### Swift for TensorFlow Deep Learning Library 85 | 86 | * [ ] Many Keras layers remain to be implemented, help wanted! ([swift-apis#54]) 87 | * [ ] Parameter sharing APIs (e.g. using the same `Tensor` weights in multiple 88 | layers) are missing. 89 | * [ ] The [`Parameter`] class does not yet conform to `Differentiable`, and is 90 | not recommended for general use. 91 | * [ ] The compiler errors displayed when a user-defined layer struct fails to 92 | fully satisfy the requirements of the `Layer` protocol are unclear. 93 | 94 | ### Automatic Differentiation 95 | 96 | * [ ] 🚧 **Differentiation does not yet support functions with control flow.** 97 | ([TF-354]) 98 | * Conditionals are supported since 0.4 ([apple/swift#25057]). 99 | * [ ] 🚧 **Higher-order differentiation is not yet supported.** 100 | * [ ] Differentiating functions with respect to an `inout` parameter is not yet 101 | supported. ([TF-357]) 102 | * [ ] The compiler will only synthesize conformance requirements for 103 | `Differentiable` in `struct` types. ([TF-37]) 104 | * [ ] The `@differentiable` attribute incorrectly passes type-checking in some 105 | cases, when an error should be produced. This leads to compiler crashes. 106 | ([TF-449]) 107 | * [x] ~~The `@differentiating` attribute leads to a compiler crash when the 108 | derivative function is defined in a generic context that is more 109 | constrained than the original function's generic context. ([TF-358])~~ 110 | * **Resolved (0.3).** The `@differentiating` attribute can register 111 | derivatives with a generic context that is more constrained than the 112 | original function's generic context. 113 | * [ ] Referring to a `@differentiable` function using key paths leads to a 114 | compiler crash. ([TF-123]) 115 | 116 | ### Python Interoperability 117 | 118 | * [ ] Python runtime errors do not show useful source location information. 119 | The Python call stack should be shown. ([TF-150]) 120 | * [ ] When an argument to a Python function cannot be converted to a Python 121 | object, the compiler wrongly claims that the function is of non-function 122 | type rather than pointing out that the argument doesn't conform to 123 | `PythonConvertible`. ([TF-220]) 124 | * [ ] Python TensorFlow cannot be imported because of various issues (binary 125 | incompatibility, symbol conflicts). 126 | 127 | [`Differentiable`]: https://www.tensorflow.org/swift/api_docs/Protocols/Differentiable 128 | [`Parameter`]: https://www.tensorflow.org/swift/api_docs/Classes/Parameter 129 | 130 | [swift-apis#54]: https://github.com/tensorflow/swift-apis/issues/54 131 | [apple/swift#25057]: https://github.com/apple/swift/pull/25057 132 | 133 | [TF-37]: https://bugs.swift.org/browse/TF-37 134 | [TF-123]: https://bugs.swift.org/browse/TF-123 135 | [TF-125]: https://bugs.swift.org/browse/TF-125 136 | [TF-150]: https://bugs.swift.org/browse/TF-150 137 | [TF-156]: https://bugs.swift.org/browse/TF-156 138 | [TF-162]: https://bugs.swift.org/browse/TF-162 139 | [TF-208]: https://bugs.swift.org/browse/TF-208 140 | [TF-220]: https://bugs.swift.org/browse/TF-220 141 | [TF-338]: https://bugs.swift.org/browse/TF-338 142 | [TF-356]: https://bugs.swift.org/browse/TF-356 143 | [TF-357]: https://bugs.swift.org/browse/TF-357 144 | [TF-358]: https://bugs.swift.org/browse/TF-357 145 | [TF-384]: https://bugs.swift.org/browse/TF-384 146 | [TF-388]: https://bugs.swift.org/browse/TF-388 147 | [TF-449]: https://bugs.swift.org/browse/TF-449 148 | [TF-458]: https://bugs.swift.org/browse/TF-458 149 | [TF-459]: https://bugs.swift.org/browse/TF-459 150 | [TF-460]: https://bugs.swift.org/browse/TF-460 151 | [TF-461]: https://bugs.swift.org/browse/TF-461 152 | [TF-521]: https://bugs.swift.org/browse/TF-521 153 | [TF-526]: https://bugs.swift.org/browse/TF-526 154 | [TF-545]: https://bugs.swift.org/browse/TF-545 155 | [TF-546]: https://bugs.swift.org/browse/TF-546 156 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:4.2 2 | // The swift-tools-version declares the minimum version of Swift required to build this package. 3 | import PackageDescription 4 | 5 | let package = Package( 6 | name: "Metaprogramming", 7 | products: [ 8 | .library( 9 | name: "SIL", 10 | type: .dynamic, 11 | targets: ["SIL"]), 12 | ], 13 | dependencies: [], 14 | targets: [ 15 | .target( 16 | name: "SIL", 17 | dependencies: []), 18 | .testTarget( 19 | name: "SILTests", 20 | dependencies: ["SIL"], 21 | path: "Tests/SILTests"), 22 | ] 23 | ) 24 | -------------------------------------------------------------------------------- /RELEASES.md: -------------------------------------------------------------------------------- 1 | # Swift for TensorFlow Release Notes 2 | 3 | ## Version 0.3 4 | 5 | ### Overview 6 | 7 | This is the second public release of Swift for TensorFlow, available across 8 | Google Colaboratory, Linux, and macOS. The focus is improving overall stability 9 | and refining APIs. 10 | 11 | ### Notebook Environments (Colab and Jupyter) 12 | 13 | * Install SwiftPM packages using `%install` directives. See [documentation in 14 | README](https://github.com/google/swift-jupyter#install-directives). 15 | ([swift-jupyter#45](https://github.com/google/swift-jupyter/pull/45), 16 | [swift-jupyter#48](https://github.com/google/swift-jupyter/pull/48), 17 | [swift-jupyter#52](https://github.com/google/swift-jupyter/pull/52)) 18 | * `swift-jupyter` can now be installed in a Conda environment. See 19 | [documentation in 20 | README](https://github.com/google/swift-jupyter#option-2-using-a-swift-for-tensorflow-toolchain-and-conda). 21 | 22 | ### Swift Standard Library Enhancements 23 | 24 | * `AnyDerivative` has been added, representing a type-erased derivative. 25 | ([apple/swift#23521](https://github.com/apple/swift/pull/23521)) 26 | 27 | ### TensorFlow Library 28 | 29 | * `Tensor` now supports advanced indexing and striding APIs. 30 | ([apple/swift#24684](https://github.com/apple/swift/pull/23684)) 31 | * `Tensor`s are now pretty-printed, based on the format of NumPy. 32 | ([apple/swift#23837](https://github.com/apple/swift/pull/23837)) 33 | * TensorFlow APIs involving shape dimensions, indices, and sizes now use `Int` 34 | instead of `Int32`. 35 | ([apple/swift#24012](https://github.com/apple/swift/pull/24012), 36 | [apple/swift#24110](https://github.com/apple/swift/pull/24110)) 37 | * Additional raw TensorFlow operator are now supported. 38 | ([apple/swift#23777](https://github.com/apple/swift/pull/23777), 39 | [apple/swift#24096](https://github.com/apple/swift/pull/24096), 40 | [apple/swift#24120](https://github.com/apple/swift/pull/24120)) 41 | * `SaveV2` (`Raw.saveV2(prefix:tensorNames:shapeAndSlices:tensors:)`) 42 | * `RestoreV2` (`Raw.restoreV2(prefix:tensorNames:shapeAndSlices:dtypes:)`) 43 | * `Split` (`Raw.split(splitDim:value:numSplit:)`) 44 | * `SplitV` (`Raw.splitV(value:sizeSplits:splitDim:numSplit:)`) 45 | * Experimental APIs have been added to group tensor ops into specialized tensor 46 | functions for further optimization, optionally using XLA compilation. 47 | ([apple/swift#23868](https://github.com/apple/swift/pull/23868)) 48 | 49 | ### Swift for TensorFlow Deep Learning Library 50 | 51 | * The `Layer` protocol's `applied(to:in:)` method has been renamed to `call(_:)`. 52 | `Layer`s are now "callable" like functions, e.g. `layer(input)`. 53 | * **Note: this is experimental functionality that is currently [being proposed 54 | through Swift 55 | Evolution](https://github.com/apple/swift-evolution/blob/main/proposals/0253-callable.md).** 56 | Expect potential changes. 57 | * The `context` argument has been removed from `Layer`'s `applied(to:)` method. 58 | Instead, contexts are now thread-local. ([swift-apis#87](https://github.com/tensorflow/swift-apis/pull/87)) 59 | * Use `Context.local` to access the current thread-local context. 60 | * Note: layers like `BatchNorm` and `Dropout` check `Context.local` to 61 | determine whether the current learning phase is training or inference. **Be 62 | sure to set the context learning phase to `.training` before running a 63 | training loop.** 64 | * Use `withContext(_:_:)` and `withLearningPhase(_:_:)` to call a closure 65 | under a temporary context or learning phase, respectively. 66 | * A `RNNCell` protocol has been added, generalizing simple RNNs, LSTMs, and 67 | GRUs. ([swift-apis#80](https://github.com/tensorflow/swift-apis/pull/80), 68 | [swift-apis#86](https://github.com/tensorflow/swift-apis/pull/86)) 69 | * New layers have been added. 70 | * `Conv1D`, `MaxPool1D`, `AvgPool1D`. 71 | ([swift-apis#57](https://github.com/tensorflow/swift-apis/pull/57)) 72 | * `UpSampling1D`. 73 | ([swift-apis#61](https://github.com/tensorflow/swift-apis/pull/61)) 74 | * `TransposedConv2D`. 75 | ([swift-apis#64](https://github.com/tensorflow/swift-apis/pull/64)) 76 | * `GlobalAveragePooling1D`, `GlobalAveragePooling2D`, 77 | `GlobalAveragePooling3D`. 78 | ([swift-apis#66](https://github.com/tensorflow/swift-apis/pull/66), 79 | [swift-apis#65](https://github.com/tensorflow/swift-apis/pull/65), 80 | [swift-apis#72](https://github.com/tensorflow/swift-apis/pull/72)) 81 | * Optimizer stored properties (e.g. `learningRate`) are now mutable. 82 | ([swift-apis#81](https://github.com/tensorflow/swift-apis/pull/81)) 83 | 84 | ### Automatic Differentiation 85 | 86 | * `Array` now conforms to `Differentiable`. 87 | ([apple/swift#23183](https://github.com/apple/swift/pull/23183)) 88 | * The `@differentiating` attribute now works when the derivative function has a 89 | generic context that is more constrained than the original function's generic 90 | context. ([apple/swift#23384](https://github.com/apple/swift/pull/23384)) 91 | * The `@differentiating` attribute now accepts a `wrt` differentiation parameter 92 | list, just like the `@differentiable` attribute. 93 | ([apple/swift#23370](https://github.com/apple/swift/pull/23370)) 94 | * The error `function is differentiable only with respect to a smaller subset of 95 | arguments` is now obsolete. 96 | ([apple/swift#23887](https://github.com/apple/swift/pull/23887)) 97 | * A differentiation-related memory leak has been fixed. 98 | ([apple/swift#24165](https://github.com/apple/swift/pull/24165)) 99 | 100 | ### Acknowledgements 101 | 102 | This release contains contributions from many people at Google, as well as: 103 | 104 | Anthony Platanios, Bart Chrzaszcz, Bastian Müller, Brett Koonce, Dante Broggi, 105 | Dave Fernandes, Doug Friedman, Ken Wigginton Jr, Jeremy Howard, John Pope, Leo 106 | Zhao, Nanjiang Jiang, Pawan Sasanka Ammanamanchi, Pedro Cuenca, Pedro José 107 | Pereira Vieito, Sendil Kumar N, Sylvain Gugger, Tanmay Bakshi, Valeriy Van, 108 | Victor Guerra, Volodymyr Pavliukevych, Vova Manannikov, Wayne Nixalo. 109 | 110 | ## Version 0.2 111 | 112 | ### Overview 113 | 114 | This is the first public release of Swift for TensorFlow, available across 115 | Google Colaboratory, Linux, and macOS. The focus is building the basic technology 116 | platform and fundamental deep learning APIs. 117 | 118 | This release includes the core Swift for TensorFlow compiler, the standard 119 | libraries, and the [Swift for TensorFlow Deep Learning 120 | Library](https://github.com/tensorflow/swift-apis). Core functionality includes: 121 | the ability to define, train and evaluate models, a notebook environment, and 122 | natural Python interoperability. 123 | 124 | ### Notebook Environments (Colab and Jupyter) 125 | 126 | * Hit "Tab" to trigger basic semantic autocomplete. 127 | * [Use matplotlib to produce inline 128 | graphs.](https://github.com/google/swift-jupyter/blob/main/README.md#rich-output) 129 | * Interrupt cell execution by clicking the "stop" button next to the cell. 130 | 131 | ### Swift Standard Library Enhancements 132 | 133 | * Declare a 134 | [`KeyPathIterable`](https://www.tensorflow.org/swift/api_docs/Protocols/KeyPathIterable) 135 | protocol conformance to make your custom type provide a collection of key 136 | paths to stored properties. Read [Dynamic Property Iteration using Key 137 | Paths](https://github.com/tensorflow/swift/blob/main/docs/DynamicPropertyIteration.md) 138 | for a deep dive into the design. 139 | * Declare an 140 | [`AdditiveArithmetic`](https://www.tensorflow.org/swift/api_docs/Protocols/KeyPathIterable) 141 | protocol conformance to make values of your custom type behave like an 142 | [additive group](https://en.wikipedia.org/wiki/Additive_group). If the 143 | declaration is in the same file as the type definition and when all stored 144 | properties conform to `AdditiveArithmetic`, the compiler will synthesize the 145 | conformance automatically. 146 | * Declare an 147 | [`VectorNumeric`](https://www.tensorflow.org/swift/api_docs/Protocols/KeyPathIterable) 148 | protocol conformance to make values of your custom type behave like a [vector 149 | space](https://en.wikipedia.org/wiki/Vector_space). If the declaration is in 150 | the same file as the type definition and when all stored properties conform to 151 | `VectorNumeric` with the same `Scalar` associated type, the compiler will 152 | synthesize the conformance automatically. 153 | 154 | ### Swift for TensorFlow Deep Learning Library 155 | 156 | * The [`Layer`](https://www.tensorflow.org/swift/api_docs/Protocols/Layer) 157 | protocol and [layers](https://www.tensorflow.org/swift/api_docs/Structs/Dense) 158 | built on top of it. 159 | * The 160 | [`Optimizer`](https://www.tensorflow.org/swift/api_docs/Protocols/Optimizer) 161 | protocol and 162 | [optimizers](https://www.tensorflow.org/swift/api_docs/Classes/SGD) built on 163 | top of it. 164 | * [Philox](https://www.tensorflow.org/swift/api_docs/Structs/PhiloxRandomNumberGenerator) 165 | and 166 | [Threefry](https://www.tensorflow.org/swift/api_docs/Structs/ThreefryRandomNumberGenerator) 167 | random number generators and [generic random 168 | distributions](https://www.tensorflow.org/swift/api_docs/Structs/BetaDistribution). 169 | * Sequential layer application utilities: 170 | [`sequenced(in:through:_:)`](https://www.tensorflow.org/swift/api_docs/Protocols/Differentiable#/s:10TensorFlow14DifferentiablePAAE9sequenced2in7through_6OutputQyd_0_AA7ContextC_qd__qd_0_t5InputQyd__RszAA5LayerRd__AaMRd_0_AKQyd_0_AGRtd__r0_lF) 171 | and its n-ary overloads. 172 | 173 | ### Automatic Differentiation 174 | 175 | * Declare a conformance to the 176 | [`Differentiable`](https://www.tensorflow.org/swift/api_docs/Protocols/Differentiable) 177 | protocol to make a custom type work with automatic differentiation. For a 178 | technical deep dive, read [Differentiable 179 | Types](https://github.com/tensorflow/swift/blob/main/docs/DifferentiableTypes.md). 180 | * Use 181 | [`differentiableFunction(from:)`](https://www.tensorflow.org/swift/api_docs/Functions#/s:10TensorFlow22differentiableFunction4fromq0_x_q_tcq0_5value_15CotangentVectorQz_AEQy_tAEQy0_c8pullbacktx_q_tc_tAA14DifferentiableRzAaJR_AaJR0_r1_lF) 182 | to form a `@differentiable` function from a custom derivative function. 183 | * Custom differentiation APIs are available in the standard library. Follow the 184 | [custom differentiation 185 | tutorial](https://colab.research.google.com/github/tensorflow/swift/blob/main/docs/site/tutorials/custom_differentiation.ipynb) 186 | to learn how to use them. 187 | * Gradient checkpointing API: 188 | [`withRecomputationInPullbacks(_:)`](https://www.tensorflow.org/swift/api_docs/Protocols/Differentiable#/s:10TensorFlow14DifferentiablePAAE28withRecomputationInPullbacksyqd__qd__xcAaBRd__lF). 189 | * Gradient surgery API: 190 | [`withGradient(_:)`](https://www.tensorflow.org/swift/api_docs/Protocols/Differentiable#/s:10TensorFlow14DifferentiablePAAE12withGradientyxy15CotangentVectorQzzcF). 191 | 192 | ### Python Interoperability 193 | 194 | * Switch between Python versions using 195 | [`PythonLibrary.useVersion(_:_:)`](https://www.tensorflow.org/swift/api_docs/Structs/PythonLibrary#/s:10TensorFlow13PythonLibraryV10useVersionyySi_SiSgtFZ). 196 | 197 | ### Acknowledgements 198 | 199 | This release contains contributions from many people at Google, as well as: 200 | 201 | Anthony Platanios, Edward Connell, Tanmay Bakshi. 202 | -------------------------------------------------------------------------------- /Sources/SIL/Bitcode.swift: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The TensorFlow Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | struct BitcodeRecord { 16 | var code: Bits 17 | var ops: [BitcodeOperand] 18 | } 19 | 20 | indirect enum OperandKind { 21 | case literal(Bits) 22 | case fixed(width: Int) 23 | case vbr(width: Int) 24 | case array(OperandKind) 25 | case char6 26 | case blob 27 | } 28 | 29 | indirect enum BitcodeOperand { 30 | case bits(Bits) 31 | case blob([UInt8]) 32 | case array([BitcodeOperand]) 33 | 34 | var bits: Bits? { 35 | guard case let .bits(value) = self else { return nil } 36 | return value 37 | } 38 | } 39 | 40 | typealias Structure = [OperandKind] 41 | 42 | class BitcodeBlockInfo { 43 | var id: Bits 44 | var name: String? 45 | var recordNames: [Bits: String] = [:] 46 | var abbreviations: [Bits: Structure] = [:] 47 | 48 | init(id: Bits) { 49 | self.id = id 50 | } 51 | // NB: This copies the structure, because all members are value types 52 | init(from other: BitcodeBlockInfo) { 53 | id = other.id 54 | name = other.name 55 | recordNames = other.recordNames 56 | abbreviations = other.abbreviations 57 | } 58 | } 59 | 60 | class BitcodeBlock { 61 | var info: BitcodeBlockInfo 62 | var records: [BitcodeRecord] = [] 63 | var subblocks: [BitcodeBlock] = [] 64 | let abbrLen: Int 65 | let blockLen32: Int 66 | 67 | convenience init(id: Bits, abbrLen: Int, blockLen32: Int) { 68 | self.init(info: BitcodeBlockInfo(id: id), abbrLen: abbrLen, blockLen32: blockLen32) 69 | } 70 | 71 | init(info: BitcodeBlockInfo, abbrLen: Int, blockLen32: Int) { 72 | self.info = info 73 | self.abbrLen = abbrLen 74 | self.blockLen32 = blockLen32 75 | } 76 | 77 | func name(of record: BitcodeRecord) -> String? { 78 | return info.recordNames[record.code] 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /Sources/SIL/BitcodePrinter.swift: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The TensorFlow Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Output of this printer is heavily inspired by llvm-bcanalyzer -dump. 16 | // It supports most of its syntax, except: 17 | // 1. We don't try to reinterpret arrays as strings 18 | // 2. We don't report abbreviation id used to encode a record 19 | class BitcodePrinter: Printer { 20 | func print(_ bits: Bits) { 21 | if bits.count <= 32 { 22 | print(bits.uint32) 23 | } else { 24 | print("...") 25 | } 26 | } 27 | 28 | private func printBlockName(_ block: BitcodeBlock) { 29 | if let name = block.info.name { 30 | print(name) 31 | } else { 32 | print("blockid=") 33 | print(block.info.id) 34 | } 35 | } 36 | 37 | private func printOperand(_ op: BitcodeOperand, _ i: inout Int) { 38 | switch (op) { 39 | case let .bits(value): 40 | print(" op") 41 | print(i) 42 | print("=") 43 | print(value) 44 | i += 1 45 | case let .array(values): 46 | for v in values { 47 | printOperand(v, &i) 48 | } 49 | default: 50 | break 51 | } 52 | } 53 | 54 | func print(_ record: BitcodeRecord, in block: BitcodeBlock) { 55 | print("<") 56 | if let name = block.info.recordNames[record.code] { 57 | print(name) 58 | } else { 59 | print("code=") 60 | print(record.code) 61 | } 62 | var i: Int = 0 63 | for op in record.ops { 64 | printOperand(op, &i) 65 | } 66 | print("/>") 67 | if case let .some(.blob(value)) = record.ops.last { 68 | print(" blob data = ") 69 | if let asString = String(bytes: value, encoding: .utf8) { 70 | print("'") 71 | print(asString) 72 | print("'") 73 | } else { 74 | print("unprintable, ") 75 | print((value.count + 7) / 8) 76 | print(" bytes.") 77 | } 78 | } 79 | print("\n") 80 | } 81 | 82 | func print(_ block: BitcodeBlock) { 83 | // ID 0 is normally reserved for the info block, but we don't parse it 84 | // as a block anyway, and we use the 0 ID for the main outer block instead 85 | if block.info.id != 0 { 86 | print("<") 87 | printBlockName(block) 88 | print(" NumWords=") 89 | print(block.blockLen32) 90 | print(" BlockCodeSize=") 91 | print(block.abbrLen) 92 | print(">\n") 93 | indent() 94 | } 95 | for record in block.records { 96 | print(record, in: block) 97 | } 98 | for subblock in block.subblocks { 99 | print(subblock) 100 | } 101 | if block.info.id != 0 { 102 | unindent() 103 | print("\n") 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /Sources/SIL/Bitstream.swift: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The TensorFlow Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | import Foundation 16 | 17 | // You can think of this struct as either [Bool] representing a bit sequence 18 | // or as an arbitrary precision integer (with checked casts to fixed-width values). 19 | struct Bits: Equatable, Hashable, ExpressibleByIntegerLiteral, CustomStringConvertible { 20 | // Sorted in the order of significance, i.e. bits[0] is the LSB. 21 | private var bits: [Bool] 22 | var description: String { String(bits.reversed().map { $0 ? "1" : "0" }) } 23 | var count: Int { bits.lastIndex(of: true).map { $0 + 1 } ?? 0 } 24 | var isZero: Bool { bits.allSatisfy(!) } 25 | 26 | // TODO(#30): Going through strings might be a bit slow, and in those cases 27 | // we can utilize bitwise shifts, or use a more efficient representation 28 | var uint8: UInt8 { return cast() } 29 | var uint32: UInt32 { return cast() } 30 | var int: Int { 31 | assert( 32 | count <= Int.bitWidth - 1, 33 | "Casting a bit sequence of length " + String(bits.count) + " to an integer of width " 34 | + String(Int.bitWidth - 1)) 35 | return Int(uint32) 36 | } 37 | 38 | init(integerLiteral lit: Int) { 39 | self.init(lit) 40 | } 41 | 42 | init(_ value: Int) { 43 | self.bits = [] 44 | var rem = value 45 | while rem != 0 { 46 | bits.append(rem % 2 == 1) 47 | rem /= 2 48 | } 49 | } 50 | 51 | init(leastFirst bits: [Bool]) { self.bits = bits } 52 | init(mostFirst bits: [Bool]) { self.bits = bits.reversed() } 53 | 54 | // NB: Assumes that the integer is unsigned! 55 | private func cast() -> T { 56 | assert( 57 | count <= T.bitWidth, 58 | "Casting a bit sequence of length " + String(bits.count) + " to an integer of width " 59 | + String(T.bitWidth)) 60 | return T.init(count == 0 ? "0" : description, radix: 2)! 61 | } 62 | 63 | static func join(_ arr: [Bits]) -> Bits { 64 | return Bits(leastFirst: Array(arr.map { $0.bits }.joined())) 65 | } 66 | 67 | static func == (lhs: Bits, rhs: Bits) -> Bool { 68 | let minLen = min(lhs.bits.count, rhs.bits.count) 69 | // NB: .count does not consider trailing zeros 70 | return zip(lhs.bits, rhs.bits).allSatisfy(==) && lhs.count <= minLen && rhs.count <= minLen 71 | } 72 | 73 | func hash(into hasher: inout Hasher) { 74 | for b in 0.. Bool { 95 | try checkOffset(needBits: 1) 96 | let byte = data[byteOffset] 97 | let result = (byte >> bitOffset) % 2 == 1 98 | offset += 1 99 | return result 100 | } 101 | 102 | mutating func nextByte() throws -> UInt8 { 103 | if (offset % 8 == 0) { 104 | try checkOffset(needBits: 8) 105 | let result = data[byteOffset] 106 | offset += 8 107 | return result 108 | } else { 109 | return try next(bits: 8).uint8 110 | } 111 | } 112 | 113 | mutating func next(bits num: Int) throws -> Bits { 114 | var bits: [Bool] = [] 115 | for _ in 0.. [UInt8] { 122 | var bytes: [UInt8] = [] 123 | for _ in 0.. Bool { 61 | assert(!query.isEmpty) 62 | return chars.suffix(from: cursor).starts(with: Array(query)) 63 | } 64 | 65 | /// If chars[cursor..] starts with a given string, consume string and skip trivia afterwards. 66 | /// Otherwise, raise a parse error. 67 | func take(_ query: String) throws { 68 | guard peek(query) else { throw parseError("\(query) expected") } 69 | cursor += query.count 70 | skipTrivia() 71 | } 72 | 73 | /// If chars[cursor..-1] starts with a given string, consume string, skip trivia and return true. 74 | /// Otherwise, return false. 75 | func skip(_ query: String) -> Bool { 76 | // TODO(#13): Deduplicate with respect to take. 77 | guard peek(query) else { return false } 78 | cursor += query.count 79 | skipTrivia() 80 | return true 81 | } 82 | 83 | /// Consume characters starting from cursor while a given predicate keeps being true and 84 | /// return the consumed string. Skip trivia afterwards. 85 | func take(while fn: (Character) -> Bool) -> String { 86 | let result = chars.suffix(from: cursor).prefix(while: fn) 87 | cursor += result.count 88 | skipTrivia() 89 | return String(result) 90 | } 91 | 92 | /// Consume characters starting from cursor while a given predicate keeps being true and 93 | /// report whether something was consumed. Skip trivia afterwards. 94 | func skip(while fn: (Character) -> Bool) -> Bool { 95 | let result = take(while: fn) 96 | return !result.isEmpty 97 | } 98 | 99 | /// If cursor points to whitespace or comment, consume until it doesn't. 100 | /// This provides a cheap way to make whitespace and comments insignificant. 101 | private func skipTrivia() { 102 | guard cursor < chars.count else { return } 103 | if chars[cursor].isWhitespace { 104 | cursor += 1 105 | skipTrivia() 106 | } else if skip("//") { 107 | while cursor < chars.count && chars[cursor] != "\n" { 108 | cursor += 1 109 | } 110 | skipTrivia() 111 | } 112 | } 113 | 114 | // MARK: Tree-level APIs 115 | 116 | // Applies the function, but restores the cursor from before the call if it returns nil. 117 | func maybeParse(_ f: () throws -> T?) rethrows -> T? { 118 | let savedCursor = cursor 119 | if let result = try f() { 120 | return result 121 | } else { 122 | cursor = savedCursor 123 | return nil 124 | } 125 | } 126 | 127 | /// Same as `parseMany` but returning `nil` if the cursor isn't pointing at `pre`. 128 | /// This is necessary to e.g. accommodate a situation when not having a parameter list is 129 | /// as valid as having an empty parameter list. 130 | func parseNilOrMany( 131 | _ pre: String, _ sep: String, _ suf: String, _ parseOne: () throws -> T 132 | ) throws -> [T]? { 133 | guard peek(pre) else { return nil } 134 | return try parseMany(pre, sep, suf, parseOne) 135 | } 136 | 137 | /// Run a given parser multiple times as follows: 138 | /// 1) Consume `pre`. 139 | /// 2) Run parser interleaved with consuming `sep`. 140 | /// 3) Consume `suf`. 141 | /// 142 | /// For example, we can parse `(x, y, ...)` via `parseMany("(", ",", ")") { parseElement() }`. 143 | func parseMany( 144 | _ pre: String, _ sep: String, _ suf: String, _ parseOne: () throws -> T 145 | ) throws -> [T] { 146 | try take(pre) 147 | var result = [T]() 148 | if !peek(suf) { 149 | while true { 150 | let element = try parseOne() 151 | result.append(element) 152 | guard !peek(suf) else { break } 153 | guard !sep.isEmpty else { continue } 154 | try take(sep) 155 | } 156 | } 157 | try take(suf) 158 | return result 159 | } 160 | 161 | /// Same as `parseMany` but returning `nil` if the cursor isn't pointing at `pre`. 162 | /// If cursor isn't pointing at `pre`, return nil. This is necessary to e.g. accommodate 163 | /// a situation when not having attributes is valid. 164 | func parseNilOrMany(_ pre: String, _ parseOne: () throws -> T) throws -> [T]? { 165 | guard peek(pre) else { return nil } 166 | return try parseMany(pre, parseOne) 167 | } 168 | 169 | func parseUntilNil(_ parseOne: () throws -> T?) rethrows -> [T] { 170 | var result = [T]() 171 | while let element = try parseOne() { 172 | result.append(element) 173 | } 174 | return result 175 | } 176 | 177 | /// Run a given parser multiple times as follows: 178 | /// 1) Check that cursor if pointing at `pre` without consuming `pre`. 179 | /// 2) Run parser, repeat. 180 | /// 181 | /// For example, we can parse `@foo @bar` via `parseMany("@") { parseAttribute() }`. 182 | func parseMany(_ pre: String, _ parseOne: () throws -> T) throws -> [T] { 183 | var result = [T]() 184 | repeat { 185 | let element = try parseOne() 186 | result.append(element) 187 | } while peek(pre) 188 | return result 189 | } 190 | 191 | // MARK: Error reporting APIs 192 | 193 | /// Raise a parser error at a given position. 194 | func parseError(_ message: String, at: Int? = nil) -> Parser.Error { 195 | let position = at ?? cursor 196 | let newlines = chars.enumerated().prefix(position).filter({ $0.element == "\n" }) 197 | let line = newlines.count + 1 198 | let column = position - (newlines.last?.offset ?? 0) + 1 199 | return Parser.Error(path, line, column, message) 200 | } 201 | 202 | class Error: Swift.Error, CustomStringConvertible { 203 | let path: String 204 | let line: Int? 205 | let column: Int? 206 | let message: String 207 | 208 | public init(_ path: String, _ message: String) { 209 | self.path = path 210 | self.line = nil 211 | self.column = nil 212 | self.message = message 213 | } 214 | 215 | public init(_ path: String, _ line: Int, _ column: Int, _ message: String) { 216 | self.path = path 217 | self.line = line 218 | self.column = column 219 | self.message = message 220 | } 221 | 222 | var description: String { 223 | guard let line = line else { return "\(path): \(message)" } 224 | guard let column = column else { return "\(path):\(line): \(message)" } 225 | return "\(path):\(line):\(column): \(message)" 226 | } 227 | } 228 | } 229 | -------------------------------------------------------------------------------- /Sources/SIL/Printer.swift: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The TensorFlow Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | class Printer: CustomStringConvertible { 16 | var description: String = "" 17 | private var indentation: String = "" 18 | private var indented: Bool = false 19 | 20 | func indent() { 21 | let count = indentation.count + 2 22 | indentation = String(repeating: " ", count: count) 23 | } 24 | 25 | func unindent() { 26 | let count = max(indentation.count - 2, 0) 27 | indentation = String(repeating: " ", count: count) 28 | } 29 | 30 | func print(when: Bool = true, _ i: T) { 31 | print(when: when, i.description) 32 | } 33 | 34 | func print(when: Bool = true, _ s: String) { 35 | guard when else { return } 36 | let lines = s.split(omittingEmptySubsequences: false) { $0.isNewline } 37 | for (i, line) in lines.enumerated() { 38 | if !indented && !line.isEmpty { 39 | description += indentation 40 | indented = true 41 | } 42 | description += line 43 | if i < lines.count - 1 { 44 | description += "\n" 45 | indented = false 46 | } 47 | } 48 | } 49 | 50 | func print(_ x: T?, _ fn: (T) -> Void) { 51 | guard let x = x else { return } 52 | fn(x) 53 | } 54 | 55 | func print(_ pre: String, _ x: T?, _ fn: (T) -> Void) { 56 | guard let x = x else { return } 57 | print(pre) 58 | fn(x) 59 | } 60 | 61 | func print(_ x: T?, _ suf: String, _ fn: (T) -> Void) { 62 | guard let x = x else { return } 63 | fn(x) 64 | print(suf) 65 | } 66 | 67 | func print(_ xs: S, _ fn: (S.Element) -> Void) { 68 | for x in xs { 69 | fn(x) 70 | } 71 | } 72 | 73 | func print(_ xs: S, _ sep: String, _ fn: (S.Element) -> Void) { 74 | var needSep = false 75 | for x in xs { 76 | if needSep { 77 | print(sep) 78 | } 79 | needSep = true 80 | fn(x) 81 | } 82 | } 83 | 84 | func print( 85 | whenEmpty: Bool = true, _ pre: String, _ xs: S, _ sep: String, _ suf: String, 86 | _ fn: (S.Element) -> Void 87 | ) { 88 | guard !xs.isEmpty || whenEmpty else { return } 89 | print(pre) 90 | print(xs, sep, fn) 91 | print(suf) 92 | } 93 | 94 | func literal(_ b: Bool) { 95 | print(String(b)) 96 | } 97 | 98 | func literal(_ f: Float) { 99 | print(String(f)) 100 | } 101 | 102 | func literal(_ n: Int) { 103 | print(String(n)) 104 | } 105 | 106 | func hex(_ n: Int) { 107 | print("0x" + String(format: "%X", n)) 108 | } 109 | 110 | func literal(_ s: String) { 111 | // TODO(#24): Print string literals with control characters in a useful way. 112 | print("\"") 113 | print(s) 114 | print("\"") 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /Sources/SIL/SExpr.swift: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The TensorFlow Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // This is a temporary addition to libSIL and will likely be removed in the future. 16 | // It is a hack that allows one to read the output of e.g. swiftc -dump-ast. 17 | public enum SExpr : Equatable { 18 | public enum Property : Equatable { 19 | case value(SExpr) 20 | case field(String, SExpr) 21 | } 22 | 23 | case symbol(String) 24 | case string(String) 25 | case sourceRange(String) // We don't parse those further right now 26 | case record(String, [Property]) 27 | 28 | public static func parse(fromPath path: String) throws -> SExpr { 29 | let parser = try SExprParser(forPath: path) 30 | return try parser.parseExpr() 31 | } 32 | } 33 | 34 | class SExprParser: Parser { 35 | func parseExpr() throws -> SExpr { 36 | if skip("(") { 37 | var properties: [SExpr.Property] = [] 38 | guard case let .symbol(name) = try parseExpr() else { 39 | throw parseError("Expected an expression body to start with a symbol") 40 | } 41 | while !skip(")") { 42 | let expr = try parseExpr() 43 | if case let .symbol(propName) = expr, skip("=") { 44 | properties.append(.field(propName, try parseValue())) 45 | } else { 46 | if case let .symbol(exprValue) = expr { 47 | guard !exprValue.isEmpty else { 48 | throw parseError("Malformed expression") 49 | } 50 | } 51 | properties.append(.value(expr)) 52 | } 53 | } 54 | return .record(name, properties) 55 | } 56 | return try parseValue() 57 | } 58 | 59 | func parseValue() throws -> SExpr { 60 | if skip("'") { 61 | let result = take(while: { $0 != "'" }) 62 | try take("'") 63 | return .string(result) 64 | } 65 | if skip("\"") { 66 | let result = take(while: { $0 != "\"" }) 67 | try take("\"") 68 | return .string(result) 69 | } 70 | if skip("[") { 71 | let result = take(while: { $0 != "]" }) 72 | try take("]") 73 | return .sourceRange(result) 74 | } 75 | return try parseSymbol() 76 | } 77 | 78 | func parseSymbol() throws -> SExpr { 79 | // NB: This is more complicated than it ever should be because swiftc 80 | // likes to print badly formed symbols that look like Module.(file).Type 81 | var balance = 0 82 | func shouldTake(_ c: Character) -> Bool { 83 | if c == "(" { 84 | balance += 1 85 | return true 86 | } 87 | if balance > 0 { 88 | if c == ")" { 89 | balance -= 1 90 | } 91 | return true 92 | } 93 | return !c.isWhitespace && !")=".contains(c) 94 | } 95 | return .symbol(take(while: shouldTake)) 96 | } 97 | } 98 | 99 | class SExprPrinter: Printer { 100 | func printExpr(_ e: SExpr) { 101 | switch e { 102 | case let .symbol(value): print(value) 103 | case let .string(value): print("'\(value)'") 104 | case let .sourceRange(value): print("[\(value)]") 105 | case let .record(name, properties): 106 | print("(") 107 | print(name) 108 | for prop in properties { 109 | switch prop { 110 | case let .value(value): 111 | if case .record(_, _) = value { 112 | print("\n") 113 | indent() 114 | printExpr(value) 115 | unindent() 116 | } else { 117 | print(" ") 118 | printExpr(value) 119 | } 120 | case let .field(name, value): 121 | print(" ") 122 | print(name) 123 | print("=") 124 | printExpr(value) 125 | } 126 | } 127 | print(")") 128 | } 129 | } 130 | } 131 | 132 | extension SExpr: CustomStringConvertible { 133 | public var description: String { 134 | let printer = SExprPrinter() 135 | printer.printExpr(self) 136 | return printer.description 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /Tests/LinuxMain.swift: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The TensorFlow Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | import XCTest 16 | 17 | #if !SKIP_SIL_TESTS 18 | import SILTests 19 | let silTests = [ 20 | testCase(BitcodeTests.allTests), 21 | testCase(BitsTests.allTests), 22 | testCase(BitstreamTests.allTests), 23 | testCase(SILTests.DescriptionTests.allTests), 24 | testCase(SILParserTests.allTests), 25 | testCase(InstructionTests.allTests), 26 | testCase(ModuleTests.allTests), 27 | testCase(PrinterTests.allTests), 28 | testCase(SExprTests.allTests), 29 | testCase(SILAnalysisTests.allTests), 30 | ] 31 | #else 32 | let silTests = [XCTestCaseEntry]() 33 | #endif 34 | 35 | let tests = silTests 36 | XCTMain(tests) 37 | -------------------------------------------------------------------------------- /Tests/SILTests/BitcodeTests.swift: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The TensorFlow Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | import XCTest 16 | @testable import SIL 17 | 18 | public final class BitcodeTests: XCTestCase { 19 | private func testLoadingSIB() { 20 | do { 21 | let topBlock = try loadSIBBitcode(fromPath: "Tests/SILTests/Resources/AddFloat.sib") 22 | XCTAssertEqual(topBlock.subblocks.count, 1) 23 | let moduleBlock = topBlock.subblocks[0] 24 | XCTAssertEqual(moduleBlock.subblocks.count, 7) 25 | let silBlock = moduleBlock.subblocks[2] 26 | let p = BitcodePrinter() 27 | p.print(silBlock) 28 | XCTAssertEqual( 29 | p.description, 30 | """ 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | """ 46 | ) 47 | } catch { 48 | XCTFail(String(describing: error)) 49 | return 50 | } 51 | } 52 | } 53 | 54 | extension BitcodeTests { 55 | public static let allTests: [(String, (BitcodeTests) -> () -> Void)] = [ 56 | // TODO(TF-774): Disabling the test since it's not on the critical path at the moment. 57 | // ("testLoadingSIB", testLoadingSIB), 58 | ] 59 | } 60 | -------------------------------------------------------------------------------- /Tests/SILTests/BitsTests.swift: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The TensorFlow Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | import XCTest 16 | @testable import SIL 17 | 18 | public final class BitsTests: XCTestCase { 19 | public func testIntConstructor() { 20 | let cases = Array(0...100) + (1...100).map { _ in Int.random(in: 0...Int.max) } 21 | for c in cases { 22 | let expected = Bits(mostFirst: Array(String(c, radix: 2)).map({ $0 == "1" })) 23 | XCTAssertEqual(Bits(c), expected) 24 | } 25 | } 26 | 27 | public func testJoin() { 28 | for _ in 1...100 { 29 | let numSequences = Int.random(in: 2...10) 30 | let sequences = (0..) -> [Bool] { 38 | return (1...Int.random(in: length)).map { _ in Bool.random() } 39 | } 40 | 41 | public func testEqualityClasses() { 42 | let equalityClasses = [ 43 | [ 44 | Bits(3), 45 | Bits(mostFirst: [true, true]), 46 | Bits(mostFirst: [false, false, true, true]) 47 | ], 48 | [ 49 | Bits(0), 50 | Bits(leastFirst: [false, false, false]), 51 | Bits(mostFirst: [false, false, false, false, false]) 52 | ], 53 | [ 54 | Bits(5), 55 | Bits(mostFirst: [false, false, false, false, true, false, true]) 56 | ] 57 | ] 58 | for (i, clsi) in equalityClasses.enumerated() { 59 | for (j, clsj) in equalityClasses.enumerated() { 60 | for ei in clsi { 61 | for ej in clsj { 62 | if (i == j) { 63 | XCTAssertEqual(ei, ej) 64 | XCTAssertEqual(ei.hashValue, ej.hashValue) 65 | } else { 66 | XCTAssertNotEqual(ei, ej) 67 | // NB: hashValues might be equal here 68 | } 69 | } 70 | } 71 | } 72 | } 73 | } 74 | 75 | public func testEqualityWithExtraSignificantZeros() { 76 | for _ in 1...100 { 77 | let seq = randomSequence(length: 1...200) 78 | let paddedSeq = seq + Array(repeating: false, count: Int.random(in: 1...50)) 79 | XCTAssertEqual(Bits(leastFirst: seq), Bits(leastFirst: paddedSeq)) 80 | XCTAssertEqual(Bits(leastFirst: seq).hashValue, Bits(leastFirst: paddedSeq).hashValue) 81 | } 82 | } 83 | } 84 | 85 | extension BitsTests { 86 | public static let allTests = [ 87 | ("testIntConstructor", testIntConstructor), 88 | ("testJoin", testJoin), 89 | ("testEqualityClasses", testEqualityClasses), 90 | ("testEqualityWithExtraSignificantZeros", testEqualityWithExtraSignificantZeros), 91 | ] 92 | } 93 | -------------------------------------------------------------------------------- /Tests/SILTests/BitstreamTests.swift: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The TensorFlow Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | import XCTest 16 | @testable import SIL 17 | 18 | public final class BitstreamTests: XCTestCase { 19 | public func testReads() { 20 | // NB: The bytes are read from left to right, the bits are read from 21 | // the LSB to MSB (i.e. right to left!). 22 | var stream = Bitstream(Data([0b11110101, 0b01000111])) 23 | for i in 0..<5 { 24 | XCTAssertEqual(try! stream.nextBit(), i % 2 == 0) 25 | } 26 | XCTAssertEqual(try! stream.nextByte(), 0b00111111) 27 | for i in 5..<8 { 28 | XCTAssertEqual(try! stream.nextBit(), i % 2 == 0) 29 | } 30 | XCTAssertEqual(stream.isEmpty, true) 31 | } 32 | } 33 | 34 | extension BitstreamTests { 35 | public static let allTests = [ 36 | ("testReads", testReads), 37 | ] 38 | } 39 | -------------------------------------------------------------------------------- /Tests/SILTests/DescriptionTests.swift: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The TensorFlow Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | import XCTest 16 | import SIL 17 | 18 | public final class DescriptionTests: XCTestCase { 19 | public func testIdentity() { 20 | let block = Block( 21 | "bb0", 22 | [Argument("%0", .namedType("Int"))], 23 | [], 24 | TerminatorDef(.return(Operand("%0", .namedType("Int"))), nil) 25 | ) 26 | let identity = Function( 27 | .public, 28 | [], 29 | "$s4main8identityyS2iF", 30 | .attributedType( 31 | [.convention(.thin)], 32 | .functionType([.namedType("Int")], .namedType("Int"))), 33 | [block]) 34 | let module = Module([identity]) 35 | XCTAssertEqual( 36 | module.description, 37 | """ 38 | sil @$s4main8identityyS2iF : $@convention(thin) (Int) -> Int { 39 | bb0(%0 : $Int): 40 | return %0 : $Int 41 | } 42 | """ 43 | ) 44 | } 45 | 46 | public func testAdd() { 47 | let block = Block( 48 | "bb0", 49 | [Argument("%0", .namedType("Int")), Argument("%1", .namedType("Int"))], 50 | [ 51 | OperatorDef( 52 | Result(["%4"]), 53 | .structExtract( 54 | Operand("%0", .namedType("Int")), DeclRef(["Int", "_value"], nil, nil)), 55 | nil), 56 | OperatorDef( 57 | Result(["%5"]), 58 | .structExtract( 59 | Operand("%1", .namedType("Int")), DeclRef(["Int", "_value"], nil, nil)), 60 | nil), 61 | OperatorDef( 62 | Result(["%6"]), 63 | .integerLiteral(.namedType("Builtin.Int1"), -1), 64 | nil), 65 | OperatorDef( 66 | Result(["%7"]), 67 | .builtin( 68 | "sadd_with_overflow_Int64", 69 | [ 70 | Operand("%4", .namedType("Builtin.Int64")), 71 | Operand("%5", .namedType("Builtin.Int64")), 72 | Operand("%6", .namedType("Builtin.Int1")) 73 | ], 74 | .tupleType([.namedType("Builtin.Int64"), .namedType("Builtin.Int1")])), 75 | nil), 76 | OperatorDef( 77 | Result(["%8"]), 78 | .tupleExtract( 79 | Operand( 80 | "%7", 81 | .tupleType([.namedType("Builtin.Int64"), .namedType("Builtin.Int1")])), 82 | 0), 83 | nil), 84 | OperatorDef( 85 | Result(["%9"]), 86 | .tupleExtract( 87 | Operand( 88 | "%7", 89 | .tupleType([.namedType("Builtin.Int64"), .namedType("Builtin.Int1")])), 90 | 1), 91 | nil), 92 | OperatorDef( 93 | nil, 94 | .condFail(Operand("%9", .namedType("Builtin.Int1")), ""), 95 | nil), 96 | OperatorDef( 97 | Result(["%11"]), 98 | .struct(.namedType("Int"), [Operand("%8", .namedType("Builtin.Int64"))]), 99 | nil), 100 | ], 101 | TerminatorDef(.return(Operand("%11", .namedType("Int"))), nil) 102 | ) 103 | let add = Function( 104 | .public, 105 | [], 106 | "$s4main3addyS2i_SitF", 107 | .attributedType( 108 | [.convention(.thin)], 109 | .functionType([.namedType("Int"), .namedType("Int")], .namedType("Int"))), 110 | [block]) 111 | let module = Module([add]) 112 | XCTAssertEqual( 113 | module.description, 114 | """ 115 | sil @$s4main3addyS2i_SitF : $@convention(thin) (Int, Int) -> Int { 116 | bb0(%0 : $Int, %1 : $Int): 117 | %4 = struct_extract %0 : $Int, #Int._value 118 | %5 = struct_extract %1 : $Int, #Int._value 119 | %6 = integer_literal $Builtin.Int1, -1 120 | %7 = builtin "sadd_with_overflow_Int64"(%4 : $Builtin.Int64, %5 : $Builtin.Int64, %6 : $Builtin.Int1) : $(Builtin.Int64, Builtin.Int1) 121 | %8 = tuple_extract %7 : $(Builtin.Int64, Builtin.Int1), 0 122 | %9 = tuple_extract %7 : $(Builtin.Int64, Builtin.Int1), 1 123 | cond_fail %9 : $Builtin.Int1, "" 124 | %11 = struct $Int (%8 : $Builtin.Int64) 125 | return %11 : $Int 126 | } 127 | """ 128 | ) 129 | } 130 | } 131 | 132 | extension DescriptionTests { 133 | public static let allTests = [ 134 | ("testIdentity", testIdentity), 135 | ("testAdd", testAdd), 136 | ] 137 | } 138 | -------------------------------------------------------------------------------- /Tests/SILTests/InstructionTests.swift: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The TensorFlow Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | import XCTest 16 | @testable import SIL 17 | 18 | let instructionDefs = [ 19 | "%103 = builtin \"ptrtoint_Word\"(%101 : $Builtin.RawPointer) : $Builtin.Word", 20 | "%139 = builtin \"smul_with_overflow_Int64\"(%136 : $Builtin.Int64, %137 : $Builtin.Int64, %138 : $Builtin.Int1) : $(Builtin.Int64, Builtin.Int1)", 21 | "cond_fail %141 : $Builtin.Int1, \"\"", 22 | "%112 = integer_literal $Builtin.Int32, 1", 23 | "return %1 : $Int", 24 | "return %280 : $()", 25 | "retain_value %124 : $TensorShape", 26 | "release_value %5 : $Tensor", 27 | "%180 = struct $Bool (%179 : $Builtin.Int1)", 28 | "%211 = struct $StaticString (%210 : $Builtin.Word, %209 : $Builtin.Word, %168 : $Builtin.Int8)", 29 | "%21 = struct_extract %20 : $Int, #Int._value", 30 | "%64 = tuple_extract %63 : $(Builtin.Int64, Builtin.Int1), 0", 31 | "alloc_stack $Float", 32 | "alloc_stack $IndexingIterator>, var, name \"$inputIndex$generator\"", 33 | "%79 = alloc_stack $Optional<(Int, Int)>, loc \"Examples/cnn.swift\":16:3, scope 6", 34 | "apply %10(%1) : $@convention(method) (@guaranteed Array) -> Int", 35 | "apply %17(%1, %2, %16) : $@convention(witness_method: Comparable) <τ_0_0 where τ_0_0 : Comparable> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_0, @thick τ_0_0.Type) -> Bool", 36 | "apply %8(%2, %6) : $@convention(thin) <τ_0_0, τ_0_1 where τ_0_0 : Strideable, τ_0_1 : Strideable> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_1) -> ()", 37 | "%154 = apply %153, ArraySlice>(%152, %150, %119) : $@convention(method) <τ_0_0 where τ_0_0 : RangeReplaceableCollection><τ_1_0 where τ_1_0 : Sequence, τ_0_0.Element == τ_1_0.Element> (@inout τ_0_0, @in_guaranteed τ_1_0, @thick τ_0_0.Type) -> (), loc \"example.swift\":22:10, scope 4", 38 | "%14 = begin_borrow %10 : $Tensor", 39 | "%6 = copy_value %0 : $TensorShape, loc \"Examples/cnn.swift\":10:11, scope 3", 40 | "%54 = copy_value %53 : $Tensor", 41 | "destroy_value %10 : $Tensor", 42 | "end_borrow %27 : $Tensor", 43 | "begin_access [modify] [static] %0 : $*Array", 44 | "begin_apply %266(%125, %265) : $@yield_once @convention(method) (Int, @inout Array) -> @yields @inout Float", 45 | "br bb9", 46 | "br label(%0 : $A, %1 : $B)", 47 | "cond_br %11, bb3, bb2", 48 | "cond_br %12, label(%0 : $A), label(%1 : $B)", 49 | "%94 = convert_escape_to_noescape [not_guaranteed] %93 : $@callee_guaranteed () -> Bool to $@noescape @callee_guaranteed () -> Bool", 50 | "%1 = convert_function %0 : $@convention(thin) () -> Bool to [without_actually_escaping] $@convention(thin) @noescape () -> Bool", 51 | "copy_addr %1 to [initialization] %33 : $*Self", 52 | "dealloc_stack %162 : $*IndexingIterator>", 53 | "debug_value %1 : $Array, let, name \"input\", argno 2", 54 | "debug_value %11 : $Int, let, name \"n\"", 55 | "debug_value_addr %0 : $*Array, var, name \"out\", argno 1", 56 | "debug_value %0 : $TensorShape, let, name \"ar\", argno 1, loc \"Examples/cnn.swift\":9:18, scope 2", 57 | "end_access %265 : $*Array", 58 | "end_access [abort] %42 : $T", 59 | "end_apply %268", 60 | "%170 = enum $Padding, #Padding.valid!enumelt", 61 | "%1 = enum $U, #U.DataCase!enumelt.1, %0 : $T", 62 | "float_literal $Builtin.FPIEEE32, 0x0", 63 | "float_literal $Builtin.FPIEEE64, 0x3F800000", 64 | "function_ref @$s4main11threadCountSiyF : $@convention(thin) () -> Int", 65 | "function_ref @$ss6stride4from2to2bys8StrideToVyxGx_x0E0QztSxRzlF : $@convention(thin) <τ_0_0 where τ_0_0 : Strideable> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_0, @in_guaranteed τ_0_0.Stride) -> @out StrideTo<τ_0_0>", 66 | "function_ref @$s4main1CV3fooyyqd___qd_0_tSayqd__GRszSxRd_0_r0_lF : $@convention(method) <τ_0_0><τ_1_0, τ_1_1 where τ_0_0 == Array<τ_1_0>, τ_1_1 : Strideable> (@in_guaranteed τ_1_0, @in_guaranteed τ_1_1, C>) -> ()", 67 | "function_ref @$ss8StrideToV12makeIterators0abD0VyxGyF : $@convention(method) <τ_0_0 where τ_0_0 : Strideable> (@in StrideTo<τ_0_0>) -> @out StrideToIterator<τ_0_0>", 68 | "%0 = global_addr @$s5small4____Sivp : $*Int", 69 | "(%5, %6) = destructure_tuple %2 : $(Array, Builtin.RawPointer)", 70 | "%42 = index_addr %35 : $*Int, %41 : $Builtin.Word", 71 | "%7 = pointer_to_address %6 : $Builtin.RawPointer to [strict] $*Int", 72 | "%7 = pointer_to_address %6 : $Builtin.RawPointer to $*Int", 73 | "load %117 : $*Optional", 74 | "%22 = load [copy] %21 : $*TensorShape", 75 | "%71 = load [take] %52 : $*Zip2Sequence, Array>", 76 | "%84 = load [trivial] %79 : $*Optional<(Int, Int)>", 77 | "mark_dependence %11 : $@noescape @callee_guaranteed () -> Bool on %1 : $TensorShape", 78 | "metatype $@thick Self.Type", 79 | "metatype $@thin Int.Type", 80 | "%4 = partial_apply [callee_guaranteed] %2(%3) : $@convention(thin) <τ_0_0 where τ_0_0 : TensorFlowScalar> (@guaranteed Tensor<τ_0_0>) -> Bool", 81 | "%n = select_enum %0 : $U, case #U.Case1!enumelt: %1, case #U.Case2!enumelt: %2, default %3 : $T", 82 | "store %88 to %89 : $*StrideTo", 83 | "store %88 to [trivial] %112 : $*Int", 84 | "store %152 to [init] %155 : $*ArraySlice", 85 | "string_literal utf8 \"Fatal error\"", 86 | "strong_release %99 : $@callee_guaranteed () -> @owned String", 87 | "strong_retain %99 : $@callee_guaranteed () -> @owned String", 88 | // TODO(#24): Parse string literals with control characters. 89 | // "string_literal utf8 \"\\n\"", 90 | "struct_element_addr %235 : $*Float, #Float._value", 91 | "switch_enum %122 : $Optional, case #Optional.some!enumelt.1: bb11, case #Optional.none!enumelt: bb18", 92 | "switch_enum %84 : $Optional<(Int, Int)>, case #Optional.some!enumelt.1: bb5, case #Optional.none!enumelt: bb6, loc \"Examples/cnn.swift\":16:3, scope 6", 93 | "tuple ()", 94 | "tuple (%a : $A, %b : $B)", 95 | // TODO(#23): Parse tuple types with argument labels 96 | // "tuple $(a:A, b:B) (%a, %b)", 97 | "%2 = thin_to_thick_function %1 : $@convention(thin) @noescape () -> Bool to $@noescape @callee_guaranteed () -> Bool", 98 | "unreachable", 99 | "witness_method $Self, #Comparable.\"<=\"!1 : (Self.Type) -> (Self, Self) -> Bool : $@convention(witness_method: Comparable) <τ_0_0 where τ_0_0 : Comparable> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_0, @thick τ_0_0.Type) -> Bool" 100 | ] 101 | 102 | // This definition uses a macOS-only trick to dynamically generate test cases. 103 | // That not just doesn't work on Linux but also doesn't compile. 104 | #if os(macOS) 105 | public final class InstructionTests: XCTestCase { 106 | // In order to declare this as `let instructionDef: String`, we need to write an appropriate init. 107 | // In that init, we need to delegate to the superclass init that involves `NSInvocation`. 108 | // That doesn't seem possible, so we use this hack. 109 | // error: 'NSInvocation' is unavailable in Swift: NSInvocation and related APIs not available. 110 | private var instructionDef: String! 111 | 112 | public func testRoundtrip() { 113 | do { 114 | let p = SILParser(forString: instructionDef) 115 | let i = try p.parseInstructionDef() 116 | XCTAssertEqual(instructionDef, i.description) 117 | } catch { 118 | XCTFail(String(describing: error) + "\n" + instructionDef) 119 | } 120 | } 121 | 122 | public override class var defaultTestSuite: XCTestSuite { 123 | let testSuite = XCTestSuite(name: NSStringFromClass(self)) 124 | for instructionDef in instructionDefs { 125 | for invocation in testInvocations { 126 | let testCase = InstructionTests(invocation: invocation) 127 | testCase.instructionDef = instructionDef 128 | testSuite.addTest(testCase) 129 | } 130 | } 131 | return testSuite 132 | } 133 | } 134 | #endif 135 | 136 | #if os(Linux) 137 | public final class InstructionTests: XCTestCase { 138 | } 139 | 140 | extension InstructionTests { 141 | public static var allTests = instructionDefs.map { instructionDef in 142 | ( 143 | "testRoundtrip", 144 | { (_: InstructionTests) in 145 | { 146 | do { 147 | let p = SILParser(forString: instructionDef) 148 | let i = try p.parseInstructionDef() 149 | XCTAssertEqual(instructionDef, i.description) 150 | } catch { 151 | XCTFail(String(describing: error) + "\n" + instructionDef) 152 | } 153 | } 154 | } 155 | ) 156 | } 157 | } 158 | #endif 159 | -------------------------------------------------------------------------------- /Tests/SILTests/ModuleTests.swift: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The TensorFlow Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | import Foundation 16 | import XCTest 17 | import SIL 18 | 19 | public final class ModuleTests: XCTestCase { 20 | public func testAvgPool1D() { 21 | testRoundtrip("Tests/SILTests/Resources/AvgPool1D.sil") 22 | } 23 | 24 | private func testRoundtrip(_ silPath: String) { 25 | do { 26 | guard let expectedData = FileManager.default.contents(atPath: silPath) else { 27 | return XCTFail("\(silPath) not found") 28 | } 29 | guard let expected = String(data: expectedData, encoding: .utf8) else { 30 | return XCTFail("\(silPath) not in UTF-8") 31 | } 32 | let module = try Module.parse(fromSILPath: silPath) 33 | let actual = module.description + "\n" 34 | if (expected != actual) { 35 | if let actualFile = FileManager.default.makeTemporaryFile() { 36 | let actualPath = actualFile.path 37 | FileManager.default.createFile(atPath: actualPath, contents: Data(actual.utf8)) 38 | if shelloutOrFail("colordiff", "-u", silPath, actualPath) { 39 | XCTFail("Roundtrip failed: expected \(silPath), actual: \(actualPath)") 40 | } else { 41 | XCTFail("Roundtrip failed: expected \(silPath), actual: \n\(actual)") 42 | } 43 | } else { 44 | XCTFail("Roundtrip failed") 45 | } 46 | } 47 | } catch { 48 | XCTFail(String(describing: error)) 49 | } 50 | } 51 | } 52 | 53 | extension ModuleTests { 54 | public static let allTests = [ 55 | ("testAvgPool1D", testAvgPool1D), 56 | ] 57 | } 58 | -------------------------------------------------------------------------------- /Tests/SILTests/PrinterTests.swift: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The TensorFlow Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | import XCTest 16 | @testable import SIL 17 | 18 | public final class PrinterTests: XCTestCase { 19 | public func testIndent() { 20 | let p = Printer() 21 | p.print("line") 22 | p.indent() 23 | p.print(" #1\n") 24 | p.print("line #2\n") 25 | p.print("line #3\n\nline #5\n") 26 | p.print("\n") 27 | p.print("line #7") 28 | p.print("\n\n") 29 | p.unindent() 30 | p.print("line #9") 31 | XCTAssertEqual( 32 | p.description, 33 | """ 34 | line #1 35 | line #2 36 | line #3 37 | 38 | line #5 39 | 40 | line #7 41 | 42 | line #9 43 | """ 44 | ) 45 | } 46 | } 47 | 48 | extension PrinterTests { 49 | public static let allTests = [ 50 | ("testIndent", testIndent), 51 | ] 52 | } 53 | -------------------------------------------------------------------------------- /Tests/SILTests/Process.swift: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The TensorFlow Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | import Foundation 16 | import XCTest 17 | 18 | func shellout(_ args: String...) throws -> Int32 { 19 | return try shellout(args) 20 | } 21 | 22 | func shellout(_ args: [String]) throws -> Int32 { 23 | // We need to have the availability annotation due to some Process shenanigans 24 | // present in the standard library... 25 | if #available(macOS 10.13, *) { 26 | let process = Process() 27 | process.executableURL = URL(fileURLWithPath: "/usr/bin/env") 28 | process.arguments = args 29 | try process.run() 30 | process.waitUntilExit() 31 | return process.terminationStatus 32 | } else { 33 | throw ShelloutError.unavailable("shellout is unavailable on this platform") 34 | } 35 | } 36 | 37 | func shelloutOrFail(_ args: String...) -> Bool { 38 | let commandLine = args.joined(separator: " ") 39 | do { 40 | let status = try shellout(args) 41 | if status != 0 { 42 | XCTFail("Failed to execute \(commandLine)\nExit code: \(status))") 43 | return false 44 | } 45 | } catch { 46 | XCTFail("Failed to execute \(commandLine)\n\(String(describing: error))") 47 | return false 48 | } 49 | return true 50 | } 51 | 52 | enum ShelloutError: Error { 53 | case unavailable(String) 54 | } 55 | -------------------------------------------------------------------------------- /Tests/SILTests/Resources/AddFloat.sib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tensorflow/swift/f0d6c74ef5d016046afc1eac0b07a2f6b74b8fdf/Tests/SILTests/Resources/AddFloat.sib -------------------------------------------------------------------------------- /Tests/SILTests/Resources/AddFloat.swift: -------------------------------------------------------------------------------- 1 | func add(x: Float) -> Float { 2 | return x + 4 3 | } 4 | -------------------------------------------------------------------------------- /Tests/SILTests/Resources/AvgPool1D.swift: -------------------------------------------------------------------------------- 1 | public func avgPool1D( 2 | _ out: inout [Float], _ input: [Float], _ windowSize: Int, _ windowStride: Int, 3 | _ outScale: Float 4 | ) { 5 | let n = input.count 6 | let outSize = (n - windowSize) / windowStride + 1 7 | let outStart = threadIndex() 8 | let outStride = threadCount() 9 | for outIndex in stride(from: outStart, to: outSize, by: outStride) { 10 | out[outIndex] = 0.0 11 | let beginWindow = outIndex * windowStride 12 | let endWindow = outIndex * windowStride + windowSize 13 | for inputIndex in beginWindow.. Int { 22 | return 0 23 | } 24 | 25 | public func threadCount() -> Int { 26 | return 1 27 | } 28 | -------------------------------------------------------------------------------- /Tests/SILTests/Resources/Example.sexpr: -------------------------------------------------------------------------------- 1 | (source_file "a.sil" 2 | (import_decl range=[a.sil:3:1 - line:3:8] 'Builtin') 3 | (import_decl range=[a.sil:4:1 - line:4:8] 'Swift') 4 | (import_decl range=[a.sil:5:1 - line:5:8] 'SwiftShims') 5 | (import_decl range=[a.sil:7:1 - line:7:8] 'TensorFlow') 6 | (struct_decl range=[a.sil:9:1 - line:14:1] "Conv2d" interface type='Conv2d.Type' access=internal non-resilient 7 | (pattern_binding_decl range=[a.sil:10:16 - line:10:43] 8 | (pattern_typed type='Tensor' 9 | (pattern_named type='Tensor' 'w') 10 | (type_ident 11 | (component id='Tensor' bind=TensorFlow.(file).Tensor) 12 | (type_ident 13 | (component id='Float' bind=Swift.(file).Float))))) 14 | (var_decl range=[a.sil:10:20 - line:10:20] "w" type='Tensor' interface type='Tensor' access=internal let readImpl=stored immutable 15 | (accessor_decl range=[a.sil:10:39 - line:10:39] 'anonname=0x7fa061817580' interface type='(Conv2d) -> () -> Tensor' access=internal get_for=w 16 | (parameter "self" interface type='Conv2d') 17 | (parameter_list))) 18 | (pattern_binding_decl range=[a.sil:11:16 - line:11:43] 19 | (pattern_typed type='Tensor' 20 | (pattern_named type='Tensor' 'b') 21 | (type_ident 22 | (component id='Tensor' bind=TensorFlow.(file).Tensor) 23 | (type_ident 24 | (component id='Float' bind=Swift.(file).Float))))) 25 | (var_decl range=[a.sil:11:20 - line:11:20] "b" type='Tensor' interface type='Tensor' access=internal let readImpl=stored immutable 26 | (accessor_decl range=[a.sil:11:39 - line:11:39] 'anonname=0x7fa0618178b0' interface type='(Conv2d) -> () -> Tensor' access=internal get_for=b 27 | (parameter "self" interface type='Conv2d') 28 | (parameter_list))) 29 | (func_decl range=[a.sil:12:3 - line:12:49] "apply(_:)" interface type='(Conv2d) -> (Tensor) -> Tensor' access=internal 30 | (parameter "self" interface type='Conv2d') 31 | (parameter_list 32 | (parameter "x" interface type='Tensor') range=[a.sil:12:13 - line:12:32]) 33 | (result 34 | (type_ident 35 | (component id='Tensor' bind=TensorFlow.(file).Tensor) 36 | (type_ident 37 | (component id='Float' bind=Swift.(file).Float))))) 38 | (constructor_decl range=[a.sil:13:3 - line:13:42] "init(w:b:)" interface type='(Conv2d.Type) -> (Tensor, Tensor) -> Conv2d' access=internal designated 39 | (parameter "self" interface type='Conv2d' inout) 40 | (parameter_list 41 | (parameter "w" apiName=w interface type='Tensor') 42 | (parameter "b" apiName=b interface type='Tensor') range=[a.sil:13:7 - line:13:42]))) 43 | (func_decl range=[a.sil:16:1 - line:16:43] "f(_:)" interface type='(Tensor) -> Tensor' access=internal 44 | (parameter_list 45 | (parameter "x" interface type='Tensor') range=[a.sil:16:7 - line:16:26]) 46 | (result 47 | (type_ident 48 | (component id='Tensor' bind=TensorFlow.(file).Tensor) 49 | (type_ident 50 | (component id='Float' bind=Swift.(file).Float)))))) 51 | -------------------------------------------------------------------------------- /Tests/SILTests/SExprTests.swift: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The TensorFlow Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | import Foundation 16 | import XCTest 17 | import SIL 18 | 19 | public final class SExprTests: XCTestCase { 20 | public func testExample() { 21 | testRoundtrip("Tests/SILTests/Resources/Example.sexpr") 22 | } 23 | 24 | private func testRoundtrip(_ sourcePath: String) { 25 | do { 26 | guard let expectedData = FileManager.default.contents(atPath: sourcePath) else { 27 | return XCTFail("\(sourcePath) not found") 28 | } 29 | guard let expected = String(data: expectedData, encoding: .utf8) else { 30 | return XCTFail("\(sourcePath) not in UTF-8") 31 | } 32 | let sexpr = try SExpr.parse(fromPath: sourcePath) 33 | let actual = sexpr.description + "\n" 34 | if normalize(expected) != actual { 35 | if let actualFile = FileManager.default.makeTemporaryFile() { 36 | let actualPath = actualFile.path 37 | FileManager.default.createFile(atPath: actualPath, contents: Data(actual.utf8)) 38 | if shelloutOrFail("colordiff", "-u", sourcePath, actualPath) { 39 | XCTFail("Roundtrip failed: expected \(sourcePath), actual: \(actualPath)") 40 | } else { 41 | XCTFail("Roundtrip failed: expected \(sourcePath), actual: \n\(actual)") 42 | } 43 | } else { 44 | XCTFail("Roundtrip failed") 45 | } 46 | } 47 | } catch { 48 | XCTFail(String(describing: error)) 49 | } 50 | } 51 | 52 | func normalize(_ s: String) -> String { 53 | return s.replacingOccurrences(of: "\"", with: "'") 54 | } 55 | } 56 | 57 | extension SExprTests { 58 | public static let allTests = [ 59 | ("testExample", testExample), 60 | ] 61 | } 62 | 63 | -------------------------------------------------------------------------------- /Tests/SILTests/SILAnalysisTests.swift: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The TensorFlow Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | import Foundation 16 | import XCTest 17 | @testable import SIL 18 | 19 | func parseDef(_ source: String) -> InstructionDef? { 20 | do { 21 | let p = SILParser(forString: source) 22 | return try p.parseInstructionDef() 23 | } catch { 24 | XCTFail(String(describing: error) + "\n" + source) 25 | return nil 26 | } 27 | } 28 | 29 | public final class SILAnalysisTests: XCTestCase { 30 | lazy var parsedInstructionDefs: [InstructionDef] = instructionDefs.compactMap(parseDef) 31 | 32 | public func testAlphaConverted() { 33 | for def in parsedInstructionDefs { 34 | checkValueNames(def.alphaConverted(using: { _ in "%TEST_NAME" })) 35 | } 36 | } 37 | 38 | // Only allows %TEST_NAME as the value name 39 | func checkValueNames(_ i: InstructionDef) { 40 | var readingValueName = false 41 | var name = "" 42 | for c in i.description { 43 | if c == "%" { 44 | name = "%" 45 | assert(!readingValueName) 46 | readingValueName = true 47 | continue 48 | } 49 | guard readingValueName else { continue } 50 | if c.isLetter || c.isNumber || c == "_" { 51 | name.append(c) 52 | } else { 53 | XCTAssertEqual(name, "%TEST_NAME") 54 | readingValueName = false 55 | } 56 | } 57 | } 58 | 59 | public func testOperandsSeeAllValueNames() { 60 | for def in parsedInstructionDefs { 61 | var valueNames: [String] = [] 62 | let _ = def.instruction.alphaConverted(using: { valueNames.append($0);return $0 }) 63 | guard let operands = def.instruction.operands else { 64 | XCTFail("Failed to retrieve operands of \(def)") 65 | continue 66 | } 67 | XCTAssertEqual(operands.map { $0.value }, valueNames) 68 | } 69 | } 70 | 71 | public func testOperandsOfApplyInstructions() { 72 | func verifyApply(_ defSource: String, _ expectedOperands: [Operand]) { 73 | guard let partialApplyDef = parseDef(defSource) else { 74 | return XCTFail("Failed to parse the partial_apply case") 75 | } 76 | let partialApply = partialApplyDef.instruction 77 | guard let operands = partialApply.operands else { 78 | return XCTFail("Failed to get a correct list of operands") 79 | } 80 | XCTAssertEqual(Array(operands[1...]), expectedOperands) 81 | } 82 | verifyApply( 83 | "apply %8(%2, %6) : $@convention(thin) <τ_0_0, τ_0_1 where τ_0_0 : Strideable, τ_0_1 : Strideable> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_1) -> ()", 84 | [ 85 | Operand("%2", .attributedType([.inGuaranteed], .namedType("Int"))), 86 | Operand("%6", .attributedType([.inGuaranteed], .namedType("Int"))), 87 | ] 88 | ) 89 | verifyApply( 90 | "begin_apply %266(%125, %265) : $@yield_once @convention(method) (Int, @inout Array) -> @yields @inout Float", 91 | [ 92 | Operand("%125", .namedType("Int")), 93 | Operand( 94 | "%265", 95 | .attributedType( 96 | [.inout], .specializedType(.namedType("Array"), [.namedType("Float")]))), 97 | ] 98 | ) 99 | verifyApply( 100 | "%4 = partial_apply [callee_guaranteed] %2(%3) : $@convention(thin) <τ_0_0 where τ_0_0 : TensorFlowScalar> (@guaranteed Tensor<τ_0_0>) -> Bool", 101 | [ 102 | Operand( 103 | "%3", 104 | .attributedType( 105 | [.guaranteed], 106 | .specializedType(.namedType("Tensor"), [.namedType("Scalar")]))) 107 | ] 108 | ) 109 | } 110 | } 111 | 112 | extension SILAnalysisTests { 113 | public static let allTests = [ 114 | ("testAlphaConverted", testAlphaConverted), 115 | ("testOperandsSeeAllValueNames", testOperandsSeeAllValueNames), 116 | ("testOperandsOfApplyInstructions", testOperandsOfApplyInstructions), 117 | ] 118 | } 119 | -------------------------------------------------------------------------------- /Tests/SILTests/SILParserTests.swift: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The TensorFlow Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | import Foundation 16 | import XCTest 17 | @testable import SIL 18 | 19 | public final class SILParserTests: XCTestCase { 20 | public func testArrayDesugar() { 21 | let instr = "%149 = apply %148<[Int], PartialRangeFrom>(%143, %146, %144) : $@convention(method) <τ_0_0 where τ_0_0 : MutableCollection><τ_1_0 where τ_1_0 : RangeExpression, τ_0_0.Index == τ_1_0.Bound> (@in_guaranteed τ_1_0, @in_guaranteed τ_0_0) -> @out τ_0_0.SubSequence" 22 | let parser = SILParser(forString: instr) 23 | do { 24 | guard case let .operator(def) = try parser.parseInstructionDef() else { 25 | return XCTFail("Expected the result to be a non-terminating instruction") 26 | } 27 | XCTAssertEqual(def.description, instr.replacingOccurrences(of: "[Int]", with: "Array")) 28 | } catch { 29 | XCTFail("Failed to parse the instruction def: \(error)") 30 | } 31 | } 32 | 33 | public func testInstructionParseError() { 34 | let instr = "%122 = apply garbage..." 35 | let parser = SILParser(forString: instr) 36 | do { 37 | guard case let .operator(def) = try parser.parseInstructionDef() else { 38 | return XCTFail("Expected the result to be a non-terminating instruction") 39 | } 40 | guard case .unknown("apply") = def.operator else { 41 | return XCTFail("Expected .unknown(apply), got \(def.operator)") 42 | } 43 | } catch { 44 | XCTFail("Failed to parse the instruction def: \(error)") 45 | } 46 | } 47 | } 48 | 49 | extension SILParserTests { 50 | public static let allTests = [ 51 | ("testArrayDesugar", testArrayDesugar), 52 | ] 53 | } 54 | 55 | -------------------------------------------------------------------------------- /Tests/SILTests/TemporaryFile.swift: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The TensorFlow Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | import XCTest 16 | import Foundation 17 | 18 | extension FileManager { 19 | func makeTemporaryFile() -> URL? { 20 | let tmpDir: URL 21 | if #available(macOS 10.12, *) { 22 | tmpDir = self.temporaryDirectory 23 | } else { 24 | tmpDir = URL(fileURLWithPath: "/tmp", isDirectory: true) 25 | } 26 | let dirPath = tmpDir.appendingPathComponent("test.XXXXXX") 27 | return dirPath.withUnsafeFileSystemRepresentation { maybePath in 28 | guard let path = maybePath else { return nil } 29 | var mutablePath = Array(repeating: Int8(0), count: Int(PATH_MAX)) 30 | mutablePath.withUnsafeMutableBytes { mutablePathBufferPtr in 31 | mutablePathBufferPtr.baseAddress!.copyMemory( 32 | from: path, byteCount: Int(strlen(path)) + 1) 33 | } 34 | guard mkstemp(&mutablePath) != -1 else { return nil } 35 | return URL( 36 | fileURLWithFileSystemRepresentation: mutablePath, isDirectory: false, 37 | relativeTo: nil) 38 | } 39 | } 40 | } 41 | 42 | extension XCTestCase { 43 | func withTemporaryFile(f: (URL) -> ()) { 44 | guard let tmp = FileManager.default.makeTemporaryFile() else { 45 | return XCTFail("Failed to create temporary directory") 46 | } 47 | defer { try? FileManager.default.removeItem(atPath: tmp.path) } 48 | f(tmp) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Usage.md: -------------------------------------------------------------------------------- 1 | # Using Swift for TensorFlow 2 | 3 | This document explains basic usage of Swift for TensorFlow, including: 4 | * How to run Swift in Colaboratory 5 | * How to use the Swift interpreter and compiler 6 | * How to use Swift for TensorFlow with Xcode (**macOS only**) 7 | 8 | To see example models written using Swift for TensorFlow, go to [tensorflow/swift-models](https://github.com/tensorflow/swift-models). 9 | 10 | **Note:** Swift for TensorFlow is an early stage project. It has been released to enable open source development and is not yet ready for general use by machine learning developers. 11 | 12 | ## Colaboratory 13 | 14 | [Colaboratory](https://colab.research.google.com) is a free [Jupyter](https://jupyter.org/) notebook environment that requires no setup and runs entirely in the cloud. 15 | 16 | [Click here to create a Swift notebook in Colaboratory](https://colab.research.google.com/notebook#create=true&language=swift)! 17 | 18 | Put Swift code in the cell, and click the play button on the left of the cell (or hit Ctrl + Enter) to execute it. 19 | 20 | For examples of what you can do, visit [this tutorial](https://colab.research.google.com/github/tensorflow/swift/blob/main/docs/site/tutorials/model_training_walkthrough.ipynb) in Colab. 21 | 22 | ## Interpreter 23 | 24 | You must have a working toolchain for Swift for TensorFlow (`swift`, `swiftc`, etc) before proceeding with these instructions. If not, please [install Swift for TensorFlow](Installation.md) or [build from source](https://github.com/apple/swift/blob/tensorflow/README.md) before proceeding. 25 | 26 | With the Swift interpreter, you can use Swift like a scripting language. Create a file called `inference.swift` with your favorite text editor and paste the following: 27 | 28 | ```swift 29 | import TensorFlow 30 | 31 | struct MLPClassifier { 32 | var w1 = Tensor(repeating: 0.1, shape: [2, 4]) 33 | var w2 = Tensor(shape: [4, 1], scalars: [0.4, -0.5, -0.5, 0.4]) 34 | var b1 = Tensor([0.2, -0.3, -0.3, 0.2]) 35 | var b2 = Tensor([[0.4]]) 36 | 37 | func prediction(for x: Tensor) -> Tensor { 38 | let o1 = tanh(matmul(x, w1) + b1) 39 | return tanh(matmul(o1, w2) + b2) 40 | } 41 | } 42 | let input = Tensor([[0.2, 0.8]]) 43 | let classifier = MLPClassifier() 44 | let prediction = classifier.prediction(for: input) 45 | print(prediction) 46 | ``` 47 | 48 | Save `inference.swift` and navigate to its containing directory in the terminal. Then, run `swift -O inference.swift`. You should see something like: 49 | 50 | ```console 51 | $ swift -O inference.swift 52 | [[0.680704]] 53 | ``` 54 | 55 | **Note:** the `-O` flag enables Swift to run with optimizations. This is currently required for some programs that use the `TensorFlow` module to run properly. This will become unnecessary when the compiler implementation is completed. Check out the [FAQ](https://github.com/tensorflow/swift/blob/main/FAQ.md#why-do-i-get-error-array-input-is-not-a-constant-array-of-tensors) for more details. 56 | 57 | The Swift interpreter ran your program and printed the classifier's prediction, as expected. 58 | 59 | **Extra**: If your operating system supports multi-argument shebang lines, you can turn `inference.swift` into a directly-invokable script by adding the following line at the top of `inference.swift`: 60 | 61 | * Mac: `#!/usr/bin/env swift -O` 62 | * Ubuntu 16.04: `#!swift -O` 63 | 64 | Next, add executable permissions to `inference.swift`: 65 | 66 | ```console 67 | $ chmod +x inference.swift 68 | ``` 69 | 70 | You can now run `inference.swift` using `./inference.swift`: 71 | 72 | ```console 73 | $ ./inference.swift 74 | [[0.680704]] 75 | ``` 76 | 77 | If you get an error from running `./inference.swift` directly but not from `swift -O inference.swift`, it’s likely because your operating system doesn’t support multi-argument shebang lines. 78 | 79 | ## Compiler 80 | 81 | You must have a working toolchain for Swift for TensorFlow (`swift`, `swiftc`, etc) before proceeding with these instructions. If not, please [install Swift for TensorFlow](Installation.md) or [build from source](https://github.com/apple/swift/blob/tensorflow/README.md) before proceeding. 82 | 83 | With the Swift compiler, you can compile Swift programs into executable binaries. To try it, run the following: 84 | * Mac: ``swiftc -O -sdk `xcrun --show-sdk-path` inference.swift`` 85 | * Ubuntu: `swiftc -O inference.swift` 86 | 87 | `swiftc` should produce an executable in the current directory called `inference`. Run it to see the same result: 88 | 89 | ```console 90 | $ ./inference 91 | [[0.680704]] 92 | ``` 93 | 94 | This was a simple demonstration of Swift for TensorFlow. To see example models written using Swift for TensorFlow, go to [tensorflow/swift-models](https://github.com/tensorflow/swift-models). 95 | 96 | ## (Mac-only) Xcode 97 | 98 | Swift for TensorFlow provides an Xcode toolchain. Begin by installing it from [this page](Installation.md). 99 | 100 | Next, switch to the new toolchain. Open Xcode’s `Preferences`, navigate to `Components > Toolchains`, and select the installed Swift for TensorFlow toolchain. The name of the toolchain should start with "Swift for TensorFlow". 101 | 102 |

103 | Select toolchain in Xcode preferences. 104 |

105 | 106 | On macOS Catalina, `Verify Code Signature` for Swift for TensorFlow toolchains produces a code signature error. This prevents Xcode projects built using Swift for TensorFlow toolchains from running. To work around this issue, go to `Project Target Settings > Signing & Capabilities > + Capability > Hardened Runtime` and check `Disable Library Validation`. 107 | 108 |

109 | Enable \ 110 |

111 | 112 | Swift for TensorFlow does not officially support Xcode Playgrounds, and related bugs are tracked by [TF-500](https://bugs.swift.org/browse/TF-500). 113 | 114 | ## Visual Studio Code setup for Swift (only tested on Linux) 115 | 116 | 1. Install the [LLDB extension](https://marketplace.visualstudio.com/items?itemName=vadimcn.vscode-lldb). 117 | 2. Install the [Swift extension](https://marketplace.visualstudio.com/items?itemName=vknabel.vscode-swift-development-environment). 118 | 3. Build [SourceKit-LSP](https://github.com/apple/sourcekit-lsp#building-on-linux) from sources. 119 | 4. Search for swift-lsp-dev from the VS Code extensions view and install it. 120 | 121 | Steps 3 and 4 are only required for code outline and navigation, first two steps are sufficient for debugging with LLDB. There are two significant caveats: 122 | 123 | * Navigation doesn't work across multiple files. In other words, jumping to a definition in another file than the current one isn't possible. 124 | * Debugging sometimes stops spuriously while stepping through the code. Also, watch doesn't work with expressions. 125 | 126 | Debugging and outline workflows match the usual VS Code experience: 127 | 128 |

129 | Debugging Swift with VS Code. 130 |

131 | 132 |

133 | Debugging Swift with VS Code. 134 |

135 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # Swift for TensorFlow documentation 2 | 3 | This is the primary location of Swift for TensorFlow's documentation and tutorials. 4 | 5 | Formatted versions of the current guides, tutorials, and automatically generated API documentation 6 | can be found at [tensorflow.org/swift](https://www.tensorflow.org/swift). The original versions 7 | of the guides can be found [here](site/guide/). 8 | 9 | ## Tutorials ![](https://www.tensorflow.org/images/colab_logo_32px.png) 10 | 11 | Tutorial | Last Updated | 12 | -------- | ------------ | 13 | [A Swift Tour](https://colab.research.google.com/github/tensorflow/swift/blob/main/docs/site/tutorials/a_swift_tour.ipynb) | March 2019 14 | [Protocol-Oriented Programming & Generics](https://colab.research.google.com/github/tensorflow/swift/blob/main/docs/site/tutorials/protocol_oriented_generics.ipynb) | August 2019 15 | [Python Interoperability](https://colab.research.google.com/github/tensorflow/swift/blob/main/docs/site/tutorials/python_interoperability.ipynb) | March 2019 16 | [Custom Differentiation](https://colab.research.google.com/github/tensorflow/swift/blob/main/docs/site/tutorials/custom_differentiation.ipynb) | March 2019 17 | [Sharp Edges in Differentiability](https://colab.research.google.com/github/tensorflow/swift/blob/main/docs/site/tutorials/Swift_autodiff_sharp_edges.ipynb) | November 2020 18 | [Model Training Walkthrough](https://colab.research.google.com/github/tensorflow/swift/blob/main/docs/site/tutorials/model_training_walkthrough.ipynb) | March 2019 19 | [Raw TensorFlow Operators](https://colab.research.google.com/github/tensorflow/swift/blob/main/docs/site/tutorials/raw_tensorflow_operators.ipynb) | December 2019 20 | [Introducing X10, an XLA-Based Backend](https://colab.research.google.com/github/tensorflow/swift/blob/main/docs/site/tutorials/introducing_x10.ipynb) | May 2020 21 | 22 | ## Technology reference 23 | 24 | Many different technological directions have been explored over the lifetime of the project. An 25 | archive of reference guides, some now obsolete, can be found here: 26 | 27 | Document | Last Updated | Status | 28 | -------- | ------------ | ------ | 29 | [Swift Differentiable Programming Manifesto](https://github.com/apple/swift/blob/main/docs/DifferentiableProgramming.md) | January 2020 | Current 30 | [Swift Differentiable Programming Implementation Overview](https://docs.google.com/document/d/1_BirmTqdotglwNTOcYAW-ib6mx_jl-gH9Dbg4WmHZh0) | August 2019 | Current 31 | [Swift Differentiable Programming Design Overview](https://docs.google.com/document/d/1bPepWLfRQa6CtXqKA8CDQ87uZHixNav-TFjLSisuKag/edit?usp=sharing) | June 2019 | Outdated 32 | [Differentiable Types](DifferentiableTypes.md) | March 2019 | Outdated 33 | [Differentiable Functions and Differentiation APIs](DifferentiableFunctions.md) | March 2019 | Outdated 34 | [Dynamic Property Iteration using Key Paths](DynamicPropertyIteration.md) | March 2019 | Current 35 | [Hierarchical Parameter Iteration and Optimization](ParameterOptimization.md) | March 2019 | Current 36 | [First-Class Automatic Differentiation in Swift: A Manifesto](https://gist.github.com/rxwei/30ba75ce092ab3b0dce4bde1fc2c9f1d) | October 2018 | Outdated 37 | [Automatic Differentiation Whitepaper](AutomaticDifferentiation.md) | April 2018 | Outdated 38 | [Python Interoperability](PythonInteroperability.md) | April 2018 | Current 39 | [Graph Program Extraction](GraphProgramExtraction.md) | April 2018 | Outdated 40 | -------------------------------------------------------------------------------- /docs/SupportedBackends.md: -------------------------------------------------------------------------------- 1 | # Swift For Tensorflow Backends 2 | 3 | Accelerated calculations in Swift for TensorFlow are performed through the Tensor type. Currently, there are two options for how that acceleration is performed: eager mode or XLA compiler backed lazy tensor mode (X10). 4 | 5 | ## Eager Backend 6 | 7 | This is the default mode. Execution is eagerly performed an operation-by-operation basis using the [TensorFlow's 2.x eager execution](https://www.tensorflow.org/guide/eager) without creating graphs. 8 | 9 | The [eager backend](https://github.com/tensorflow/swift-apis/blob/master/Sources/TensorFlow/Bindings/EagerExecution.swift) supports CPUs and GPUs. It does *not* support TPUs. 10 | 11 | 12 | 13 | ## X10 (XLA Compiler Based) 14 | 15 | The [X10 backend](https://github.com/tensorflow/swift-apis/blob/master/Documentation/X10/API_GUIDE.md) is backed by [XLA](https://www.tensorflow.org/xla) and tensor operations are lazily evaluated. Operations are recorded in a graph until the results are needed. This allows for optimizations such as fusion into one graph. 16 | 17 | This backend provides improved performance over the eager backend in many cases. However, if the model changes shapes at each step, recompilation costs might outweigh the benefits. See the [X10 Troubleshooting Guide](https://github.com/tensorflow/swift-apis/blob/master/Documentation/X10/TROUBLESHOOTING.md) for more details. 18 | 19 | X10 supports CPUs, GPUs, and TPUs. 20 | 21 | ## Usage 22 | 23 | Check out this [Colab notebook](https://github.com/tensorflow/swift/blob/main/docs/site/tutorials/introducing_x10.ipynb) to learn how to switch between the eager and X10 backends. 24 | 25 | -------------------------------------------------------------------------------- /docs/images/AutomaticDifferentiation-Approaches.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tensorflow/swift/f0d6c74ef5d016046afc1eac0b07a2f6b74b8fdf/docs/images/AutomaticDifferentiation-Approaches.png -------------------------------------------------------------------------------- /docs/images/AutomaticDifferentiation-Compiler.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tensorflow/swift/f0d6c74ef5d016046afc1eac0b07a2f6b74b8fdf/docs/images/AutomaticDifferentiation-Compiler.png -------------------------------------------------------------------------------- /docs/images/AutomaticDifferentiation-ReverseAD.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tensorflow/swift/f0d6c74ef5d016046afc1eac0b07a2f6b74b8fdf/docs/images/AutomaticDifferentiation-ReverseAD.png -------------------------------------------------------------------------------- /docs/images/DesignOverview-AD.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tensorflow/swift/f0d6c74ef5d016046afc1eac0b07a2f6b74b8fdf/docs/images/DesignOverview-AD.png -------------------------------------------------------------------------------- /docs/images/DesignOverview-Pipeline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tensorflow/swift/f0d6c74ef5d016046afc1eac0b07a2f6b74b8fdf/docs/images/DesignOverview-Pipeline.png -------------------------------------------------------------------------------- /docs/images/GraphProgramExtraction-Graph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tensorflow/swift/f0d6c74ef5d016046afc1eac0b07a2f6b74b8fdf/docs/images/GraphProgramExtraction-Graph.png -------------------------------------------------------------------------------- /docs/images/Installation-XcodePreferences.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tensorflow/swift/f0d6c74ef5d016046afc1eac0b07a2f6b74b8fdf/docs/images/Installation-XcodePreferences.png -------------------------------------------------------------------------------- /docs/images/Usage-Playground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tensorflow/swift/f0d6c74ef5d016046afc1eac0b07a2f6b74b8fdf/docs/images/Usage-Playground.png -------------------------------------------------------------------------------- /docs/images/Usage-macOSCatalinaHardenedRuntime.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tensorflow/swift/f0d6c74ef5d016046afc1eac0b07a2f6b74b8fdf/docs/images/Usage-macOSCatalinaHardenedRuntime.png -------------------------------------------------------------------------------- /docs/images/VSCodeSwiftDebug.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tensorflow/swift/f0d6c74ef5d016046afc1eac0b07a2f6b74b8fdf/docs/images/VSCodeSwiftDebug.png -------------------------------------------------------------------------------- /docs/images/VSCodeSwiftOutline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tensorflow/swift/f0d6c74ef5d016046afc1eac0b07a2f6b74b8fdf/docs/images/VSCodeSwiftOutline.png -------------------------------------------------------------------------------- /docs/site/_book.yaml: -------------------------------------------------------------------------------- 1 | upper_tabs: 2 | - name: "Install" 3 | lower_tabs: 4 | guides: 5 | - include: /install/_toc.yaml 6 | 7 | - name: "Learn" 8 | path: /learn/ 9 | is_default: true 10 | menu: 11 | - include: /learn/_menu_toc.yaml 12 | lower_tabs: 13 | # Subsite tabs 14 | other: 15 | - name: Guide 16 | contents: 17 | - title: Overview 18 | path: /swift/guide/overview 19 | - heading: "Language additions" 20 | - title: Python interoperability 21 | path: /swift/guide/python_interoperability 22 | - title: Swift differentiable programming manifesto 23 | path: https://github.com/apple/swift/blob/main/docs/DifferentiableProgramming.md 24 | status: external 25 | - heading: "Accelerated computation" 26 | - title: Tensors 27 | path: /swift/guide/tensors 28 | - title: Accelerator backends 29 | path: /swift/guide/backends 30 | - title: Debugging X10 issues 31 | path: /swift/guide/debugging_x10 32 | - heading: "Machine learning models" 33 | - title: Layers and optimizers 34 | path: /swift/guide/layers 35 | - title: Datasets 36 | path: /swift/guide/datasets 37 | - title: Training loop 38 | path: /swift/guide/training_loop 39 | - title: Model checkpoints 40 | path: /swift/guide/checkpoints 41 | - title: Model summaries 42 | path: /swift/guide/model_summary 43 | - title: "Behind the scenes: WordSeg" 44 | path: https://docs.google.com/document/d/1NlFH0_89gB_qggtgzJIKYHL2xPI3IQjWjv18pnT1M0E 45 | status: external 46 | - title: Swift for TensorFlow model garden 47 | path: https://github.com/tensorflow/swift-models 48 | status: external 49 | - name: Tutorials 50 | contents: 51 | - title: "Model training walkthrough" 52 | path: /swift/tutorials/model_training_walkthrough 53 | - title: "A Swift tour" 54 | path: /swift/tutorials/a_swift_tour 55 | - title: "Protocol-Oriented Programming & Generics" 56 | path: /swift/tutorials/protocol_oriented_generics 57 | - title: "Python interoperability" 58 | path: /swift/tutorials/python_interoperability 59 | - title: "Custom differentiation" 60 | path: /swift/tutorials/custom_differentiation 61 | - title: "Using raw TensorFlow operators" 62 | path: /swift/tutorials/raw_tensorflow_operators 63 | - title: "Introducing X10, an XLA-based backend" 64 | path: /swift/tutorials/introducing_x10 65 | - name: API 66 | skip_translation: true 67 | contents: 68 | - title: Overview 69 | path: /swift/api_docs/ 70 | - title: View on GitHub 71 | path: https://github.com/tensorflow/swift-apis 72 | status: external 73 | - include: /swift/api_docs/_toc.yaml 74 | 75 | - include: /api_docs/_upper_tabs_api.yaml 76 | - include: /resources/_upper_tabs_resources.yaml 77 | - include: /_upper_tabs_right.yaml 78 | -------------------------------------------------------------------------------- /docs/site/guide/backends.md: -------------------------------------------------------------------------------- 1 | # Accelerator backends 2 | 3 | It's pretty straightforward to describe a `Tensor` calculation, but when and how that calculation 4 | is performed will depend on which backend is used for the `Tensor`s and when the results 5 | are needed on the host CPU. 6 | 7 | Behind the scenes, operations on `Tensor`s are dispatched to accelerators like GPUs or 8 | [TPUs](https://cloud.google.com/tpu), or run on the CPU when no accelerator is available. This 9 | happens automatically for you, and makes it easy to perform complex parallel calculations using 10 | a high-level interface. However, it can be useful to understand how this dispatch occurs and be 11 | able to customize it for optimal performance. 12 | 13 | Swift for TensorFlow has two backends for performing accelerated computation: TensorFlow eager mode 14 | and X10. The default backend is TensorFlow eager mode, but that can be overridden. An 15 | [interactive tutorial](https://colab.research.google.com/github/tensorflow/swift/blob/main/docs/site/tutorials/introducing_x10.ipynb) 16 | is available that walks you through the use of these different backends. 17 | 18 | ## TensorFlow eager mode 19 | 20 | The TensorFlow eager mode backend leverages 21 | [the TensorFlow C API](https://www.tensorflow.org/install/lang_c) to send each `Tensor` operation 22 | to a GPU or CPU as it is encountered. The result of that operation is then retrieved and passed on 23 | to the next operation. 24 | 25 | This operation-by-operation dispatch is straightforward to understand and requires no explicit 26 | configuration within your code. However, in many cases it does not result in optimal performance 27 | due to the overhead from sending off many small operations, combined with the lack of operation 28 | fusion and optimization that can occur when graphs of operations are present. Finally, TensorFlow eager mode is incompatible with TPUs, and can only be used with CPUs and GPUs. 29 | 30 | ## X10 (XLA-based tracing) 31 | 32 | X10 is the name of the Swift for TensorFlow backend that uses lazy tensor tracing and [the XLA 33 | optimizing compiler](https://www.tensorflow.org/xla) to in many cases significantly improve 34 | performance over operation-by-operation dispatch. Additionally, it adds compatibility for 35 | [TPUs](https://cloud.google.com/tpu), accelerators specifically optimized for the kinds of 36 | calculations found within machine learning models. 37 | 38 | The use of X10 for `Tensor` calculations is not the default, so you need to opt in to this backend. 39 | That is done by specifying that a `Tensor` is placed on an XLA device: 40 | 41 | ```swift 42 | let tensor1 = Tensor([0.0, 1.0, 2.0], on: Device.defaultXLA) 43 | let tensor2 = Tensor([1.5, 2.5, 3.5], on: Device.defaultXLA) 44 | ``` 45 | 46 | After that point, describing a calculation is exactly the same as for TensorFlow eager mode: 47 | 48 | ```swift 49 | let tensor3 = tensor1 + tensor2 50 | ``` 51 | 52 | Further detail can be provided when creating a `Tensor`, such as what kind of accelerator to use 53 | and even which one, if several are available. For example, a `Tensor` can be created on the second 54 | TPU device (assuming it is visible to the host the program is running on) using the following: 55 | 56 | ```swift 57 | let tpuTensor = Tensor([0.0, 1.0, 2.0], on: Device(kind: .TPU, ordinal: 1, backend: .XLA)) 58 | ``` 59 | 60 | No implicit movement of `Tensor`s between devices is performed, so if two `Tensor`s on different 61 | devices are used in an operation together, a runtime error will occur. To manually copy the 62 | contents of a `Tensor` to a new device, you can use the `Tensor(copying:to:)` initializer. Some 63 | larger-scale structures that contain `Tensor`s within them, like models and optimizers, have helper 64 | functions for moving all of their interior `Tensor`s to a new device in one step. 65 | 66 | Unlike TensorFlow eager mode, operations using the X10 backend are not individually dispatched as 67 | they are encountered. Instead, dispatching to an accelerator is only triggered by either reading 68 | calculated values back to the host or by placing an explicit barrier. The way this works is that 69 | the runtime starts from the value being read to the host (or the last calculation before a manual 70 | barrier) and traces the graph of calculations that result in that value. 71 | 72 | This traced graph is then converted to the XLA HLO intermediate representation and passed to the 73 | XLA compiler to be optimized and compiled for execution on the accelerator. From there, the entire 74 | calculation is sent to the accelerator and the end result obtained. 75 | 76 | Calculation is a time-consuming process, so X10 is best used with massively parallel calculations 77 | that are expressed via a graph and that are performed many times. Hash values and caching are used so that identical graphs are only compiled once for every unique configuration. 78 | 79 | For machine learning models, the training process often involves a loop where the model is 80 | subjected to the same series of calculations over and over. You'll want each of these passes to be 81 | seen as a repetition of the same trace, rather than one long graph with repeated units inside it. 82 | This is enabled by the manual insertion of a call to `LazyTensorBarrier()` function at the 83 | locations in your code where you wish for a trace to end. 84 | 85 | ### Mixed-precision support in X10 86 | 87 | Training with mixed precision via X10 is supported and both low-level and 88 | high-level API are provided to control it. The 89 | [low-level API](https://github.com/tensorflow/swift-apis/blob/main/Sources/TensorFlow/Core/MixedPrecision.swift) 90 | offers two computed properties: `toReducedPrecision` and `toFullPrecision` which 91 | convert between full and reduced precision, along with `isReducedPrecision` 92 | to query the precision. Besides `Tensor`s, models and optimizers can be converted 93 | between full and reduced precision using this API. 94 | 95 | Note that conversion to reduced precision doesn't change the logical type of a 96 | `Tensor`. If `t` is a `Tensor`, `t.toReducedPrecision` is also a 97 | `Tensor` with a reduced-precision underlying representation. 98 | 99 | As with devices, operations between tensors of different precisions are not 100 | allowed. This avoids silent and unwanted promotion to 32-bit floats, which would be hard 101 | to detect by the user. 102 | -------------------------------------------------------------------------------- /docs/site/guide/checkpoints.md: -------------------------------------------------------------------------------- 1 | # Model checkpoints 2 | 3 | The ability to save and restore the state of a model is vital for a number of applications, such 4 | as in transfer learning or for performing inference using pretrained models. Saving the 5 | parameters of a model (weights, biases, etc.) in a checkpoint file or directory is one way to 6 | accomplish this. 7 | 8 | This module provides a high-level interface for loading and saving 9 | [TensorFlow v2 format](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/core/util/tensor_bundle/tensor_bundle.h) 10 | checkpoints, as well as lower-level components that write to and read from this file format. 11 | 12 | 13 | ## Loading and saving simple models 14 | 15 | By conforming to the `Checkpointable` protocol, many simple models can be serialized to 16 | checkpoints without any additional code: 17 | 18 | ```swift 19 | import Checkpoints 20 | import ImageClassificationModels 21 | 22 | extension LeNet: Checkpointable {} 23 | 24 | var model = LeNet() 25 | 26 | ... 27 | 28 | try model.writeCheckpoint(to: directory, name: "LeNet") 29 | ``` 30 | and then that same checkpoint can be read by using: 31 | 32 | ```swift 33 | try model.readCheckpoint(from: directory, name: "LeNet") 34 | ``` 35 | This default implementation for model loading and saving will use a path-based naming 36 | scheme for each tensor in the model that is based on the names of the properties within the 37 | model structs. For example, the weights and biases within the first convolution in 38 | [the LeNet-5 model](https://github.com/tensorflow/swift-models/blob/main/Models/ImageClassification/LeNet-5.swift#L26) 39 | will be saved with the names `conv1/filter` and `conv1/bias`, respectively. When loading, 40 | the checkpoint reader will search for tensors with these names. 41 | 42 | ## Customizing model loading and saving 43 | 44 | If you want to have greater control over which tensors are saved and loaded, or the naming 45 | of those tensors, the `Checkpointable` protocol offers a few points of customization. 46 | 47 | To ignore properties on certain types, you can provide an implementation of 48 | `ignoredTensorPaths` on your model that returns a Set of strings in the form of 49 | `Type.property`. For example, to ignore the `scale` property on every Attention layer, you 50 | could return `["Attention.scale"]`. 51 | 52 | By default, a forward slash is used to separate each deeper level in a model. This can be 53 | customized by implementing `checkpointSeparator` on your model and providing a new 54 | string to use for this separator. 55 | 56 | Finally, for the greatest degree of customization in tensor naming, you can implement 57 | `tensorNameMap` and provide a function that maps from the default string name generated 58 | for a tensor in the model to a desired string name in the checkpoint. Most commonly, this 59 | will be used to interoperate with checkpoints generated with other frameworks, each of which 60 | have their own naming conventions and model structures. A custom mapping function gives 61 | the greatest degree of customization for how these tensors are named. 62 | 63 | Some standard helper functions are provided, like the default 64 | `CheckpointWriter.identityMap` (which simply uses the automatically generated tensor 65 | path name for checkpoints), or the `CheckpointWriter.lookupMap(table:)` function, 66 | which can build a mapping from a dictionary. 67 | 68 | For an example of how custom mapping can be accomplished, please see 69 | [the GPT-2 model](https://github.com/tensorflow/swift-models/blob/main/Models/Text/GPT2/CheckpointWriter.swift), 70 | which uses a mapping function to match the exact naming scheme used for OpenAI's 71 | checkpoints. 72 | 73 | ## The CheckpointReader and CheckpointWriter components 74 | 75 | For checkpoint writing, the extension provided by the `Checkpointable` protocol 76 | uses reflection and keypaths to iterate over a model's properties and generate a dictionary 77 | that maps string tensor paths to Tensor values. This dictionary is provided to an underlying 78 | `CheckpointWriter`, along with a directory in which to write the checkpoint. That 79 | `CheckpointWriter` handles the task of generating the on-disk checkpoint from that 80 | dictionary. 81 | 82 | The reverse of this process is reading, where a `CheckpointReader` is given the location of 83 | an on-disk checkpoint directory. It then reads from that checkpoint and forms a dictionary that 84 | maps the names of tensors within the checkpoint with their saved values. This dictionary is 85 | used to replace the current tensors in a model with the ones in this dictionary. 86 | 87 | For both loading and saving, the `Checkpointable` protocol maps the string paths to tensors 88 | to corresponding on-disk tensor names using the above-described mapping function. 89 | 90 | If the `Checkpointable` protocol lacks needed functionality, or more control is desired over 91 | the checkpoint loading and saving process, the `CheckpointReader` and 92 | `CheckpointWriter` classes can be used by themselves. 93 | 94 | ## The TensorFlow v2 checkpoint format 95 | 96 | The TensorFlow v2 checkpoint format, as briefly described in 97 | [this header](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/core/util/tensor_bundle/tensor_bundle.h), 98 | is the second generation format for TensorFlow model checkpoints. This second-generation 99 | format has been in use since late 2016, and has a number of improvements over the v1 100 | checkpoint format. TensorFlow SavedModels use v2 checkpoints within them to save model 101 | parameters. 102 | 103 | A TensorFlow v2 checkpoint consists of a directory with a structure like the following: 104 | 105 | ``` 106 | checkpoint/modelname.index 107 | checkpoint/modelname.data-00000-of-00002 108 | checkpoint/modelname.data-00001-of-00002 109 | ``` 110 | 111 | where the first file stores the metadata for the checkpoint and the remaining files are binary 112 | shards holding the serialized parameters for the model. 113 | 114 | The index metadata file contains the types, sizes, locations, and string names of all serialized 115 | tensors contained in the shards. That index file is the most structurally complex part of the 116 | checkpoint, and is based on `tensorflow::table`, which is itself based on SSTable / LevelDB. 117 | This index file is composed of a series of key-value pairs, where the keys are strings and the 118 | values are protocol buffers. The strings are sorted and prefix-compressed. For example: if 119 | the first entry is `conv1/weight` and next `conv1/bias`, the second entry only uses the 120 | `bias` part. 121 | 122 | This overall index file is sometimes compressed using 123 | [Snappy compression](https://github.com/google/snappy). The 124 | `SnappyDecompression.swift` file provides a native Swift implementation of Snappy 125 | decompression from a compressed Data instance. 126 | 127 | The index header metadata and tensor metadata are encoded as protocol buffers and 128 | encoded / decoded directly via [Swift Protobuf](https://github.com/apple/swift-protobuf). 129 | 130 | The `CheckpointIndexReader` and `CheckpointIndexWriter` classes handle loading 131 | and saving these index files as part of the overarching `CheckpointReader` and 132 | `CheckpointWriter` classes. The latter use the index files as basis for determining what to 133 | read from and write to the structurally simpler binary shards that contain the tensor data. 134 | -------------------------------------------------------------------------------- /docs/site/guide/datasets.md: -------------------------------------------------------------------------------- 1 | # Datasets 2 | 3 | In many machine learning models, especially for supervised learning, datasets are a vital part of 4 | the training process. Swift for TensorFlow provides wrappers for several common datasets within the 5 | Datasets module in the [the models repository](https://github.com/tensorflow/swift-models). These 6 | wrappers ease the use of common datasets with Swift-based models and integrate well with the 7 | Swift for TensorFlow's generalized training loop. 8 | 9 | ## Provided dataset wrappers 10 | 11 | These are the currently provided dataset wrappers within the models repository: 12 | 13 | - [BostonHousing](https://github.com/tensorflow/swift-models/tree/main/Datasets/BostonHousing) 14 | - [CIFAR-10](https://github.com/tensorflow/swift-models/tree/main/Datasets/CIFAR10) 15 | - [MS COCO](https://github.com/tensorflow/swift-models/tree/main/Datasets/COCO) 16 | - [CoLA](https://github.com/tensorflow/swift-models/tree/main/Datasets/CoLA) 17 | - [ImageNet](https://github.com/tensorflow/swift-models/tree/main/Datasets/Imagenette) 18 | - [Imagenette](https://github.com/tensorflow/swift-models/tree/main/Datasets/Imagenette) 19 | - [Imagewoof](https://github.com/tensorflow/swift-models/tree/main/Datasets/Imagenette) 20 | - [FashionMNIST](https://github.com/tensorflow/swift-models/tree/main/Datasets/MNIST) 21 | - [KuzushijiMNIST](https://github.com/tensorflow/swift-models/tree/main/Datasets/MNIST) 22 | - [MNIST](https://github.com/tensorflow/swift-models/tree/main/Datasets/MNIST) 23 | - [MovieLens](https://github.com/tensorflow/swift-models/tree/main/Datasets/MovieLens) 24 | - [Oxford-IIIT Pet](https://github.com/tensorflow/swift-models/tree/main/Datasets/OxfordIIITPets) 25 | - [WordSeg](https://github.com/tensorflow/swift-models/tree/main/Datasets/WordSeg) 26 | 27 | To use one of these dataset wrappers within a Swift 28 | project, add `Datasets` as a dependency to your Swift target and import the module: 29 | 30 | ```swift 31 | import Datasets 32 | ``` 33 | 34 | Most dataset wrappers are designed to produce randomly shuffled batches of labeled data. For 35 | example, to use the CIFAR-10 dataset, you first initialize it with the desired batch size: 36 | 37 | ```swift 38 | let dataset = CIFAR10(batchSize: 100) 39 | ``` 40 | 41 | On first use, the Swift for TensorFlow dataset wrappers will automatically download the original 42 | dataset for you, extract and parse all relevant archives, and then store the processed dataset in a 43 | user-local cache directory. Subsequent uses of the same dataset will load directly from the local 44 | cache. 45 | 46 | To set up a manual training loop involving this dataset, you'd use something like the following: 47 | 48 | ```swift 49 | for (epoch, epochBatches) in dataset.training.prefix(100).enumerated() { 50 | Context.local.learningPhase = .training 51 | ... 52 | for batch in epochBatches { 53 | let (images, labels) = (batch.data, batch.label) 54 | ... 55 | } 56 | } 57 | ``` 58 | 59 | The above sets up an iterator through 100 epochs (`.prefix(100)`), and returns the current epoch's 60 | numerical index and a lazily-mapped sequence over shuffled batches that make up that epoch. Within 61 | each training epoch, batches are iterated over and extracted for processing. In the case of the 62 | `CIFAR10` dataset wrapper, each batch is a 63 | [`LabeledImage`](https://github.com/tensorflow/swift-models/blob/main/Datasets/ImageClassificationDataset.swift) 64 | , which provides a `Tensor` containing all images from that batch and a `Tensor` with 65 | their matching labels. 66 | 67 | In the case of CIFAR-10, the entire dataset is small and can be loaded into memory at one time, but 68 | for other larger datasets batches are loaded lazily from disk and processed at the point where each 69 | batch is obtained. This prevents memory exhaustion with those larger datasets. 70 | 71 | ## The Epochs API 72 | 73 | Most of these dataset wrappers are built on a shared infrastructure that we've called the 74 | [Epochs API](https://github.com/tensorflow/swift-apis/tree/main/Sources/TensorFlow/Epochs). Epochs 75 | provides flexible components intended to support a wide variety of dataset types, from text to 76 | images and more. 77 | 78 | If you wish to create your own Swift dataset wrapper, you'll most likely want to use the Epochs API 79 | to do so. However, for common cases, such as image classification datasets, we highly recommend 80 | starting from a template based on one of the existing dataset wrappers and modifying that to meet 81 | your specific needs. 82 | 83 | As an example, let's examine the CIFAR-10 dataset wrapper and how it works. The core of the training 84 | dataset is defined here: 85 | 86 | ```swift 87 | let trainingSamples = loadCIFARTrainingFiles(in: localStorageDirectory) 88 | training = TrainingEpochs(samples: trainingSamples, batchSize: batchSize, entropy: entropy) 89 | .lazy.map { (batches: Batches) -> LazyMapSequence in 90 | return batches.lazy.map{ 91 | makeBatch(samples: $0, mean: mean, standardDeviation: standardDeviation, device: device) 92 | } 93 | } 94 | ``` 95 | 96 | The result from the `loadCIFARTrainingFiles()` function is an array of 97 | `(data: [UInt8], label: Int32)` tuples for each image in the training dataset. This is then provided 98 | to `TrainingEpochs(samples:batchSize:entropy:)` to create an infinite sequence of epochs with 99 | batches of `batchSize`. You can provide your own random number generator in cases where you may want 100 | deterministic batching behavior, but by default the `SystemRandomNumberGenerator` is used. 101 | 102 | From there, lazy maps over the batches culminate in the 103 | `makeBatch(samples:mean:standardDeviation:device:)` function. This is a custom function where the 104 | actual image processing pipeline for the CIFAR-10 dataset is located, so let's take a look at that: 105 | 106 | ```swift 107 | fileprivate func makeBatch( 108 | samples: BatchSamples, mean: Tensor?, standardDeviation: Tensor?, device: Device 109 | ) -> LabeledImage where BatchSamples.Element == (data: [UInt8], label: Int32) { 110 | let bytes = samples.lazy.map(\.data).reduce(into: [], +=) 111 | let images = Tensor(shape: [samples.count, 3, 32, 32], scalars: bytes, on: device) 112 | 113 | var imageTensor = Tensor(images.transposed(permutation: [0, 2, 3, 1])) 114 | imageTensor /= 255.0 115 | if let mean = mean, let standardDeviation = standardDeviation { 116 | imageTensor = (imageTensor - mean) / standardDeviation 117 | } 118 | 119 | let labels = Tensor(samples.map(\.label), on: device) 120 | return LabeledImage(data: imageTensor, label: labels) 121 | } 122 | ``` 123 | 124 | The two lines of this function concatenate all `data` bytes from the incoming `BatchSamples` into 125 | a `Tensor` that matches the byte layout of the images within the raw CIFAR-10 dataset. Next, 126 | the image channels are reordered to match those expected in our standard image classification models 127 | and the image data re-cast into a `Tensor` for model consumption. 128 | 129 | Optional normalization parameters can be provided to further adjust image channel values, a process 130 | that is common when training many image classification models. The normalization parameter `Tensor`s 131 | are created once at dataset initialization and then passed into `makeBatch()` as an optimization 132 | to prevent the repeated creation of small temporary tensors with the same values. 133 | 134 | Finally, the integer labels are placed in a `Tensor` and the image / label tensor pair 135 | returned in a `LabeledImage`. A `LabeledImage` is a specific case of 136 | [`LabeledData`](https://github.com/tensorflow/swift-models/blob/main/Support/LabeledData.swift), a 137 | struct with data and labels that conform to the Eppch API's 138 | [`Collatable`](https://github.com/tensorflow/swift-apis/blob/main/Sources/TensorFlow/Epochs/Collatable.swift) protocol. 139 | 140 | For more examples of the Epochs API in different dataset types, you can examine 141 | [the other dataset wrappers](https://github.com/tensorflow/swift-models/tree/main/Datasets) 142 | within the models repository. 143 | -------------------------------------------------------------------------------- /docs/site/guide/debugging_x10.md: -------------------------------------------------------------------------------- 1 | # Debugging X10 issues 2 | 3 | The X10 accelerator backend can provide significantly higher throughput for graph-based parallel 4 | computation, but its deferred tracing and just-in-time compilation can lead to non-obvious behavior 5 | sometimes. This might include frequent recompilation of traces due to graph or tensor shape changes, 6 | or huge graphs that lead to memory issues during compilation. 7 | 8 | One way to diagnose issues is to use the execution metrics and counters provided by 9 | X10. The first thing to check when a model is slow is to generate a metrics 10 | report. 11 | 12 | # Metrics 13 | 14 | To print a metrics report, add a `PrintX10Metrics()` call to your program: 15 | 16 | ```swift 17 | import TensorFlow 18 | 19 | ... 20 | PrintX10Metrics() 21 | ... 22 | ``` 23 | 24 | This will log various metrics and counters at the `INFO` level. 25 | 26 | ## Understanding the metrics report 27 | 28 | The report includes things like: 29 | 30 | - How many times we trigger XLA compilations and the total time spent on 31 | compilation. 32 | - How many times we launch an XLA computation and the total time spent on 33 | execution. 34 | - How many device data handles we create / destroy, etc. 35 | 36 | This information is reported in terms of percentiles of the samples. An example 37 | is: 38 | 39 | ``` 40 | Metric: CompileTime 41 | TotalSamples: 202 42 | Counter: 06m09s401ms746.001us 43 | ValueRate: 778ms572.062us / second 44 | Rate: 0.425201 / second 45 | Percentiles: 1%=001ms32.778us; 5%=001ms61.283us; 10%=001ms79.236us; 20%=001ms110.973us; 50%=001ms228.773us; 80%=001ms339.183us; 90%=001ms434.305us; 95%=002ms921.063us; 99%=21s102ms853.173us 46 | ``` 47 | 48 | We also provide counters, which are named integer variables which track internal 49 | software status. For example: 50 | 51 | ``` 52 | Counter: CachedSyncTensors 53 | Value: 395 54 | ``` 55 | 56 | ## Known caveats 57 | 58 | `Tensor`s backed by X10 behave semantically like default eager mode`Tensor`s. However, there are 59 | some performance and completeness caveats: 60 | 61 | 1. Degraded performance because of too many recompilations. 62 | 63 | XLA compilation is expensive. X10 automatically recompiles the graph every 64 | time new shapes are encountered, with no user intervention. Models need to 65 | see stabilized shapes within a few training steps and from that point no 66 | recompilation is needed. Additionally, the execution paths must stabilize 67 | quickly for the same reason: X10 recompiles when a new execution path is 68 | encountered. To sum up, in order to avoid recompilations: 69 | 70 | * Avoid highly variable dynamic shapes. However, a low number of different 71 | shapes could be fine. Pad tensors to fixed sizes when possible. 72 | * Avoid loops with different number of iterations between training steps. 73 | X10 currently unrolls loops, therefore different number of loop 74 | iterations translate into different (unrolled) execution paths. 75 | 76 | 2. A small number of operations aren't supported by X10 yet. 77 | 78 | We currently have a handful of operations which aren't supported, either 79 | because there isn't a good way to express them via XLA and static shapes 80 | (currently just `nonZeroIndices`) or lack of known use cases (several linear 81 | algebra operations and multinomial initialization). While the second 82 | category is easy to address as needed, the first category can only be 83 | addressed through interoperability with the CPU, non-XLA implementation. 84 | Using interoperability too often has significant performance implications 85 | because of host round-trips and fragmenting a fully fused model in multiple 86 | traces. Users are therefore advised to avoid using such operations in their 87 | models. 88 | 89 | On Linux, use `XLA_SAVE_TENSORS_FILE` (documented in the next section) to 90 | get the Swift stack trace which called the unsupported operation. Function 91 | names can be manually demangled using `swift-demangle`. 92 | 93 | 94 | # Obtaining and graphing traces 95 | 96 | If you suspect there are problems with the way graphs are being traced, or want to understand the 97 | tracing process, tools are provided to log out and visualize traces. You can have X10 log out the 98 | traces it finds by setting the `XLA_SAVE_TENSORS_FILE` environment variable: 99 | 100 | ```sh 101 | export XLA_SAVE_TENSORS_FILE=/home/person/TraceLog.txt 102 | ``` 103 | 104 | These trace logs come in three formats: `text`, `hlo`, and `dot`, with the format settable through 105 | the environment variable XLA_SAVE_TENSORS_FMT: 106 | 107 | ```sh 108 | export XLA_SAVE_TENSORS_FMT=text 109 | ``` 110 | 111 | When you run your application, the `text` representation that is logged out will show each 112 | individual trace in a high-level text notation used by X10. The `hlo` representation shows the 113 | intermediate representation that is passed to the XLA compiler. You may want to restrict the number 114 | of iterations within your training or calculation loops to prevent these logs from becoming too large. Also, each run of your application will append to this file, so you may wish to delete it 115 | between runs. 116 | 117 | Setting the variable `XLA_LOG_GRAPH_CHANGES` to 1 will also indicate within the trace log where 118 | changes in the graph have occurred. This is extremely helpful in finding places where recompilation 119 | will result. 120 | 121 | For a visual representation of a trace, the `dot` option will log out Graphviz-compatible graphs. If 122 | you extract the portion of a trace that looks like 123 | 124 | ``` 125 | digraph G { 126 | ... 127 | } 128 | ``` 129 | 130 | into its own file, Graphviz (assuming it is installed) can generate a visual diagram via 131 | 132 | ```sh 133 | dot -Tpng trace.dot -o trace.png 134 | ``` 135 | 136 | Note that setting the `XLA_SAVE_TENSORS_FILE` environment variable, especially when used in 137 | combination with `XLA_LOG_GRAPH_CHANGES` will have a substantial negative impact on performance. 138 | Only use these when debugging, and not for regular operation. 139 | 140 | # Additional environment variables 141 | 142 | Additional environment variables for debugging include: 143 | 144 | * `XLA_USE_BF16`: If set to 1, transforms all the `Float` values to BF16. 145 | Should only be used for debugging since we offer automatic mixed precision. 146 | 147 | * `XLA_USE_32BIT_LONG`: If set to 1, maps S4TF `Long` type to the XLA 32 bit 148 | integer type. On TPU, 64 bit integer computations are expensive, so setting 149 | this flag might help. Of course, the user needs to be certain that the 150 | values still fit in a 32 bit integer. 151 | -------------------------------------------------------------------------------- /docs/site/guide/layers.md: -------------------------------------------------------------------------------- 1 | # Layers 2 | 3 | Just as `Tensor` is our fundamental building block for accelerated parallel computation, most 4 | machine learning models and operations will be expressed in terms of the 5 | [`Layer`](https://github.com/tensorflow/swift-apis/blob/main/Sources/TensorFlow/Layer.swift) 6 | protocol. `Layer` defines an interface for types that take a differentiable input, process it, and 7 | produce a differentiable output. A `Layer` can contain state, such as trainable weights. 8 | 9 | `Layer` is a refinement of the `Module` protocol, with `Module` defining the more general case where 10 | the input to the type is not necessarily differentiable. Most components in a model will deal with 11 | differentiable inputs, but there are cases where types may need to conform to `Module` instead. 12 | 13 | If you create an operation has no trainable parameters within it, you'll want to define it in terms 14 | of `ParameterlessLayer` instead of `Layer`. 15 | 16 | Models themselves are often defined as `Layer`s, and are regularly composed of other `Layer`s. A 17 | model or subunit that has been defined as a `Layer` can be treated just like any other `Layer`, 18 | allowing for the construction of arbitarily complex models from other models or subunits. 19 | 20 | To define a custom `Layer` for a model or operation of your own, you generally will follow a 21 | template similar to this: 22 | 23 | ```swift 24 | public struct MyModel: Layer { 25 | // Define your layers or other properties here. 26 | 27 | // A custom initializer may be desired to configure the model. 28 | public init() {} 29 | 30 | @differentiable 31 | public func callAsFunction(_ input: Tensor) -> Tensor { 32 | // Define the sequence of operations performed on model input to arrive at the output. 33 | return ... 34 | } 35 | } 36 | ``` 37 | 38 | Trainable components of `Layers`, such as weights and biases, as well as other `Layer`s, can be 39 | declared as properties. A custom initializer is a good place to expose customizable parameters for 40 | a model, such as a variable numbers of layers or the output size of a classification model. 41 | Finally, the core of the `Layer` is `callAsFunction()`, where you will define the types for the 42 | input and output as well as the transformation that takes in one and returns the other. 43 | 44 | ## Built-in layers 45 | 46 | Many common machine learning operations have been encapsulated as `Layer`s for you to use when 47 | defining models or subunits. The following is a list of the layers provided by Swift for TensorFlow, 48 | grouped by functional areas: 49 | 50 | ### Augmentation 51 | 52 | - [AlphaDropout](https://www.tensorflow.org/swift/api_docs/Structs/AlphaDropout) 53 | - [Dropout](https://www.tensorflow.org/swift/api_docs/Structs/Dropout) 54 | - [GaussianDropout](https://www.tensorflow.org/swift/api_docs/Structs/GaussianDropout) 55 | - [GaussianNoise](https://www.tensorflow.org/swift/api_docs/Structs/GaussianNoise) 56 | 57 | ### Convolution 58 | 59 | - [Conv1D](https://www.tensorflow.org/swift/api_docs/Structs/Conv1D) 60 | - [Conv2D](https://www.tensorflow.org/swift/api_docs/Structs/Conv2D) 61 | - [Conv3D](https://www.tensorflow.org/swift/api_docs/Structs/Conv3D) 62 | - [Dense](https://www.tensorflow.org/swift/api_docs/Structs/Dense) 63 | - [DepthwiseConv2D](https://www.tensorflow.org/swift/api_docs/Structs/DepthwiseConv2D) 64 | - [SeparableConv1D](https://www.tensorflow.org/swift/api_docs/Structs/SeparableConv1D) 65 | - [SeparableConv2D](https://www.tensorflow.org/swift/api_docs/Structs/SeparableConv2D) 66 | - [TransposedConv1D](https://www.tensorflow.org/swift/api_docs/Structs/TransposedConv1D) 67 | - [TransposedConv2D](https://www.tensorflow.org/swift/api_docs/Structs/TransposedConv2D) 68 | - [TransposedConv3D](https://www.tensorflow.org/swift/api_docs/Structs/TransposedConv3D) 69 | - [ZeroPadding1D](https://www.tensorflow.org/swift/api_docs/Structs/ZeroPadding1D) 70 | - [ZeroPadding2D](https://www.tensorflow.org/swift/api_docs/Structs/ZeroPadding2D) 71 | - [ZeroPadding3D](https://www.tensorflow.org/swift/api_docs/Structs/ZeroPadding3D) 72 | 73 | ### Embedding 74 | 75 | - [Embedding](https://www.tensorflow.org/swift/api_docs/Structs/Embedding) 76 | 77 | ### Morphological 78 | 79 | - [Dilation2D](https://www.tensorflow.org/swift/api_docs/Structs/Dilation2D) 80 | - [Erosion2D](https://www.tensorflow.org/swift/api_docs/Structs/Erosion2D) 81 | 82 | ### Normalization 83 | 84 | - [BatchNorm](https://www.tensorflow.org/swift/api_docs/Structs/BatchNorm) 85 | - [LayerNorm](https://www.tensorflow.org/swift/api_docs/Structs/LayerNorm) 86 | - [GroupNorm](https://www.tensorflow.org/swift/api_docs/Structs/GroupNorm) 87 | - [InstanceNorm](https://www.tensorflow.org/swift/api_docs/Structs/InstanceNorm) 88 | 89 | ### Pooling 90 | 91 | - [AvgPool1D](https://www.tensorflow.org/swift/api_docs/Structs/AvgPool1D) 92 | - [AvgPool2D](https://www.tensorflow.org/swift/api_docs/Structs/AvgPool2D) 93 | - [AvgPool3D](https://www.tensorflow.org/swift/api_docs/Structs/AvgPool3D) 94 | - [MaxPool1D](https://www.tensorflow.org/swift/api_docs/Structs/MaxPool1D) 95 | - [MaxPool2D](https://www.tensorflow.org/swift/api_docs/Structs/MaxPool2D) 96 | - [MaxPool3D](https://www.tensorflow.org/swift/api_docs/Structs/MaxPool3D) 97 | - [FractionalMaxPool2D](https://www.tensorflow.org/swift/api_docs/Structs/FractionalMaxPool2D) 98 | - [GlobalAvgPool1D](https://www.tensorflow.org/swift/api_docs/Structs/GlobalAvgPool1D) 99 | - [GlobalAvgPool2D](https://www.tensorflow.org/swift/api_docs/Structs/GlobalAvgPool2D) 100 | - [GlobalAvgPool3D](https://www.tensorflow.org/swift/api_docs/Structs/GlobalAvgPool3D) 101 | - [GlobalMaxPool1D](https://www.tensorflow.org/swift/api_docs/Structs/GlobalMaxPool1D) 102 | - [GlobalMaxPool2D](https://www.tensorflow.org/swift/api_docs/Structs/GlobalMaxPool2D) 103 | - [GlobalMaxPool3D](https://www.tensorflow.org/swift/api_docs/Structs/GlobalMaxPool3D) 104 | 105 | ### Recurrent neural networks 106 | 107 | - [BasicRNNCell](https://www.tensorflow.org/swift/api_docs/Structs/BasicRNNCell) 108 | - [LSTMCell](https://www.tensorflow.org/swift/api_docs/Structs/LSTMCell) 109 | - [GRUCell](https://www.tensorflow.org/swift/api_docs/Structs/GRUCell) 110 | - [RecurrentLayer](https://www.tensorflow.org/swift/api_docs/Structs/RecurrentLayer) 111 | - [BidirectionalRecurrentLayer](https://www.tensorflow.org/swift/api_docs/Structs/BidirectionalRecurrentLayer) 112 | 113 | ### Reshaping 114 | 115 | - [Flatten](https://www.tensorflow.org/swift/api_docs/Structs/Flatten) 116 | - [Reshape](https://www.tensorflow.org/swift/api_docs/Structs/Reshape) 117 | 118 | ### Upsampling 119 | 120 | - [UpSampling1D](https://www.tensorflow.org/swift/api_docs/Structs/UpSampling1D) 121 | - [UpSampling2D](https://www.tensorflow.org/swift/api_docs/Structs/UpSampling2D) 122 | - [UpSampling3D](https://www.tensorflow.org/swift/api_docs/Structs/UpSampling3D) 123 | 124 | # Optimizers 125 | 126 | Optimizers are a key component of the training of a machine learning model, updating the model 127 | based on a calculated gradient. These updates ideally will adjust the parameters of a model in such 128 | a way as to train the model. 129 | 130 | To use an optimizer, first initialize it for a target model with appropriate training parameters: 131 | 132 | ```swift 133 | let optimizer = RMSProp(for: model, learningRate: 0.0001, decay: 1e-6) 134 | ``` 135 | 136 | Train a model by obtaining a gradient with respect to input and a loss function, and then update the 137 | model along that gradient using your optimizer: 138 | 139 | ```swift 140 | optimizer.update(&model, along: gradients) 141 | ``` 142 | 143 | ## Built-in optimizers 144 | 145 | Several common optimizers are provided by Swift for TensorFlow. These include the following: 146 | 147 | - [SGD](https://www.tensorflow.org/swift/api_docs/Classes/SGD) 148 | - [RMSProp](https://www.tensorflow.org/swift/api_docs/Classes/RMSProp) 149 | - [AdaGrad](https://www.tensorflow.org/swift/api_docs/Classes/AdaGrad) 150 | - [AdaDelta](https://www.tensorflow.org/swift/api_docs/Classes/AdaDelta) 151 | - [Adam](https://www.tensorflow.org/swift/api_docs/Classes/Adam) 152 | - [AdaMax](https://www.tensorflow.org/swift/api_docs/Classes/AdaMax) 153 | - [AMSGrad](https://www.tensorflow.org/swift/api_docs/Classes/AMSGrad) 154 | - [RAdam](https://www.tensorflow.org/swift/api_docs/Classes/RAdam) 155 | -------------------------------------------------------------------------------- /docs/site/guide/model_summary.md: -------------------------------------------------------------------------------- 1 | # Model Summaries 2 | 3 | A summary provides details about the architecture of a model, such as layer 4 | types and shapes. 5 | 6 | The design proposal can be found [here][design]. This 7 | implementation is a WIP, so please file an [Issue][new_issue] with 8 | enhancements you would like to see or problems you run into. 9 | 10 | **Note:** Model summaries are currently supported on the X10 backend only. 11 | 12 | ## Viewing a model summary 13 | 14 | Create an X10 device and model. 15 | 16 | ``` 17 | import TensorFlow 18 | 19 | public struct MyModel: Layer { 20 | public var dense1 = Dense(inputSize: 1, outputSize: 1) 21 | public var dense2 = Dense(inputSize: 4, outputSize: 4) 22 | public var dense3 = Dense(inputSize: 4, outputSize: 4) 23 | public var flatten = Flatten() 24 | 25 | @differentiable 26 | public func callAsFunction(_ input: Tensor) -> Tensor { 27 | let layer1 = dense1(input) 28 | let layer2 = layer1.reshaped(to: [1, 4]) 29 | let layer3 = dense2(layer2) 30 | let layer4 = dense3(layer3) 31 | return flatten(layer4) 32 | } 33 | } 34 | 35 | let device = Device.defaultXLA 36 | let model0 = MyModel() 37 | let model = MyModel(copying: model0, to: device) 38 | ``` 39 | 40 | Create an input tensor. 41 | 42 | ``` 43 | let input = Tensor(repeating: 1, shape: [1, 4, 1, 1], on: device) 44 | ``` 45 | 46 | Generate a summary of your model. 47 | 48 | ``` 49 | let summary = model.summary(input: input) 50 | print(summary) 51 | ``` 52 | 53 | ``` 54 | Layer Output Shape Attributes 55 | =============================== ==================== ====================== 56 | Dense [1, 4, 1, 1] 57 | Dense [1, 4] 58 | Dense [1, 4] 59 | Flatten [1, 4] 60 | ``` 61 | 62 | **Note:** the `summary()` function executes the model in order to obtain 63 | details about its architecture. 64 | 65 | 66 | [design]: https://docs.google.com/document/d/1hEhMiwLtuzsN3RvIC3FAh6NvtTimU8o_qdzMkGvntVg/view 67 | [new_issue]: https://github.com/tensorflow/swift-apis/issues/new 68 | -------------------------------------------------------------------------------- /docs/site/guide/overview.md: -------------------------------------------------------------------------------- 1 | # Swift for TensorFlow (In Archive Mode) 2 | 3 | Swift for TensorFlow was an experiment in the next-generation platform for machine learning, incorporating the latest research across machine learning, compilers, differentiable programming, systems design, and beyond. It was archived in February 2021. Some significant achievements from this project include: 4 | 5 | * Added [language-integrated differentiable programming](https://forums.swift.org/t/differentiable-programming-for-gradient-based-machine-learning/42147) into the Swift language. This work continues in the official Swift compiler. 6 | * Developed a mutable-value-semantics-oriented [deep learning API](https://github.com/tensorflow/swift-apis) 7 | * Fostered the development of [a model garden](https://github.com/tensorflow/swift-models) with more than [30 models from a variety of deep learning disciplines](https://github.com/tensorflow/swift-models#examples). 8 | * Enabled novel research that [combines deep learning with probabilistic graphical models](https://github.com/borglab/SwiftFusion) for 3D motion tracking and beyond. 9 | * Powered a(n almost) pure-Swift prototype of a [GPU+CPU runtime supporting pmap](https://github.com/ewconnell/swiftrt). 10 | * Spun off multiple open source side efforts which continue to be under active development: 11 | * [PythonKit](https://github.com/pvieito/PythonKit): Python interoperability with Swift. 12 | * [swift-jupyter](https://github.com/google/swift-jupyter): Enables use of Swift within Jupyter notebooks. 13 | * [swift-benchmark](https://github.com/google/swift-benchmark): Provides a robust benchmarking suite for Swift code. 14 | * Spun off several other open source efforts: 15 | * [penguin](https://github.com/saeta/penguin): Parallel programming, data structures, graph algorithms, and more. 16 | * [Tensors Fitting Perfectly](https://github.com/google-research/swift-tfp): Static analysis of tensor shape mismatches. 17 | * Swift-evolution proposals proposed and implemented: 18 | * [SE-0195](https://github.com/apple/swift-evolution/blob/main/proposals/0195-dynamic-member-lookup.md): User-defined "Dynamic Member Lookup" Types 19 | * [SE-0216](https://github.com/apple/swift-evolution/blob/main/proposals/0216-dynamic-callable.md): Introduce user-defined dynamically "callable" types 20 | * [SE-0233](https://github.com/apple/swift-evolution/blob/main/proposals/0233-additive-arithmetic-protocol.md): Make Numeric Refine a new AdditiveArithmetic Protocol 21 | * [SE-0253](https://github.com/apple/swift-evolution/blob/main/proposals/0253-callable.md): Callable values of user-defined nominal types 22 | 23 | This site will not receive further updates. The API documentation and binary downloads will continue to be accessible as well as the [Open Design Review meeting recordings](https://docs.google.com/document/d/1Fm56p5rV1t2Euh6WLtBFKGqI43ozC3EIjReyLk-LCLU/edit). 24 | 25 | ## Swift 26 | 27 | Swift is an [open source](https://swift.org/) general-purpose programming language, which has a 28 | large and growing user base. We chose Swift because it has an 29 | [open language design process](https://github.com/apple/swift-evolution) and for specific technical 30 | reasons detailed in the 31 | "[Why *Swift* for TensorFlow](https://github.com/BradLarson/swift/blob/main/docs/WhySwiftForTensorFlow.md)" 32 | document. We assume that most readers are unfamiliar with it, so we’ll briefly touch on some 33 | additional important things about it here. 34 | 35 | The development of Swift started in 2010, and aimed to bring the best practices in programming 36 | language design together into one system rather than trying for academic novelty or to religiously 37 | propagate programming methodologies. As a result, it supports multi-paradigm development (e.g. 38 | functional, OOP, generic, procedural, etc) all in one system, and brings many well-known concepts 39 | from academic languages (e.g. 40 | [pattern matching](http://alisoftware.github.io/swift/pattern-matching/2016/03/27/pattern-matching-1/), 41 | [algebraic data types](https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Enumerations.html), 42 | and type classes) into the forefront. Instead of strongly encouraging developers to rewrite all 43 | their code in Swift, it pragmatically focuses on interoperability with other languages, e.g., 44 | allowing you to directly import C header files and use them without an 45 | [FFI](https://en.wikipedia.org/wiki/Foreign_function_interface) and (now) the ability to use Python 46 | APIs without wrappers. 47 | 48 | Swift has the audacious goal of spanning all the way from low-level systems programming to 49 | high-level scripting, with a focus on being 50 | [easy to learn and use](https://www.apple.com/swift/playgrounds/). 51 | Because Swift needs to be easy to learn and use but also powerful, it relies on the principle of 52 | [progressive disclosure of complexity](https://www.nngroup.com/articles/progressive-disclosure/), 53 | which aggressively factors the cost of complexity onto the people who benefit from that complexity. 54 | The "scripting language feel" combined with high performance is very useful for machine learning. 55 | 56 | A final pertinent aspect of the design of Swift is that much of the Swift language is actually 57 | implemented in its standard library. "Builtin" types like 58 | [Int](https://developer.apple.com/documentation/swift/int) and 59 | [Bool](https://developer.apple.com/documentation/swift/bool) are actually just structs defined in 60 | the standard library that wrap magic types and operations. As such, sometimes we joke that Swift 61 | is just "syntactic sugar for LLVM". 62 | 63 | There is a lot more that is cool about Swift and a ton of content available online. If you are 64 | interested in learning more about general Swift programming concepts, here are a few links to get 65 | started: 66 | 67 | - [A Swift Tour](https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/GuidedTour.html) is a skimmable tour of the high level syntax and feel of Swift, and is part of the larger "The Swift Programming Language" book. 68 | - Value semantics are powerful and play an important role in Swift code, as explained in "[Building Better Apps with Value Types in Swift](https://developer.apple.com/videos/play/wwdc2015/414/)" [[YouTube](https://www.youtube.com/watch?v=av4i3x-aZbM)]. 69 | - Swift supports classic OOP, but has adapted ideas from the Haskell type system. This is explained in "[Protocol-Oriented Programming in Swift](https://developer.apple.com/videos/play/wwdc2015/408/)" [[YouTube](https://www.youtube.com/watch?v=g2LwFZatfTI)]. 70 | 71 | One warning: Swift evolved rapidly in its early years, so you should be careful with anything 72 | before Swift 3 (released in 2016). 73 | 74 | ## Why Swift for TensorFlow? 75 | 76 | Swift for TensorFlow is a new way to develop machine learning models. It 77 | gives you the power of 78 | [TensorFlow](https://www.tensorflow.org) directly integrated into the 79 | [Swift programming language](https://swift.org/about). We believe that 80 | machine learning paradigms are so important that they deserve 81 | **first-class language and compiler support**. 82 | 83 | A fundamental primitive in machine learning is gradient-based optimization: 84 | computing function derivatives to optimize parameters. With Swift for 85 | TensorFlow, you can easily differentiate functions using differential 86 | operators like [`gradient(of:)`](https://www.tensorflow.org/swift/api_docs/Functions#/s:10TensorFlow8gradient2of15CotangentVectorQzxcq_xc_tAA14DifferentiableRzSFR_AaFR_AdaFPQy_Rs_r0_lF), or differentiate with respect to an entire 87 | model by calling method [`gradient(in:)`](https://www.tensorflow.org/swift/api_docs/Protocols/Differentiable#/s:10TensorFlow14DifferentiablePAAE8gradient2in15CotangentVectorQzqd__xXE_tSFRd__AaBRd__AfCQyd__Rsd__lF). These differentiation APIs 88 | are not just available for `Tensor`-related concepts—they are 89 | generalized for all types that conform to the [`Differentiable`](https://www.tensorflow.org/swift/api_docs/Protocols/Differentiable) 90 | protocol, including `Float`, `Double`, SIMD vectors, and your own data 91 | structures. 92 | 93 | ```swift 94 | // Custom differentiable type. 95 | struct Model: Differentiable { 96 | var w: Float 97 | var b: Float 98 | func applied(to input: Float) -> Float { 99 | return w * input + b 100 | } 101 | } 102 | 103 | // Differentiate using `gradient(at:_:in:)`. 104 | let model = Model(w: 4, b: 3) 105 | let input: Float = 2 106 | let (𝛁model, 𝛁input) = gradient(at: model, input) { model, input in 107 | model.applied(to: input) 108 | } 109 | 110 | print(𝛁model) // Model.TangentVector(w: 2.0, b: 1.0) 111 | print(𝛁input) // 4.0 112 | ``` 113 | 114 | Beyond derivatives, the Swift for TensorFlow project comes with a sophisticated toolchain 115 | to make users more productive. You can run Swift interactively in a Jupyter 116 | notebook, and get helpful autocomplete suggestions to help you explore the 117 | massive API surface of a modern deep learning library. You can [get started 118 | right in your browser in 119 | seconds](https://colab.research.google.com/github/tensorflow/swift/blob/main/docs/site/tutorials/model_training_walkthrough.ipynb)! 120 | 121 | Migrating to Swift for TensorFlow is really easy thanks to Swift's powerful 122 | Python integration. You can incrementally migrate your Python code over (or 123 | continue to use your favorite Python libraries), because you can easily call 124 | your favorite Python library with a familiar syntax: 125 | 126 | ```swift 127 | import TensorFlow 128 | import Python 129 | 130 | let np = Python.import("numpy") 131 | 132 | let array = np.arange(100).reshape(10, 10) // Create a 10x10 numpy array. 133 | let tensor = Tensor(numpy: array) // Seamless integration! 134 | ``` 135 | -------------------------------------------------------------------------------- /docs/site/guide/tensors.md: -------------------------------------------------------------------------------- 1 | # Tensor overview 2 | 3 | The Swift for TensorFlow APIs use the `Tensor` type as the primary means for performing accelerated 4 | computation. A `Tensor` represents a multidimensional array of values, and operations on `Tensor`s 5 | are automatically dispatched to available accelerators using one of two backends. 6 | 7 | A `Tensor` is [generic](https://docs.swift.org/swift-book/LanguageGuide/Generics.html) about the 8 | type of the values it contains. The type of these values must conform to `TensorFlowScalar`, with 9 | common types being `Float`, `Int32`, and `Bool`. For example, to initialize two `Float`-containing 10 | `Tensor`s with some predetermined values, you could do the following: 11 | 12 | ```swift 13 | let tensor1 = Tensor([0.0, 1.0, 2.0]) 14 | let tensor2 = Tensor([1.5, 2.5, 3.5]) 15 | ``` 16 | 17 | If you had left out the `` type parameter, Swift would infer a type of `Tensor`. 18 | `Double` is the default type for floating-point literals in Swift. `Float` values tend to be more 19 | common in machine learning calculations, so we're using that here. 20 | 21 | Many common operators work on `Tensor`s. For example, to add two of them and obtain the result, you 22 | can do the following: 23 | 24 | ```swift 25 | let tensor3 = tensor1 + tensor2 26 | ``` 27 | 28 | The full list of operations you can perform on a `Tensor` is available in 29 | [the API documentation](https://www.tensorflow.org/swift/api_docs/Structs/Tensor). 30 | 31 | ## `_Raw` operations 32 | 33 | `Tensor` operations are backed by two different means of working with accelerators, yet they have 34 | a unified high-level interface. Under the hood, `_Raw` operations are defined that either dispatch 35 | to `_RawXLA` or `_RawTFEager` versions, depending on the backend used for the `Tensor`s in 36 | question. These [`_Raw` bindings](https://github.com/tensorflow/swift-apis/tree/main/Sources/TensorFlow/Bindings) 37 | to TensorFlow or X10 are automatically generated. 38 | 39 | Normally, you would not need to interact with `_Raw` operations directly. Idiomatic Swift interfaces 40 | have been constructed on top of these, and that's how you typically will perform `Tensor` 41 | calculations. 42 | 43 | However, not all underlying TensorFlow operations have matching Swift interfaces, so 44 | you may occasionally need to access `_Raw` operators in your code. If you need to do so, an 45 | [interactive tutorial](https://colab.research.google.com/github/tensorflow/swift/blob/main/docs/site/tutorials/raw_tensorflow_operators.ipynb) 46 | is available to demonstrate how this works. -------------------------------------------------------------------------------- /docs/site/guide/training_loop.md: -------------------------------------------------------------------------------- 1 | # Training loop 2 | 3 | When training a machine learning model, it's common to have a loop where training data is ingested 4 | (or generated), batches run through a model, gradients obtained, and the model updated via an 5 | optimizer. While you can write a training loop of your own for each training application, 6 | Swift for TensorFlow provides an experimental training loop abstraction that may simplify this 7 | process. 8 | 9 | The [`TrainingLoop`](https://github.com/tensorflow/swift-models/tree/main/TrainingLoop) module 10 | within [the models repository](https://github.com/tensorflow/swift-models) contains the current 11 | version of this experimental generalized training loop. It is structured in such a way as to 12 | integrate with dataset wrappers that conform to the Epochs API for easy data ingestion, and to 13 | automate the interaction of models, datasets, and optimizers with accelerator backends to achieve 14 | optimal performance. Heavy customization of the training process can be achieved through the use 15 | of callbacks. 16 | 17 | Most image-based examples in the model repository have been converted to use this training loop 18 | abstraction, as well as the supervised text model training examples. However, the training loop may 19 | not be appropriate in its current design for all machine learning models. 20 | 21 | The implementation of Swift for TensorFlow's generalized training loop is heavily influenced by 22 | [fastai's Learner](https://docs.fast.ai/learner.html). For more on their design, please refer to 23 | ["fastai: A Layered API for Deep Learning"](https://arxiv.org/abs/2002.04688) and Sylvain Gugger's 24 | presentation 25 | ["Fast.ai - An infinitely customizable training loop"](https://www.youtube.com/watch?v=roc-dOSeehM). 26 | 27 | ## Usage 28 | 29 | The [ResNet-CIFAR10](https://github.com/tensorflow/swift-models/tree/main/Examples/ResNet-CIFAR10) 30 | example provides a good demonstration of how to use this training loop in practice. First, import 31 | the module: 32 | 33 | ```swift 34 | import TrainingLoop 35 | ``` 36 | 37 | then choose an accelerator backend by setting up a `Device`. In this case, we'll select the X10 38 | XLA-based backend and use the first available accelerator: 39 | 40 | ```swift 41 | let device = Device.defaultXLA 42 | ``` 43 | 44 | The next step is to configure the dataset, model, and optimizer to use with your training loop: 45 | 46 | ```swift 47 | let dataset = CIFAR10(batchSize: 10, on: device) 48 | var model = ResNet(classCount: 10, depth: .resNet56, downsamplingInFirstStage: false) 49 | var optimizer = SGD(for: model, learningRate: 0.001) 50 | ``` 51 | 52 | and then set up the training loop: 53 | 54 | ```swift 55 | var trainingLoop = TrainingLoop( 56 | training: dataset.training, 57 | validation: dataset.validation, 58 | optimizer: optimizer, 59 | lossFunction: softmaxCrossEntropy, 60 | metrics: [.accuracy]) 61 | ``` 62 | 63 | The training loop assumes that the dataset you're using conforms to the Epochs API, and allows you 64 | to specify which splits within the dataset to use for training and validation. Any loss function 65 | can be used once placed into a compatible wrapper, such as `softmaxCrossEntropy` is 66 | [here](https://github.com/tensorflow/swift-models/blob/main/TrainingLoop/LossFunctions.swift). 67 | 68 | The current metrics that can be captured include: 69 | 70 | - `loss` 71 | - `accuracy` 72 | - `top5Accuracy` 73 | - `matthewsCorrelationCoefficient` 74 | - `perplexity` 75 | 76 | Finally, to perform training, you call the following: 77 | 78 | ```swift 79 | try! trainingLoop.fit(&model, epochs: 10, on: device) 80 | ``` 81 | 82 | This will train the model for 10 epochs using the accelerator backend we specified. Statistics will 83 | be displayed during training to the console using an animated prompt. 84 | 85 | ## Callbacks 86 | 87 | Customization of this generalized training loop occurs via the use of callbacks. These callbacks can 88 | be hooked into various points within the loop. 89 | 90 | Several built-in callbacks provide functionality that can be added to any training loop. These 91 | include: 92 | 93 | - Logging statistics to comma-separated-value (CSV) files 94 | - Adjusting the learning rate according to a custom schedule 95 | - Monitoring and graphing training progress via TensorBoard 96 | 97 | In addition to these, you can create your own custom callbacks to add a range of additional 98 | functionality to a standard training loop. 99 | 100 | ### CSV logging 101 | 102 | The [`CSVLogger`](https://github.com/tensorflow/swift-models/blob/main/TrainingLoop/Callbacks/CSVLogger.swift) 103 | class encapsulates a callback that will write out training statistics in a comma-separated-value 104 | format to a file of your choosing. This file will start with columns labeled `epoch`, `batch`, and 105 | whatever metrics you have enabled within your training loop. One row will then be written for each 106 | batch, with the current values of those columns. 107 | 108 | To add CSV logging to your training loop, add something like the following to an array of callbacks 109 | provided to the `callbacks:` parameter for your `TrainingLoop`: 110 | 111 | ```swift 112 | try! CSVLogger(path: "file.csv").log 113 | ``` 114 | 115 | As an example, the [`LeNet-MNIST` sample](https://github.com/tensorflow/swift-models/blob/main/Examples/LeNet-MNIST/main.swift#L52) 116 | uses this within its training loop. 117 | 118 | ### Learning rate schedules 119 | 120 | It's common when training a model to change the learning rate provided to an optimizer during the 121 | training process. This can be as simple as a linear decrease over time, or as complex as warmup and 122 | decline cycles described by complicated functions. 123 | 124 | The [`learningRateScheduler`](https://github.com/tensorflow/swift-models/blob/main/TrainingLoop/Callbacks/LearningRateScheduler/LearningRateScheduler.swift) 125 | callback provides the means of describing learning rate schedules composed of different segments, 126 | each with their own distinct shape. This is accomplished by defining a 127 | [`LearningRateSchedule`](https://github.com/tensorflow/swift-models/blob/main/TrainingLoop/Callbacks/LearningRateScheduler/LearningRateSchedule.swift) 128 | composed of `ScheduleSegment`s that each have a `Shape` defined by a function, an initial learning 129 | rate, and a final learning rate. 130 | 131 | For example, the [BERT-CoLA sample](https://github.com/tensorflow/swift-models/blob/main/Examples/BERT-CoLA/main.swift) 132 | uses a linear increase in the learning rate during a warmup period and a linear decrease after that. 133 | To do this, the learning rate schedule callback is defined as follows: 134 | 135 | ```swift 136 | learningRateScheduler( 137 | schedule: makeSchedule( 138 | [ 139 | ScheduleSegment(shape: linear, startRate: 0, endRate: peakLearningRate, stepCount: 10), 140 | ScheduleSegment(shape: linear, endRate: 0) 141 | ] 142 | ) 143 | ) 144 | ``` 145 | 146 | The two `ScheduleSegment`s define a learning rate that starts at 0 and increases linearly to 147 | `peakLearningRate` over a series of 10 discrete steps, then starts at the final learning rate from 148 | the previous step and decreases linearly to 0 by the end of the training process. 149 | 150 | ### TensorBoard integration 151 | 152 | [TensorBoard](https://www.tensorflow.org/tensorboard) is a powerful visualization tool for 153 | monitoring model training, analyzing training when completed, or comparing training runs. Swift for 154 | TensorFlow supports TensorBoard visualization through the use of the 155 | [`TensorBoard`](https://github.com/tensorflow/swift-models/tree/main/TensorBoard) module in the 156 | models repository, which provides callbacks that log training metrics. 157 | 158 | The [GPT2-WikiText2](https://github.com/tensorflow/swift-models/tree/main/Examples/GPT2-WikiText2) 159 | sample illustrates how to add TensorBoard logging to your model training. First, import the 160 | `TensorBoard` module. Then it's as simple as adding `tensorBoardStatisticsLogger()` to your 161 | `TrainingLoop`'s `callbacks:` array. 162 | 163 | By default, that will log each training run within a `run/tensorboard/stats` directory. To view this 164 | within Tensorboard, run 165 | 166 | ```sh 167 | tensorboard --logdir ./run/tensorboard/stats 168 | ``` 169 | 170 | and TensorBoard should start a local server where you can view your training metrics. Training and 171 | validation results should be shown separately, and each run has a unique timestamp to allow for 172 | easy comparison between multiple runs of the same model. 173 | 174 | The design of the Swift for TensorFlow TensorBoard integration was inspired by 175 | [tensorboardX](https://github.com/lanpa/tensorboardX). The TensorBoard callbacks directly create the 176 | appropriate event and summary protocol buffers and write them within a log file during training. 177 | 178 | ### Custom callbacks 179 | 180 | In addition to the built-in callbacks described above, you have the ability to customize the 181 | function of training loops by creating your own callbacks. These callbacks are functions that 182 | have a signature similar to the following: 183 | 184 | ```swift 185 | func customCallback(_ loop: inout L, event: TrainingLoopEvent) throws 186 | { 187 | if event == .updateStart { 188 | ... 189 | } 190 | } 191 | ``` 192 | 193 | The training loop and associated state are passed in as the first parameter. The current part of 194 | the loop that the callback is responding to is provided via `event`. The training loop event has 195 | one of the following states, each corresponding to a different point in the loop's life cycle: 196 | 197 | - `fitStart` 198 | - `fitEnd` 199 | - `epochStart` 200 | - `epochEnd` 201 | - `trainingStart` 202 | - `trainingEnd` 203 | - `validationStart` 204 | - `validationEnd` 205 | - `batchStart` 206 | - `batchEnd` 207 | - `updateStart` 208 | - `inferencePredictionEnd` 209 | 210 | Your callback function can choose to activate its logic on any combination of above states, which 211 | allows for extracting data from or otherwise controlling the training loop in many ways. -------------------------------------------------------------------------------- /docs/site/tutorials/.gitignore: -------------------------------------------------------------------------------- 1 | iris_training.csv 2 | iris_test.csv 3 | .ipynb_checkpoints/ 4 | -------------------------------------------------------------------------------- /docs/site/tutorials/raw_tensorflow_operators.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": { 6 | "id": "XNWJ6JVGkOlf" 7 | }, 8 | "source": [ 9 | "##### Copyright 2018 The TensorFlow Authors. [Licensed under the Apache License, Version 2.0](#scrollTo=bPJq2qP2KE3u)." 10 | ] 11 | }, 12 | { 13 | "cell_type": "code", 14 | "execution_count": null, 15 | "metadata": { 16 | "cellView": "form", 17 | "id": "fSlQ2vFzKGOY" 18 | }, 19 | "outputs": [], 20 | "source": [ 21 | "// #@title Licensed under the Apache License, Version 2.0 (the \"License\"); { display-mode: \"form\" }\n", 22 | "// Licensed under the Apache License, Version 2.0 (the \"License\");\n", 23 | "// you may not use this file except in compliance with the License.\n", 24 | "// You may obtain a copy of the License at\n", 25 | "//\n", 26 | "// https://www.apache.org/licenses/LICENSE-2.0\n", 27 | "//\n", 28 | "// Unless required by applicable law or agreed to in writing, software\n", 29 | "// distributed under the License is distributed on an \"AS IS\" BASIS,\n", 30 | "// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n", 31 | "// See the License for the specific language governing permissions and\n", 32 | "// limitations under the License." 33 | ] 34 | }, 35 | { 36 | "cell_type": "markdown", 37 | "metadata": { 38 | "id": "yfNdITLmJtX8" 39 | }, 40 | "source": [ 41 | "\n", 42 | " \n", 45 | " \n", 48 | " \n", 51 | "
\n", 43 | " View on TensorFlow.org\n", 44 | " \n", 46 | " Run in Google Colab\n", 47 | " \n", 49 | " View source on GitHub\n", 50 | "
" 52 | ] 53 | }, 54 | { 55 | "cell_type": "markdown", 56 | "metadata": { 57 | "id": "lONNcRalVUO9" 58 | }, 59 | "source": [ 60 | "# Raw TensorFlow operators\n", 61 | "\n", 62 | "Building on TensorFlow, Swift for TensorFlow takes a fresh approach to API design. APIs are carefully curated from established libraries and combined with new language idioms. This means that not all TensorFlow APIs will be directly available as Swift APIs, and our API curation needs time and dedicated effort to evolve. However, do not worry if your favorite TensorFlow operator is not available in Swift -- the TensorFlow Swift library gives you transparent access to most TensorFlow operators, under the `_Raw` namespace.\n" 63 | ] 64 | }, 65 | { 66 | "cell_type": "markdown", 67 | "metadata": { 68 | "id": "CYqNvcHxqg0Y" 69 | }, 70 | "source": [ 71 | "Import `TensorFlow` to get started." 72 | ] 73 | }, 74 | { 75 | "cell_type": "code", 76 | "execution_count": null, 77 | "metadata": { 78 | "id": "cVRrzjzFqee9" 79 | }, 80 | "outputs": [], 81 | "source": [ 82 | "import TensorFlow" 83 | ] 84 | }, 85 | { 86 | "cell_type": "markdown", 87 | "metadata": { 88 | "id": "5vza91sR09r-" 89 | }, 90 | "source": [ 91 | "## Calling raw operators\n", 92 | "\n", 93 | "Simply find the function you need under the `_Raw` namespace via code completion." 94 | ] 95 | }, 96 | { 97 | "cell_type": "code", 98 | "execution_count": null, 99 | "metadata": { 100 | "id": "kZRlD4utdPuX" 101 | }, 102 | "outputs": [], 103 | "source": [ 104 | "print(_Raw.mul(Tensor([2.0, 3.0]), Tensor([5.0, 6.0])))" 105 | ] 106 | }, 107 | { 108 | "cell_type": "markdown", 109 | "metadata": { 110 | "id": "iIgKg-ueVCy_" 111 | }, 112 | "source": [ 113 | "## Defining a new multiply operator\n", 114 | "\n", 115 | "Multiply is already available as operator `*` on `Tensor`, but let us pretend that we wanted to make it available under a new name as `.*`. Swift allows you to retroactively add methods or computed properties to existing types using `extension` declarations.\n", 116 | "\n", 117 | "Now, let us add `.*` to `Tensor` by declaring an extension and make it available when the tensor's `Scalar` type conforms to [`Numeric`](https://developer.apple.com/documentation/swift/numeric)." 118 | ] 119 | }, 120 | { 121 | "cell_type": "code", 122 | "execution_count": null, 123 | "metadata": { 124 | "id": "BdH-yZBjTZNx" 125 | }, 126 | "outputs": [], 127 | "source": [ 128 | "infix operator .* : MultiplicationPrecedence\n", 129 | "\n", 130 | "extension Tensor where Scalar: Numeric {\n", 131 | " static func .* (_ lhs: Tensor, _ rhs: Tensor) -> Tensor {\n", 132 | " return _Raw.mul(lhs, rhs)\n", 133 | " }\n", 134 | "}\n", 135 | "\n", 136 | "let x: Tensor = [[1.0, 2.0], [3.0, 4.0]]\n", 137 | "let y: Tensor = [[8.0, 7.0], [6.0, 5.0]]\n", 138 | "print(x .* y)" 139 | ] 140 | }, 141 | { 142 | "cell_type": "markdown", 143 | "metadata": { 144 | "id": "ucD5XZYYyzNe" 145 | }, 146 | "source": [ 147 | "## Defining a derivative of a wrapped function\n", 148 | "\n", 149 | "Not only can you easily define a Swift API for a raw TensorFlow operator, you can also make it differentiable to work with Swift's first-class automatic differentiation.\n", 150 | "\n", 151 | "To make `.*` differentiable, use the `@derivative` attribute on the derivative function and specify the original function as an attribute argument under the `of:` label. Since the `.*` operator is defined when the generic type `Scalar` conforms to `Numeric`, it is not enough for making `Tensor` conform to the `Differentiable` protocol. Born with type safety, Swift will remind us to add a generic constraint on the `@differentiable` attribute to require `Scalar` to conform to `TensorFlowFloatingPoint` protocol, which would make `Tensor` conform to `Differentiable`.\n", 152 | "\n", 153 | "```swift\n", 154 | "@differentiable(where Scalar: TensorFlowFloatingPoint)\n", 155 | "```" 156 | ] 157 | }, 158 | { 159 | "cell_type": "code", 160 | "execution_count": null, 161 | "metadata": { 162 | "id": "fDXS0h_YumcL" 163 | }, 164 | "outputs": [], 165 | "source": [ 166 | "infix operator .* : MultiplicationPrecedence\n", 167 | "\n", 168 | "extension Tensor where Scalar: Numeric {\n", 169 | " @differentiable(where Scalar: TensorFlowFloatingPoint)\n", 170 | " static func .* (_ lhs: Tensor, _ rhs: Tensor) -> Tensor {\n", 171 | " return _Raw.mul(lhs, rhs)\n", 172 | " }\n", 173 | "}\n", 174 | "\n", 175 | "extension Tensor where Scalar : TensorFlowFloatingPoint { \n", 176 | " @derivative(of: .*)\n", 177 | " static func multiplyDerivative(\n", 178 | " _ lhs: Tensor, _ rhs: Tensor\n", 179 | " ) -> (value: Tensor, pullback: (Tensor) -> (Tensor, Tensor)) {\n", 180 | " return (lhs * rhs, { v in\n", 181 | " ((rhs * v).unbroadcasted(to: lhs.shape),\n", 182 | " (lhs * v).unbroadcasted(to: rhs.shape))\n", 183 | " })\n", 184 | " }\n", 185 | "}\n", 186 | "\n", 187 | "// Now, we can take the derivative of a function that calls `.*` that we just defined.\n", 188 | "print(gradient(at: x, y) { x, y in\n", 189 | " (x .* y).sum()\n", 190 | "})" 191 | ] 192 | }, 193 | { 194 | "cell_type": "markdown", 195 | "metadata": { 196 | "id": "l7kae5o1VKnu" 197 | }, 198 | "source": [ 199 | "## More examples" 200 | ] 201 | }, 202 | { 203 | "cell_type": "code", 204 | "execution_count": null, 205 | "metadata": { 206 | "id": "v92FrXpCSuLT" 207 | }, 208 | "outputs": [], 209 | "source": [ 210 | "let matrix = Tensor([[1, 2], [3, 4]])\n", 211 | "\n", 212 | "print(_Raw.matMul(matrix, matrix, transposeA: true, transposeB: true))\n", 213 | "print(_Raw.matMul(matrix, matrix, transposeA: true, transposeB: false))\n", 214 | "print(_Raw.matMul(matrix, matrix, transposeA: false, transposeB: true))\n", 215 | "print(_Raw.matMul(matrix, matrix, transposeA: false, transposeB: false))" 216 | ] 217 | } 218 | ], 219 | "metadata": { 220 | "colab": { 221 | "collapsed_sections": [], 222 | "name": "raw_tensorflow_operators.ipynb", 223 | "toc_visible": true 224 | }, 225 | "kernelspec": { 226 | "display_name": "Swift", 227 | "name": "swift" 228 | } 229 | }, 230 | "nbformat": 4, 231 | "nbformat_minor": 0 232 | } 233 | -------------------------------------------------------------------------------- /images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tensorflow/swift/f0d6c74ef5d016046afc1eac0b07a2f6b74b8fdf/images/logo.png -------------------------------------------------------------------------------- /notebooks/blank_swift.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "nbformat": 4, 3 | "nbformat_minor": 0, 4 | "metadata": { 5 | "colab": { 6 | "name": "blank_swift.ipynb", 7 | "version": "0.3.2", 8 | "provenance": [], 9 | "collapsed_sections": [] 10 | }, 11 | "kernelspec": { 12 | "name": "swift", 13 | "display_name": "Swift" 14 | } 15 | }, 16 | "cells": [ 17 | { 18 | "metadata": { 19 | "id": "kZRlD4utdPuX", 20 | "colab_type": "code", 21 | "colab": {} 22 | }, 23 | "cell_type": "code", 24 | "source": [ 25 | "" 26 | ], 27 | "execution_count": 0, 28 | "outputs": [] 29 | } 30 | ] 31 | } -------------------------------------------------------------------------------- /notebooks/blank_swift_gpu.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "nbformat": 4, 3 | "nbformat_minor": 0, 4 | "metadata": { 5 | "colab": { 6 | "name": "blank_swift_gpu.ipynb", 7 | "version": "0.3.2", 8 | "provenance": [], 9 | "collapsed_sections": [] 10 | }, 11 | "kernelspec": { 12 | "name": "swift", 13 | "display_name": "Swift" 14 | }, 15 | "accelerator": "GPU" 16 | }, 17 | "cells": [ 18 | { 19 | "metadata": { 20 | "id": "kZRlD4utdPuX", 21 | "colab_type": "code", 22 | "colab": {} 23 | }, 24 | "cell_type": "code", 25 | "source": [ 26 | "" 27 | ], 28 | "execution_count": 0, 29 | "outputs": [] 30 | } 31 | ] 32 | } -------------------------------------------------------------------------------- /notebooks/blank_swift_tpu.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "nbformat": 4, 3 | "nbformat_minor": 0, 4 | "metadata": { 5 | "colab": { 6 | "name": "blank_swift_tpu.ipynb", 7 | "version": "0.3.2", 8 | "provenance": [], 9 | "collapsed_sections": [] 10 | }, 11 | "kernelspec": { 12 | "name": "swift", 13 | "display_name": "Swift" 14 | }, 15 | "accelerator": "TPU" 16 | }, 17 | "cells": [ 18 | { 19 | "metadata": { 20 | "id": "kZRlD4utdPuX", 21 | "colab_type": "code", 22 | "colab": {} 23 | }, 24 | "cell_type": "code", 25 | "source": [ 26 | "" 27 | ], 28 | "execution_count": 0, 29 | "outputs": [] 30 | } 31 | ] 32 | } -------------------------------------------------------------------------------- /proposals/LayerProtocolRevision.md: -------------------------------------------------------------------------------- 1 | # Layer Protocol - Training and Concurrent Testing 2 | 3 | * Author: [@ewconnell](https://github.com/ewconnell) 4 | 5 | ## Introduction 6 | This document discusses the requirements and proposed design changes to enable concurrent testing while training. It also discusses problems with use of the _LearningPhaseIndicator_ class in the current design. 7 | 8 | ## Performance 9 | Significant training time improvement can be achieved by performing model test passes concurrently, allowing training to continue uninterrupted. The larger the training sample, number of samples in the test set, and more expensive the model design, the greater the benefit. Concurrent test passes can utilize idle GPU capacity, utilize additional GPUs, or be distributed to other nodes. 10 | 11 | The current S4TF implementation does allow this to a limited degree. The following example runs correctly. In this case very little benefit is achieved because the S4TF training pass is currently much slower than the inference pass. In Netlib a training pass is very fast, so I was able to confirm that a concurrent test pass achieves a significant performance gain. The following is an example of a concurrent training loop that currently works with the simple addition of a _DispatchQueue_ and _DispatchGroup_. Since Layer is a struct, models copy correctly. As training proceeds the learned parameters mutate, making the copies independent. 12 | ```swift 13 | var model = MNISTClassifier() 14 | let optimizer = SGD(learningRate: 0.1, momentum: 0.9) 15 | let batchSize: Int32 = 60 16 | let testBatchSize: Int32 = 1000 17 | let trainingIterations: Int32 = trainingImages.shape[0] / batchSize 18 | let epochs = 10 19 | let testQueue = DispatchQueue(label: "testQueue") 20 | let testGroup = DispatchGroup() 21 | 22 | func minibatch(_ x: Tensor, size: Int32, batchIndex: Int32) -> Tensor { 23 | let start = batchIndex * size 24 | return x[start.. Tensor in 38 | let logits = model.applied(to: images) 39 | let batchLoss = softmaxCrossEntropy(logits: logits, labels: labels) 40 | totalLoss += batchLoss.scalarized() 41 | return batchLoss 42 | } 43 | optimizer.update(&model.allDifferentiableVariables, along: gradients) 44 | } 45 | // test 46 | testQueue.async(group: testGroup) { 47 | var totalCorrect: Int32 = 0 48 | for i in 0..(correct).sum().scalarized() 54 | } 55 | 56 | let accuracy = Float(totalCorrect) / Float(numericTestLabels.shape[0]) 57 | print("epoch \(epoch) accuracy: \(accuracy) loss: \(totalLoss)") 58 | } 59 | } 60 | testGroup.wait() 61 | print("Training complete: \(String(timeInterval: Date().timeIntervalSince(start)))") 62 | ``` 63 | ## Copying Layers and the LearningPhaseIndicator 64 | Some operators such as BatchNorm and Dropout need to behave differently depending on whether they are performing training or inference. The current design defines the LearningPhaseIndicator class which is intended to behave like a global variable scoped to a single model. The training loop would toggle the _.training_ value depending on whether training or inference is being performed. 65 | 66 | The examples I saw in [tensorflow swift-models](https://github.com/tensorflow/swift-models) had the LearningPhaseIndicator declared and manipulated separately from the model it was affecting. One object having a side effect on another is problematic. Declaring it as a member of the root layer would have been better. In any case this design won’t work because as soon as you copy a model, the same LearningPhaseIndicator will be affecting both models. This would make it impossible to perform concurrent testing, or work with model copies in general. I don’t believe there is any clean way to have a pseudo global variable scoped to a Layer tree. I ran into the same design problem several years ago. 67 | 68 | ## Suggested Design Change 69 | A simple and performative solution is to modify the Layer _applied(to:_ API to include a training parameter. Perhaps add an extension function to drop the parameter when calling layers that don’t make a distinction. 70 | ```swift 71 | public protocol Layer: Differentiable & KeyPathIterable 72 | where AllDifferentiableVariables: KeyPathIterable { 73 | /// The input type of the layer. 74 | associatedtype Input: Differentiable 75 | /// The output type of the layer. 76 | associatedtype Output: Differentiable 77 | 78 | /// Returns the output obtained from applying to an input. 79 | @differentiable(wrt: (self, input)) 80 | func applied(to input: Input, training: Bool) -> Output 81 | } 82 | 83 | public extension Layer { 84 | func applied(to input: Input) -> Output { 85 | return applied(to: input, training: false) 86 | } 87 | } 88 | ``` 89 | Layer implementations such as BatchNorm can easily switch functionality in a readable way. 90 | ```swift 91 | public func applied(to input: Tensor, training: Bool) -> Tensor { 92 | if training { 93 | return applyTraining(to: input) 94 | } else { 95 | return applyInference(to: input) 96 | } 97 | } 98 | ``` 99 | Layer pass through would look clean as well 100 | ```swift 101 | public func applied(to input: Tensor, training: Bool) -> Tensor { 102 | let h0 = conv1.applied(to: input) 103 | let h1 = maxPool1.applied(to: h0) 104 | let bn = batchNorm.applied(to: h1, training: training) 105 | let h2 = conv2.applied(to: bn) 106 | let h3 = maxPool2.applied(to: h2) 107 | let dense1InputShape = Tensor([h3.shape[0], 800]) 108 | let h4 = dense1.applied(to: h3.reshaped(toShape: dense1InputShape)) 109 | return dense2.applied(to: h4) 110 | } 111 | ``` 112 | A simple model declaration might look like 113 | ```swift 114 | public struct MNISTClassifier: Layer { 115 | let maxPool1: MaxPool2D 116 | let maxPool2: MaxPool2D 117 | var batchNorm: BatchNorm 118 | var conv1: Conv2D 119 | var conv2: Conv2D 120 | var dense1: Dense 121 | var dense2: Dense 122 | 123 | public init() { 124 | conv1 = Conv2D(filterShape: (5, 5, 1, 20), padding: .valid) 125 | maxPool1 = MaxPool2D(poolSize: (2, 2), strides: (2, 2), padding: .valid) 126 | batchNorm = BatchNorm(featureCount: 20) 127 | conv2 = Conv2D(filterShape: (5, 5, 20, 50), padding: .valid) 128 | maxPool2 = MaxPool2D(poolSize: (2, 2), strides: (2, 2), padding: .valid) 129 | dense1 = Dense(inputSize: 800, outputSize: 500, activation: relu) 130 | dense2 = Dense(inputSize: 500, outputSize: 10, activation: { $0 }) 131 | } 132 | 133 | @differentiable(wrt: (self, input)) 134 | public func applied(to input: Tensor, training: Bool) -> Tensor { 135 | let h0 = conv1.applied(to: input) 136 | let h1 = maxPool1.applied(to: h0) 137 | let bn = batchNorm.applied(to: h1, training: training) 138 | let h2 = conv2.applied(to: bn) 139 | let h3 = maxPool2.applied(to: h2) 140 | let dense1InputShape = Tensor([h3.shape[0], 800]) 141 | let h4 = dense1.applied(to: h3.reshaped(toShape: dense1InputShape)) 142 | return dense2.applied(to: h4) 143 | } 144 | 145 | public func infer(from input: Tensor) -> Tensor { 146 | return softmax(applied(to: input)) 147 | } 148 | } 149 | ``` 150 | The revised training loop would only need to specify the _training_ parameter and would now look like: 151 | ```swift 152 | for i in 0.. Tensor in 156 | // set training to true 157 | let logits = model.applied(to: images, training: true) 158 | let batchLoss = softmaxCrossEntropy(logits: logits, labels: labels) 159 | totalLoss += batchLoss.scalarized() 160 | return batchLoss 161 | } 162 | optimizer.update(&model.allDifferentiableVariables, along: gradients) 163 | } 164 | ``` 165 | ## Conclusion 166 | This minor design change will 167 | * Eliminate the need for the LearningPhaseIndicator class 168 | * Fix the model copying problem 169 | * Enable concurrent testing to improve performance 170 | 171 | The implementation change will likely affect a lot of the codebase as the _applied(to:_ function is central to automatic differentiation. 172 | 173 | -------------------------------------------------------------------------------- /proposals/README.md: -------------------------------------------------------------------------------- 1 | # Proposals 2 | 3 | This directory contains proposals written by the open-source community and the 4 | TensorFlow team regarding the design and implementation of Swift for TensorFlow. 5 | -------------------------------------------------------------------------------- /proposals/modern-layer-api/LayerApiDesignSpace.md: -------------------------------------------------------------------------------- 1 | # Layer API Design Space 2 | ## Overview 3 | This document describes the high-level requirements and tradeoffs for a successful layer API design. It also walks through a couple conceptual approaches for API designs. 4 | 5 | ## Definitions 6 | Neural Network - collection of weight values and a differentiable execution function 7 | 8 | Layer - a differentiable function from (weights & input) to output, and a default function from hyperparameters to weights 9 | 10 | Initialization Function - an optionally differentiable function mapping hyperparameters of a layer to a new instance of the weights or an existing weight store that has been mutated 11 | 12 | Model - a mapping of a set of layers to a set of weight instances (bipartite; many-to-many) 13 | 14 | ## API Requirements 15 | ### Layer Composition 16 | - Any layer, combination of layers, or trained model, should be usable as a layer in another model 17 | - Scales to complex architectures, no need to rewrite the model to use a different API for advanced graph types 18 | - No boilerplate: 19 | - No duplicate shapes 20 | - No duplicate input/output/scalar types (use type inference) 21 | - No redundant definition of weights and execution in default use cases 22 | 23 | ### Complex Architectures 24 | - Skip-connections (use results of a layer multiple times) 25 | - Shared layers (reuse weights multiple times, usually but not always with the same execution function but at different points of the graph) 26 | - Support dynamic architectures, with generated layers and connections based on runtime configuration 27 | - Also support reconfiguration of models to use different hyperparameters at runtime 28 | 29 | ### State Management 30 | - Weight access should be type-safe (no casting into the specific type) 31 | - All weights should have associated names (variables, subscripts) and can use those names to access the current value 32 | - Weights should be groupable for advanced optimizers (e.g. that use multiple learning rates) or partially "freezing" a model. 33 | - Weights should be loadable from checkpoint files and support mapping weights from equivalent models 34 | - Weight manipulation should be handled in a value-semantic way to prevent unexpected changes 35 | 36 | ### Execution Debugging 37 | - Access to the values of intermediate tensors within the graph (inputs/outputs to layers) 38 | - Not stored by default, should be opt-in 39 | - Insert debugging “layers” (e.g. that print out their input, or compute arbitrary other data-dependent information) 40 | - Display the final model architecture in a graphical format 41 | 42 | ### Type-Safety 43 | - No stringly-typed APIs 44 | - access to weights/intermediate tensors must be type-safe 45 | - Rank-safe computation - track the number of dimensions of data 46 | - Track the meaning of each channel (differentiate “CHW” vs “HWC” images) 47 | - All other opportunities that are reasonably accessible 48 | 49 | ## Design Approaches 50 | ### Weights vs. Execution vs. Layers 51 | One of the key insights resulting from our discussions was the separation between weights and model execution. In the current S4TF layer API, these are combined by defining layers as stateful functions which both capture the current weight values and define how the layer should be applied to input data. While this works well for simple systems that have a bijection between weights and execution functions, this is harder to adapt to systems that require the same layer with the same weights to be applied at multiple locations in the model (layer sharing). Implementing such architectures with packaged weights and execution results in referential semantics since multiple nodes in the graph would need to refer to the same underlying layer. 52 | 53 | If we take a more functional approach, however, where weights do not make up the state of a function but instead are just an additional parameter for execution, this becomes more straightforward to handle. Instead of having to refer to a shared mutable state of a specific layer, the execution functions instead take the entire set of weights of the model and use the weights that are relevant to the current node. As a result, we effectively bubble up individual weights to the model level and eliminate the referential semantics needed when execution functions are tied to mutable state. 54 | 55 | When separating weights from execution, however, we must be careful to not introduce boilerplate that forces duplicate definitions of weights and execution when one can be inferred from the other. Although layers are not a core component of the final model, they can exist as helpers that associate weights with default execution functions in order to eliminate boilerplate. 56 | 57 | ### Explicit Graphs vs Layers as Tensors 58 | 59 | In our prototypes for layer APIs, we settled on two primary strategies for combining layers into more complex architectures: building an explicit graph or inferring the graph from layer dependencies. 60 | 61 | In the first strategy, the user directly builds the entire graph in a way that requires no additional computation to determine the dependents of any layer. This requires the user to be aware of both incoming and outgoing edges from every layer “node” when constructing the model, but makes it easy to get high performance since there is a direct mapping from the composed layers to the weights and functions to execute. For example, when implementing a skip connection, the user would need to specify both a “fan-out” for the layer whose results will be used along multiple paths as well as a “fan-in” to combine the results. 62 | 63 | For a simpler user experience, we can infer the graph based on dependencies, which eliminates the need to specify the “fan-out” of skip connections since we can detect layers that have multiple dependents. When designing models with this style, every layer tracks its dependencies. In this way, users can manipulate layers just like lazy tensors, since they accumulate a trace of dependencies and can be used as dependencies of other layers to define connections. 64 | -------------------------------------------------------------------------------- /utils/base-deps-docker/Dockerfile: -------------------------------------------------------------------------------- 1 | # A Docker image with all dependencies required to run (not build) an S4TF toolchain. 2 | 3 | ARG S4TF_CUDA_VERSION 4 | ARG S4TF_CUDNN_VERSION 5 | 6 | FROM nvidia/cuda:${S4TF_CUDA_VERSION}-cudnn${S4TF_CUDNN_VERSION}-devel-ubuntu18.04 7 | 8 | # ARGs from before FROM are cleared after the FROM, so we repeat ourselves. 9 | ARG S4TF_CUDA_VERSION 10 | ARG S4TF_CUDNN_VERSION 11 | 12 | ENV DEBIAN_FRONTEND=noninteractive 13 | 14 | # Install Swift deps. 15 | RUN apt-get update \ 16 | && apt-get install -y --no-install-recommends \ 17 | build-essential \ 18 | ca-certificates \ 19 | curl \ 20 | git \ 21 | python \ 22 | python-dev \ 23 | python-pip \ 24 | python-setuptools \ 25 | python-tk \ 26 | python3 \ 27 | python3-dev \ 28 | python3-pip \ 29 | python3-setuptools \ 30 | clang \ 31 | libcurl4-openssl-dev \ 32 | libicu-dev \ 33 | libpython-dev \ 34 | libpython3-dev \ 35 | libncurses5-dev \ 36 | libxml2 \ 37 | libblocksruntime-dev \ 38 | libbsd-dev \ 39 | # Install Python libraries 40 | && pip3 install numpy matplotlib gym psutil junit-xml \ 41 | # Configure cuda 42 | && echo "/usr/local/cuda-${S4TF_CUDA_VERSION}/targets/x86_64-linux/lib/stubs" > /etc/ld.so.conf.d/cuda-${S4TF_CUDA_VERSION}-stubs.conf \ 43 | && ldconfig 44 | -------------------------------------------------------------------------------- /utils/base-deps-docker/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Builds base deps docker images for various CUDA versions, and uploads them to 4 | # the Google Container Registry. 5 | 6 | set -exuo pipefail 7 | 8 | do_build() { 9 | export S4TF_CUDA_VERSION="$1" 10 | export S4TF_CUDNN_VERSION="$2" 11 | IMAGE_NAME="gcr.io/swift-tensorflow/base-deps-cuda${S4TF_CUDA_VERSION}-cudnn${S4TF_CUDNN_VERSION}-ubuntu18.04" 12 | sudo -E docker build \ 13 | -t "$IMAGE_NAME" \ 14 | --build-arg S4TF_CUDA_VERSION \ 15 | --build-arg S4TF_CUDNN_VERSION \ 16 | . 17 | docker push "$IMAGE_NAME" 18 | } 19 | 20 | do_build 10.1 7 21 | do_build 10.2 7 22 | -------------------------------------------------------------------------------- /utils/install-ubuntu1804.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Installs a Swift for TensorFlow toolchain from scratch on Ubuntu 18.04. 4 | # 5 | # Usage: 6 | # ./install-ubuntu1804 7 | # [--toolchain-url TOOLCHAIN_URL] 8 | # [--jupyter-url JUPYTER_URL] 9 | # [--cuda CUDA_VERSION] 10 | # [--no-jupyter] 11 | # [--install-location INSTALL_LOCATION] 12 | # 13 | # Arguments: 14 | # --toolchain-url: Specifies the URL for the toolchain. Defaults to the latest 15 | # nightly CPU-only toolchain. 16 | # --jupyter-url: Specifies the URL for swift-jupyter. Defaults to the latest 17 | # nightly build. Set this to the empty string to disable 18 | # swift-jupyter installation. 19 | # --install-location: Directory to extract the toolchain. Defaults to 20 | # "./swift-toolchain". 21 | 22 | set -exuo pipefail 23 | 24 | TOOLCHAIN_URL=https://storage.googleapis.com/swift-tensorflow-artifacts/nightlies/latest/swift-tensorflow-DEVELOPMENT-ubuntu18.04.tar.gz 25 | JUPYTER_URL=https://storage.googleapis.com/swift-tensorflow-artifacts/nightlies/latest/swift-jupyter.tar.gz 26 | INSTALL_LOCATION=./swift-toolchain 27 | 28 | # Parse arguments. 29 | PARSE_ERROR="invalid arguments" 30 | while 31 | arg="${1-}" 32 | case "$arg" in 33 | --toolchain-url) TOOLCHAIN_URL="${2?"$PARSE_ERROR"}"; shift;; 34 | --jupyter-url) JUPYTER_URL="${2?"$PARSE_ERROR"}"; shift;; 35 | --install-location) INSTALL_LOCATION="${2?"$PARSE_ERROR"}"; shift;; 36 | "") break;; 37 | *) echo "$PARSE_ERROR" >&2; exit 2;; 38 | esac 39 | do 40 | shift 41 | done 42 | 43 | # Wait for apt lock to be released 44 | # Source: https://askubuntu.com/a/373478 45 | while sudo fuser /var/{lib/{dpkg,apt/lists},cache/apt/archives}/lock >/dev/null 2>&1; do 46 | sleep 1 47 | done 48 | 49 | # Install dependencies 50 | DEBIAN_FRONTEND=noninteractive 51 | apt-get update 52 | apt-get install -y --no-install-recommends \ 53 | build-essential \ 54 | ca-certificates \ 55 | curl \ 56 | git \ 57 | python3-dev \ 58 | python3-tk \ 59 | clang \ 60 | libcurl4-openssl-dev \ 61 | libicu-dev \ 62 | libpython-dev \ 63 | libpython3-dev \ 64 | libncurses5-dev \ 65 | libxml2 \ 66 | libblocksruntime-dev 67 | 68 | # Download and extract Swift toolchain. 69 | mkdir -p "$INSTALL_LOCATION" 70 | wget "$TOOLCHAIN_URL" -O "$INSTALL_LOCATION"/swift-toolchain.tar.gz 71 | tar -xf "$INSTALL_LOCATION"/swift-toolchain.tar.gz -C "$INSTALL_LOCATION" 72 | rm "$INSTALL_LOCATION"/swift-toolchain.tar.gz 73 | 74 | # Download, extract, and register Jupyter, if requested. 75 | if [[ ! -z "$JUPYTER_URL" ]]; then 76 | wget "$JUPYTER_URL" -O "$INSTALL_LOCATION"/swift-jupyter.tar.gz 77 | tar -xf "$INSTALL_LOCATION"/swift-jupyter.tar.gz -C "$INSTALL_LOCATION" 78 | rm "$INSTALL_LOCATION"/swift-jupyter.tar.gz 79 | 80 | python3 -m pip install -r "$INSTALL_LOCATION"/swift-jupyter/requirements.txt 81 | 82 | python3 "$INSTALL_LOCATION"/swift-jupyter/register.py --user --swift-toolchain "$INSTALL_LOCATION" --swift-python-library /usr/lib/x86_64-linux-gnu/libpython3.6m.so 83 | fi 84 | --------------------------------------------------------------------------------