{ "cells": [ { "cell_type": "markdown", "metadata": { "id": "9TV7IYeqifSv" }, "source": [ "##### Copyright 2018 The TensorFlow Authors. [Licensed under the Apache License, Version 2.0](#scrollTo=ByZjmtFgB_Y5)." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "cellView": "form", "id": "tRIJp_4m_Afz" }, "outputs": [], "source": [ "// #@title Licensed under the Apache License, Version 2.0 (the \"License\"); { display-mode: \"form\" }\n", "// Licensed under the Apache License, Version 2.0 (the \"License\");\n", "// you may not use this file except in compliance with the License.\n", "// You may obtain a copy of the License at\n", "//\n", "// https://www.apache.org/licenses/LICENSE-2.0\n", "//\n", "// Unless required by applicable law or agreed to in writing, software\n", "// distributed under the License is distributed on an \"AS IS\" BASIS,\n", "// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n", "// See the License for the specific language governing permissions and\n", "// limitations under the License." ] }, { "cell_type": "markdown", "metadata": { "id": "sI1ZtrdiA4aY" }, "source": [ "\n", " \n", " \n", " \n", "
\n", " View on TensorFlow.org\n", " \n", " Run in Google Colab\n", " \n", " View source on GitHub\n", "
" ] }, { "cell_type": "markdown", "metadata": { "id": "8sa42_NblqRE" }, "source": [ "# Python interoperability\n", "\n", "Swift For TensorFlow supports Python interoperability.\n", "\n", "You can import Python modules from Swift, call Python functions, and convert values between Swift and Python." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "kZRlD4utdPuX" }, "outputs": [], "source": [ "import PythonKit\n", "print(Python.version)" ] }, { "cell_type": "markdown", "metadata": { "id": "W7MpNcIwIIy8" }, "source": [ "## Setting the Python version" ] }, { "cell_type": "markdown", "metadata": { "id": "lM9dRji7IIy8" }, "source": [ "By default, when you `import Python`, Swift searches system library paths for the newest version of Python installed. \n", "To use a specific Python installation, set the `PYTHON_LIBRARY` environment variable to the `libpython` shared library provided by the installation. For example: \n", "\n", "`export PYTHON_LIBRARY=\"~/anaconda3/lib/libpython3.7m.so\"`\n", "\n", "The exact filename will differ across Python environments and platforms." ] }, { "cell_type": "markdown", "metadata": { "id": "eoyLeSQVIIy9" }, "source": [ "Alternatively, you can set the `PYTHON_VERSION` environment variable, which instructs Swift to search system library paths for a matching Python version. Note that `PYTHON_LIBRARY` takes precedence over `PYTHON_VERSION`.\n", "\n", "In code, you can also call the `PythonLibrary.useVersion` function, which is equivalent to setting `PYTHON_VERSION`." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "FCMWR11NIIy-" }, "outputs": [], "source": [ "// PythonLibrary.useVersion(2)\n", "// PythonLibrary.useVersion(3, 7)" ] }, { "cell_type": "markdown", "metadata": { "id": "HrlMNOinIIy_" }, "source": [ "__Note: you should run `PythonLibrary.useVersion` right after `import Python`, before calling any Python code. It cannot be used to dynamically switch Python versions.__" ] }, { "cell_type": "markdown", "metadata": { "id": "mIbIOW0HIIzA" }, "source": [ "Set `PYTHON_LOADER_LOGGING=1` to see [debug output for Python library loading](https://github.com/apple/swift/pull/20674#discussion_r235207008). " ] }, { "cell_type": "markdown", "metadata": { "id": "rU0WY_sJodio" }, "source": [ "## Basics\n", "\n", "In Swift, `PythonObject` represents an object from Python.\n", "All Python APIs use and return `PythonObject` instances.\n", "\n", "Basic types in Swift (like numbers and arrays) are convertible to `PythonObject`. In some cases (for literals and functions taking `PythonConvertible` arguments), conversion happens implicitly. To explicitly cast a Swift value to `PythonObject`, use the `PythonObject` initializer.\n", "\n", "`PythonObject` defines many standard operations, including numeric operations, indexing, and iteration." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "kqXILiXhq-iM" }, "outputs": [], "source": [ "// Convert standard Swift types to Python.\n", "let pythonInt: PythonObject = 1\n", "let pythonFloat: PythonObject = 3.0\n", "let pythonString: PythonObject = \"Hello Python!\"\n", "let pythonRange: PythonObject = PythonObject(5..<10)\n", "let pythonArray: PythonObject = [1, 2, 3, 4]\n", "let pythonDict: PythonObject = [\"foo\": [0], \"bar\": [1, 2, 3]]\n", "\n", "// Perform standard operations on Python objects.\n", "print(pythonInt + pythonFloat)\n", "print(pythonString[0..<6])\n", "print(pythonRange)\n", "print(pythonArray[2])\n", "print(pythonDict[\"bar\"])" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "fEAEyUExXT3I" }, "outputs": [], "source": [ "// Convert Python objects back to Swift.\n", "let int = Int(pythonInt)!\n", "let float = Float(pythonFloat)!\n", "let string = String(pythonString)!\n", "let range = Range(pythonRange)!\n", "let array: [Int] = Array(pythonArray)!\n", "let dict: [String: [Int]] = Dictionary(pythonDict)!\n", "\n", "// Perform standard operations.\n", "// Outputs are the same as Python!\n", "print(Float(int) + float)\n", "print(string.prefix(6))\n", "print(range)\n", "print(array[2])\n", "print(dict[\"bar\"]!)" ] }, { "cell_type": "markdown", "metadata": { "id": "1pMewsl0VgnJ" }, "source": [ "`PythonObject` defines conformances to many standard Swift protocols:\n", "* `Equatable`\n", "* `Comparable`\n", "* `Hashable`\n", "* `SignedNumeric`\n", "* `Strideable`\n", "* `MutableCollection`\n", "* All of the `ExpressibleBy_Literal` protocols\n", "\n", "Note that these conformances are not type-safe: crashes will occur if you attempt to use protocol functionality from an incompatible `PythonObject` instance." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "W9bUsiOxVf_v" }, "outputs": [], "source": [ "let one: PythonObject = 1\n", "print(one == one)\n", "print(one < one)\n", "print(one + one)\n", "\n", "let array: PythonObject = [1, 2, 3]\n", "for (i, x) in array.enumerated() {\n", " print(i, x)\n", "}" ] }, { "cell_type": "markdown", "metadata": { "id": "w3lmTRCWT5sS" }, "source": [ "To convert tuples from Python to Swift, you must statically know the arity of the tuple.\n", "\n", "Call one of the following instance methods:\n", "- `PythonObject.tuple2`\n", "- `PythonObject.tuple3`\n", "- `PythonObject.tuple4`" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "fQ0HEX89T4mW" }, "outputs": [], "source": [ "let pythonTuple = Python.tuple([1, 2, 3])\n", "print(pythonTuple, Python.len(pythonTuple))\n", "\n", "// Convert to Swift.\n", "let tuple = pythonTuple.tuple3\n", "print(tuple)" ] }, { "cell_type": "markdown", "metadata": { "id": "Te7sNNx9c_am" }, "source": [ "## Python builtins\n", "\n", "Access Python builtins via the global `Python` interface." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "jpcOByipc75O" }, "outputs": [], "source": [ "// `Python.builtins` is a dictionary of all Python builtins.\n", "_ = Python.builtins\n", "\n", "// Try some Python builtins.\n", "print(Python.type(1))\n", "print(Python.len([1, 2, 3]))\n", "print(Python.sum([1, 2, 3]))" ] }, { "cell_type": "markdown", "metadata": { "id": "H2wwUL1tY3JX" }, "source": [ "## Importing Python modules\n", "\n", "Use `Python.import` to import a Python module. It works like the `import` keyword in `Python`." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "XrZee8n3Y17_" }, "outputs": [], "source": [ "let np = Python.import(\"numpy\")\n", "print(np)\n", "let zeros = np.ones([2, 3])\n", "print(zeros)" ] }, { "cell_type": "markdown", "metadata": { "id": "hQvza3dUXlr0" }, "source": [ "Use the throwing function `Python.attemptImport` to perform safe importing." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "QD-uQGuaXhrM" }, "outputs": [], "source": [ "let maybeModule = try? Python.attemptImport(\"nonexistent_module\")\n", "print(maybeModule)" ] }, { "cell_type": "markdown", "metadata": { "id": "Qej_Z6V3mZnG" }, "source": [ "## Conversion with `numpy.ndarray`\n", "\n", "The following Swift types can be converted to and from `numpy.ndarray`:\n", "- `Array`\n", "- `ShapedArray`\n", "- `Tensor`\n", "\n", "Conversion succeeds only if the `dtype` of the `numpy.ndarray` is compatible with the `Element` or `Scalar` generic parameter type.\n", "\n", "For `Array`, conversion from `numpy` succeeds only if the `numpy.ndarray` is 1-D." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "hPvKgZBeDQ1p" }, "outputs": [], "source": [ "import TensorFlow\n", "\n", "let numpyArray = np.ones([4], dtype: np.float32)\n", "print(\"Swift type:\", type(of: numpyArray))\n", "print(\"Python type:\", Python.type(numpyArray))\n", "print(numpyArray.shape)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "ZuDgZ5cBS3Uk" }, "outputs": [], "source": [ "// Examples of converting `numpy.ndarray` to Swift types.\n", "let array: [Float] = Array(numpy: numpyArray)!\n", "let shapedArray = ShapedArray(numpy: numpyArray)!\n", "let tensor = Tensor(numpy: numpyArray)!\n", "\n", "// Examples of converting Swift types to `numpy.ndarray`.\n", "print(array.makeNumpyArray())\n", "print(shapedArray.makeNumpyArray())\n", "print(tensor.makeNumpyArray())\n", "\n", "// Examples with different dtypes.\n", "let doubleArray: [Double] = Array(numpy: np.ones([3], dtype: np.float))!\n", "let intTensor = Tensor(numpy: np.ones([2, 3], dtype: np.int32))!" ] }, { "cell_type": "markdown", "metadata": { "id": "8EQFZZ5iafwh" }, "source": [ "## Displaying images\n", "\n", "You can display images in-line using `matplotlib`, just like in Python notebooks." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "vjQ7Rd3_IXuX" }, "outputs": [], "source": [ "// This cell is here to display plots inside a Jupyter Notebook.\n", "// Do not copy it into another environment.\n", "%include \"EnableIPythonDisplay.swift\"\n", "print(IPythonDisplay.shell.enable_matplotlib(\"inline\"))" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "jUzsa2cxafQV" }, "outputs": [], "source": [ "let np = Python.import(\"numpy\")\n", "let plt = Python.import(\"matplotlib.pyplot\")\n", "\n", "let time = np.arange(0, 10, 0.01)\n", "let amplitude = np.exp(-0.1 * time)\n", "let position = amplitude * np.sin(3 * time)\n", "\n", "plt.figure(figsize: [15, 10])\n", "\n", "plt.plot(time, position)\n", "plt.plot(time, amplitude)\n", "plt.plot(time, -amplitude)\n", "\n", "plt.xlabel(\"Time (s)\")\n", "plt.ylabel(\"Position (m)\")\n", "plt.title(\"Oscillations\")\n", "\n", "plt.show()" ] } ], "metadata": { "colab": { "collapsed_sections": [], "name": "python_interoperability.ipynb", "toc_visible": true }, "kernelspec": { "display_name": "Swift", "name": "swift" } }, "nbformat": 4, "nbformat_minor": 0 }