aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar Manjunath Kudlur <keveman@gmail.com>2016-02-04 15:23:44 -0800
committerGravatar Vijay Vasudevan <vrv@google.com>2016-02-04 15:37:38 -0800
commit9c7f71244e7e91e83b7ef79ef0841cfe08d2154d (patch)
tree553e425dc6354ac2342e5c72a24edfc45d46d76a
parent6607d30417454bec519c6d75c91936d4ab4e9c1c (diff)
Updated documentation for adding an op.
- Added functions to get the directory where the headers and the library are installed for use with -I and -L options to the compiler. Change: 113894868
-rw-r--r--tensorflow/g3doc/how_tos/adding_an_op/index.md131
-rw-r--r--tensorflow/g3doc/how_tos/adding_an_op/zero_out_1_test.py4
-rw-r--r--tensorflow/g3doc/how_tos/adding_an_op/zero_out_2_test.py6
-rw-r--r--tensorflow/g3doc/how_tos/adding_an_op/zero_out_3_test.py10
-rw-r--r--tensorflow/g3doc/how_tos/adding_an_op/zero_out_op_1.py31
-rw-r--r--tensorflow/g3doc/how_tos/adding_an_op/zero_out_op_2.py33
-rw-r--r--tensorflow/g3doc/how_tos/adding_an_op/zero_out_op_3.py31
-rw-r--r--tensorflow/python/__init__.py5
-rw-r--r--tensorflow/python/platform/sysconfig.py46
-rw-r--r--tensorflow/tools/pip_package/setup.py13
10 files changed, 248 insertions, 62 deletions
diff --git a/tensorflow/g3doc/how_tos/adding_an_op/index.md b/tensorflow/g3doc/how_tos/adding_an_op/index.md
index 119935f598..38f232104f 100644
--- a/tensorflow/g3doc/how_tos/adding_an_op/index.md
+++ b/tensorflow/g3doc/how_tos/adding_an_op/index.md
@@ -3,7 +3,10 @@
PREREQUISITES:
* Some familiarity with C++.
-* Must have [downloaded TensorFlow source](../../get_started/os_setup.md#installing-from-sources),
+* Must have installed the
+ [TensorFlow binary](../../get_started/os_setup.md#pip-installation), or must
+ have
+ [downloaded TensorFlow source](../../get_started/os_setup.md#installing-from-sources),
and be able to build it.
If you'd like to incorporate an operation that isn't covered by the existing
@@ -16,13 +19,13 @@ to:
* Implement the Op in C++. This implementation is called a "kernel", and there
can be multiple kernels for different architectures (e.g. CPUs, GPUs) or
input / output types.
-* Create a Python wrapper. This wrapper is the public API to create the Op. A
- default wrapper is generated from the Op registration, which can be used
- directly or added to.
+* Optionally, create a Python wrapper. This wrapper is the public API to create
+ the Op. A default wrapper is generated from the Op registration, which can be
+ used directly or added to.
* Optionally, write a function to compute gradients for the Op.
* Optionally, write a function that describes the input and output shapes
for the Op. This allows shape inference to work with your Op.
-* Test the Op, typically in Python. If you define gradients, you can verify them with the Python [`GradientChecker`](https://www.tensorflow.org/code/tensorflow/python/kernel_tests/gradient_checker.py).
+* Test the Op, typically in Python. If you define gradients, you can verify them with the Python [`GradientChecker`](https://www.tensorflow.org/code/tensorflow/python/kernel_tests/gradient_checker.py).
[TOC]
@@ -106,68 +109,96 @@ To do this for the `ZeroOut` op, add the following to `zero_out.cc`:
REGISTER_KERNEL_BUILDER(Name("ZeroOut").Device(DEVICE_CPU), ZeroOutOp);
```
-Once you
-[build and reinstall TensorFlow](../../get_started/os_setup.md#create-the-pip-package-and-install), the
-Tensorflow system can reference and use the Op when requested.
+## Building the Op library
+### With TensorFlow binary installation
+
+You should be able to compile `zero_out.cc` with a `C++` compiler such as `g++`
+or `clang` available on your system. The binary PIP package installs the header
+files and the library that you need to compile your Op in locations that are
+system specific. However, the TensorFlow python library provides functions
+`get_include` and `get_lib` to get the header and library directories
+respectively. Here is the output of those functions on a Ubuntu machine.
+
+```bash
+$ python
+>>> import tensorflow as tf
+>>> tf.sysconfig.get_include()
+'/usr/local/lib/python2.7/site-packages/tensorflow/include'
+>>> tf.sysconfig.get_lib()
+'/usr/local/lib/python2.7/site-packages/tensorflow/core'
+>>>
-## Generate the client wrapper
-### The Python Op wrapper
-
-Python op wrappers are created automatically in
-`bazel-genfiles/tensorflow/python/ops/gen_user_ops.py` for all ops placed in the
-[`tensorflow/core/user_ops`][user_ops] directory when you build Tensorflow.
+```
-> Note: The generated function will be given a snake\_case name (to comply with
-> [PEP8](https://www.python.org/dev/peps/pep-0008/)). So if your op is named
-> `ZeroOut` in the C++ files, the python function will be called `zero_out`.
+Assuming you have `g++` installed, here is the sequence of commands you can use
+to compile your Op into a dynamic library.
-Those ops are imported into
-[`tensorflow/python/user_ops/user_ops.py`][python-user_ops] with the statement:
+```bash
+$ TF_INC=$(python -c 'import tensorflow as tf; print(tf.sysconfig.get_include())')
+$ TF_LIB=$(python -c 'import tensorflow as tf; print(tf.sysconfig.get_lib())')
-```python
-from tensorflow.python.ops.gen_user_ops import *
+$ g++ -std=c++11 -shared zero_out.cc -o zero_out.so \
+-I $TF_INC -l tensorflow_framework -L $TF_LIB \
+-fPIC -Wl,-rpath $TF_LIB
```
-You may optionally use your own function instead. To do this, you first hide
-the generated code for that op by adding its name to the `hidden` list in the
-`"user_ops"` rule in
-[`tensorflow/python/BUILD`](https://www.tensorflow.org/code/tensorflow/python/BUILD):
+### With TensorFlow source installation
+
+If you have TensorFlow sources installed, you can make use of TensorFlow's build
+system to compile your Op. The following Bazel build rule would build
+`zero_out.so`.
```python
-tf_gen_op_wrapper_py(
- name = "user_ops",
- hidden = [
- "Fact",
+cc_binary(
+ name = "zero_out.so",
+ srcs = ["zero_out.cc"],
+ linkopts = [
+ "-Wl,-Bsymbolic",
+ "-lm",
+ ],
+ linkshared = 1,
+ linkstatic = 1,
+ deps = [
+ "//third_party/tensorflow/core:framework",
],
- require_shape_functions = False,
)
```
-List your op next to `"Fact"`. Next you add your replacement function to
-[`tensorflow/python/user_ops/user_ops.py`](https://www.tensorflow.org/code/tensorflow/python/user_ops/user_ops.py).
-Typically your function will call the generated function to actually add the op
-to the graph. The hidden version of the generated function will be in the
-`gen_user_ops` package and start with an underscore ("`_`"). For example:
+## Using the Op in Python
+
+TensorFlow Python API provides the
+[load_op_library](../../api_docs/python/framework#load_op_library) function to
+load the dynamic library and register the Op with the TensorFlow
+framework. `load_op_library` returns a Python module, that contains the Python
+wrappers for the Op. Thus, once you have built the op, you can do the following
+to run it from Python :
```python
-def my_fact():
- """Example of overriding the generated code for an Op."""
- return gen_user_ops._fact()
+import tensorflow as tf
+zero_out_module = tf.load_op_library('zero_out.so')
+with tf.Session(''):
+ zero_out_module.zero_out([[1, 2], [3, 4]]).eval()
+
+# Prints
+array([[1, 0],
+ [0, 0]], dtype=int32)
```
-### The C++ Op wrapper
+> Note: The generated function will be given a snake\_case name (to comply with
+> [PEP8](https://www.python.org/dev/peps/pep-0008/)). So if your op is named
+> `ZeroOut` in the C++ files, the python function will be called `zero_out`.
-C++ op wrappers are created automatically for all ops placed in the
-[`tensorflow/core/user_ops`][user_ops] directory, when you build Tensorflow. For
-example, ops in `tensorflow/core/user_ops/zero_out.cc` will generate wrappers in
-`bazel-genfiles/tensorflow/cc/ops/user_ops.{h,cc}`.
+To make the Op available as a regular function `import`-able from a Python
+module, it maybe useful to have the `load_op_library` call in a Python source
+file as follows (see
+[zero_out_op_1.py](https://www.tensorflow.org/code/tensorflow/g3doc/how_tos/adding_an_op/zero_out_op_1.py))
+:
-All generated wrappers for user ops are automatically
-imported into [`tensorflow/cc/ops/standard_ops.h`][standard_ops-cc] with the
-statement
+```python
+import tensorflow as tf
-```c++
-#include "tensorflow/cc/ops/user_ops.h"
+_zero_out_module = tf.load_op_library('zero_out_op_kernel_1.so')
+zero_out = _zero_out_module.zero_out
```
## Verify it works
@@ -179,11 +210,11 @@ test for it. Create the file
```python
import tensorflow as tf
-
class ZeroOutTest(tf.test.TestCase):
def testZeroOut(self):
+ zero_out_module = tf.load_op_library('zero_out.so')
with self.test_session():
- result = tf.user_ops.zero_out([5, 4, 3, 2, 1])
+ result = zero_out_module.zero_out([5, 4, 3, 2, 1])
self.assertAllEqual(result.eval(), [5, 0, 0, 0, 0])
```
diff --git a/tensorflow/g3doc/how_tos/adding_an_op/zero_out_1_test.py b/tensorflow/g3doc/how_tos/adding_an_op/zero_out_1_test.py
index 84defddc64..3b6efc53d8 100644
--- a/tensorflow/g3doc/how_tos/adding_an_op/zero_out_1_test.py
+++ b/tensorflow/g3doc/how_tos/adding_an_op/zero_out_1_test.py
@@ -22,14 +22,14 @@ from __future__ import print_function
import tensorflow.python.platform
import tensorflow as tf
-from tensorflow.g3doc.how_tos.adding_an_op import gen_zero_out_op_1
+from tensorflow.g3doc.how_tos.adding_an_op import zero_out_op_1
class ZeroOut1Test(tf.test.TestCase):
def test(self):
with self.test_session():
- result = gen_zero_out_op_1.zero_out([5, 4, 3, 2, 1])
+ result = zero_out_op_1.zero_out([5, 4, 3, 2, 1])
self.assertAllEqual(result.eval(), [5, 0, 0, 0, 0])
diff --git a/tensorflow/g3doc/how_tos/adding_an_op/zero_out_2_test.py b/tensorflow/g3doc/how_tos/adding_an_op/zero_out_2_test.py
index 25229213b9..e72dba9f78 100644
--- a/tensorflow/g3doc/how_tos/adding_an_op/zero_out_2_test.py
+++ b/tensorflow/g3doc/how_tos/adding_an_op/zero_out_2_test.py
@@ -22,7 +22,7 @@ from __future__ import print_function
import tensorflow.python.platform
import tensorflow as tf
-from tensorflow.g3doc.how_tos.adding_an_op import gen_zero_out_op_2
+from tensorflow.g3doc.how_tos.adding_an_op import zero_out_op_2
from tensorflow.g3doc.how_tos.adding_an_op import zero_out_grad_2
@@ -30,14 +30,14 @@ class ZeroOut2Test(tf.test.TestCase):
def test(self):
with self.test_session():
- result = gen_zero_out_op_2.zero_out([5, 4, 3, 2, 1])
+ result = zero_out_op_2.zero_out([5, 4, 3, 2, 1])
self.assertAllEqual(result.eval(), [5, 0, 0, 0, 0])
def test_grad(self):
with self.test_session():
shape = (5,)
x = tf.constant([5, 4, 3, 2, 1], dtype=tf.float32)
- y = gen_zero_out_op_2.zero_out(x)
+ y = zero_out_op_2.zero_out(x)
err = tf.test.compute_gradient_error(x, shape, y, shape)
self.assertLess(err, 1e-4)
diff --git a/tensorflow/g3doc/how_tos/adding_an_op/zero_out_3_test.py b/tensorflow/g3doc/how_tos/adding_an_op/zero_out_3_test.py
index 99ab43c93d..5c9be2f8d5 100644
--- a/tensorflow/g3doc/how_tos/adding_an_op/zero_out_3_test.py
+++ b/tensorflow/g3doc/how_tos/adding_an_op/zero_out_3_test.py
@@ -22,30 +22,30 @@ from __future__ import print_function
import tensorflow.python.platform
import tensorflow as tf
-from tensorflow.g3doc.how_tos.adding_an_op import gen_zero_out_op_3
+from tensorflow.g3doc.how_tos.adding_an_op import zero_out_op_3
class ZeroOut3Test(tf.test.TestCase):
def test(self):
with self.test_session():
- result = gen_zero_out_op_3.zero_out([5, 4, 3, 2, 1])
+ result = zero_out_op_3.zero_out([5, 4, 3, 2, 1])
self.assertAllEqual(result.eval(), [5, 0, 0, 0, 0])
def testAttr(self):
with self.test_session():
- result = gen_zero_out_op_3.zero_out([5, 4, 3, 2, 1], preserve_index=3)
+ result = zero_out_op_3.zero_out([5, 4, 3, 2, 1], preserve_index=3)
self.assertAllEqual(result.eval(), [0, 0, 0, 2, 0])
def testNegative(self):
with self.test_session():
- result = gen_zero_out_op_3.zero_out([5, 4, 3, 2, 1], preserve_index=-1)
+ result = zero_out_op_3.zero_out([5, 4, 3, 2, 1], preserve_index=-1)
with self.assertRaisesOpError("Need preserve_index >= 0, got -1"):
result.eval()
def testLarge(self):
with self.test_session():
- result = gen_zero_out_op_3.zero_out([5, 4, 3, 2, 1], preserve_index=17)
+ result = zero_out_op_3.zero_out([5, 4, 3, 2, 1], preserve_index=17)
with self.assertRaisesOpError("preserve_index out of range"):
result.eval()
diff --git a/tensorflow/g3doc/how_tos/adding_an_op/zero_out_op_1.py b/tensorflow/g3doc/how_tos/adding_an_op/zero_out_op_1.py
new file mode 100644
index 0000000000..a54e8ec4c8
--- /dev/null
+++ b/tensorflow/g3doc/how_tos/adding_an_op/zero_out_op_1.py
@@ -0,0 +1,31 @@
+# Copyright 2015 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# ==============================================================================
+"""ZeroOut op Python library."""
+from __future__ import absolute_import
+from __future__ import division
+from __future__ import print_function
+
+# pylint: disable=g-bad-import-order
+# pylint: disable=unused-import
+import os.path
+
+import tensorflow.python.platform
+
+import tensorflow as tf
+
+_zero_out_module = tf.load_op_library(
+ os.path.join(tf.resource_loader.get_data_files_path(),
+ 'zero_out_op_kernel_1.so'))
+zero_out = _zero_out_module.zero_out
diff --git a/tensorflow/g3doc/how_tos/adding_an_op/zero_out_op_2.py b/tensorflow/g3doc/how_tos/adding_an_op/zero_out_op_2.py
new file mode 100644
index 0000000000..8889e9ae4e
--- /dev/null
+++ b/tensorflow/g3doc/how_tos/adding_an_op/zero_out_op_2.py
@@ -0,0 +1,33 @@
+# Copyright 2015 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# ==============================================================================
+"""ZeroOut ops Python library."""
+from __future__ import absolute_import
+from __future__ import division
+from __future__ import print_function
+
+# pylint: disable=g-bad-import-order
+# pylint: disable=unused-import
+import os.path
+
+import tensorflow.python.platform
+
+import tensorflow as tf
+
+_zero_out_module = tf.load_op_library(
+ os.path.join(tf.resource_loader.get_data_files_path(),
+ 'zero_out_op_kernel_2.so'))
+zero_out = _zero_out_module.zero_out
+zero_out2 = _zero_out_module.zero_out2
+zero_out3 = _zero_out_module.zero_out3
diff --git a/tensorflow/g3doc/how_tos/adding_an_op/zero_out_op_3.py b/tensorflow/g3doc/how_tos/adding_an_op/zero_out_op_3.py
new file mode 100644
index 0000000000..239247f1c7
--- /dev/null
+++ b/tensorflow/g3doc/how_tos/adding_an_op/zero_out_op_3.py
@@ -0,0 +1,31 @@
+# Copyright 2015 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# ==============================================================================
+"""ZeroOut op Python library."""
+from __future__ import absolute_import
+from __future__ import division
+from __future__ import print_function
+
+# pylint: disable=g-bad-import-order
+# pylint: disable=unused-import
+import os.path
+
+import tensorflow.python.platform
+
+import tensorflow as tf
+
+_zero_out_module = tf.load_op_library(
+ os.path.join(tf.resource_loader.get_data_files_path(),
+ 'zero_out_op_kernel_3.so'))
+zero_out = _zero_out_module.zero_out
diff --git a/tensorflow/python/__init__.py b/tensorflow/python/__init__.py
index 4ac298a3bf..08c815a171 100644
--- a/tensorflow/python/__init__.py
+++ b/tensorflow/python/__init__.py
@@ -80,12 +80,13 @@ from tensorflow.python.platform import app
from tensorflow.python.platform import flags
from tensorflow.python.platform import logging
from tensorflow.python.platform import resource_loader
+from tensorflow.python.platform import sysconfig
from tensorflow.python.platform import test
# Don't export modules except for the few we really want
_whitelist = set([app, compat, contrib, errors, flags, image, learn, logging, nn,
- python_io, resource_loader, test, train, unsupported,
- user_ops])
+ python_io, resource_loader, sysconfig, test, train,
+ unsupported, user_ops])
# TODO(b/25561952): tf.tensor_util is DEPRECATED. Please avoid.
_whitelist.update([tensor_util]) # pylint: disable=undefined-variable
__all__ = [name for name, x in locals().items() if not name.startswith('_') and
diff --git a/tensorflow/python/platform/sysconfig.py b/tensorflow/python/platform/sysconfig.py
new file mode 100644
index 0000000000..8b5b4069d6
--- /dev/null
+++ b/tensorflow/python/platform/sysconfig.py
@@ -0,0 +1,46 @@
+# Copyright 2015 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# ==============================================================================
+
+"""Load a file resource and return the contents."""
+from __future__ import absolute_import
+from __future__ import division
+from __future__ import print_function
+
+import os.path
+
+
+# pylint: disable=g-import-not-at-top
+def get_include():
+ """Get the directory containing the TensorFlow C++ header files.
+
+ Returns:
+ The directory as string.
+ """
+ # Import inside the function.
+ # sysconfig is imported from the tensorflow module, so having this
+ # import at the top would cause a circular import, resulting in
+ # the tensorflow module missing symbols that come after sysconfig.
+ import tensorflow as tf
+ return os.path.join(os.path.dirname(tf.__file__), 'include')
+
+
+def get_lib():
+ """Get the directory containing the TensorFlow framework library.
+
+ Returns:
+ The directory as string.
+ """
+ import tensorflow as tf
+ return os.path.join(os.path.dirname(tf.__file__), 'core')
diff --git a/tensorflow/tools/pip_package/setup.py b/tensorflow/tools/pip_package/setup.py
index 128ad8a52d..44eb136c8d 100644
--- a/tensorflow/tools/pip_package/setup.py
+++ b/tensorflow/tools/pip_package/setup.py
@@ -99,6 +99,19 @@ class InstallHeaders(Command):
# directories for -I
install_dir = re.sub('/google/protobuf/src', '', install_dir)
+ # Copy eigen code into tensorflow/include and
+ # tensorflow/include/external/eigen_archive/eigen-eigen-<revision>.
+ # A symlink would do, but the wheel file that gets created ignores
+ # symlink within the directory hierarchy.
+ # NOTE(keveman): Figure out how to customize bdist_wheel package so
+ # we can do the symlink.
+ if re.search(r'(external/eigen_archive/eigen-eigen-\w+)', install_dir):
+ extra_dir = re.sub(r'/external/eigen_archive/eigen-eigen-\w+', '',
+ install_dir)
+ if not os.path.exists(extra_dir):
+ self.mkpath(extra_dir)
+ self.copy_file(header, extra_dir)
+
if not os.path.exists(install_dir):
self.mkpath(install_dir)
return self.copy_file(header, install_dir)