• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2017 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"""State management for eager execution."""
16
17from __future__ import absolute_import
18from __future__ import division
19from __future__ import print_function
20
21import collections
22import contextlib
23import copy
24import random
25import threading
26
27from absl import logging
28import numpy as np
29import six
30
31from tensorflow.core.protobuf import config_pb2
32from tensorflow.core.protobuf import rewriter_config_pb2
33from tensorflow.python import pywrap_tfe
34from tensorflow.python import tf2
35from tensorflow.python.client import pywrap_tf_session
36from tensorflow.python.eager import executor
37from tensorflow.python.eager import monitoring
38from tensorflow.python.framework import c_api_util
39from tensorflow.python.framework import device as pydev
40from tensorflow.python.util import compat
41from tensorflow.python.util import is_in_graph_mode
42from tensorflow.python.util import tf_contextlib
43from tensorflow.python.util.tf_export import tf_export
44
45GRAPH_MODE = 0
46EAGER_MODE = 1
47
48default_execution_mode = EAGER_MODE if tf2.enabled() else GRAPH_MODE
49
50# Cache from (old_device_name, partial_new_device_name) -> (new_device_name,
51# new_device_spec).
52# Note that we do not protect this with a lock and instead rely on python's GIL
53# and the idempotent nature of writes to provide thread safety.
54_device_parsing_cache = {}
55_starting_device_spec = pydev.DeviceSpec.from_string("")
56
57_MAXINT32 = 2**31 - 1
58
59DEVICE_PLACEMENT_EXPLICIT = pywrap_tfe.TFE_DEVICE_PLACEMENT_EXPLICIT
60DEVICE_PLACEMENT_WARN = pywrap_tfe.TFE_DEVICE_PLACEMENT_WARN
61DEVICE_PLACEMENT_SILENT = pywrap_tfe.TFE_DEVICE_PLACEMENT_SILENT
62DEVICE_PLACEMENT_SILENT_FOR_INT32 = (
63    pywrap_tfe.TFE_DEVICE_PLACEMENT_SILENT_FOR_INT32)
64
65SYNC = 0
66ASYNC = 1
67
68MIRRORING_NONE = pywrap_tfe.TFE_MIRRORING_NONE
69MIRRORING_ALL = pywrap_tfe.TFE_MIRRORING_ALL
70
71_KEEP_ALIVE_SECS = 600
72
73_python_eager_context_create_counter = monitoring.Counter(
74    "/tensorflow/api/python/eager_context_create_counter",
75    "Counter for number of eager contexts created in Python.")
76
77
78class _EagerTensorCache(object):
79  """Simple cache which evicts items based on length in a FIFO manner."""
80
81  def __init__(self, max_items=256, max_tensor_size=10000):
82    self._data = collections.OrderedDict()
83    self._max_items = max_items
84    self._max_tensor_size = max_tensor_size
85
86  def put(self, key, value):
87    if value._num_elements() > self._max_tensor_size:  # pylint: disable=protected-access
88      return
89
90    self._data[key] = value
91
92    if len(self._data) > self._max_items:
93      self._data.popitem(last=False)
94
95  def get(self, key):
96    return self._data.get(key, None)
97
98  def flush(self):
99    self._data = {}
100
101
102class FunctionCallOptions(object):
103  """Options applied at call sites of eager functions.
104
105  Eager functions are functions decorated with tf.contrib.eager.defun.
106  """
107
108  def __init__(self, executor_type=None, config_proto=None):
109    """Constructor.
110
111    Args:
112      executor_type: (optional) name of the executor to be used to execute the
113        eager function. If None or an empty string, the default Tensorflow
114        executor will be used.
115      config_proto: (optional) a `config_pb2.ConfigProto` proto or
116        a serialized string of that proto.
117        The config used by Grappler when optimizing the function graph.
118        Each concrete function is optimized the first time is called. Changing
119        config_proto after the first call has no effect.
120        If config_proto is None, an empty RewriterConfig will be used.
121    """
122    self.config_proto_serialized = config_proto
123    self.executor_type = executor_type
124
125  @property
126  def executor_type(self):
127    return self._executor_type
128
129  @executor_type.setter
130  def executor_type(self, executor_type):
131    self._executor_type = executor_type
132
133  @property
134  def config_proto_serialized(self):
135    return self._config_proto_serialized
136
137  @config_proto_serialized.setter
138  def config_proto_serialized(self, config):
139    if isinstance(config, config_pb2.ConfigProto):
140      self._config_proto_serialized = config.SerializeToString()
141    elif isinstance(config, str):
142      self._config_proto_serialized = config
143    elif config is None:
144      self._config_proto_serialized = (
145          config_pb2.ConfigProto().SerializeToString())
146    else:
147      raise ValueError("the rewriter config must be either a "
148                       "config_pb2.ConfigProto, or a serialized string of that "
149                       "proto or None. got: {}".format(type(config)))
150
151
152# Map from context_id (an int) to _TensorCaches.
153# Dicts are thread safe in CPython.
154# TODO(iga): Remove this once TensorCaches are moved to C++.
155_tensor_caches_map = {}
156
157
158class _TensorCaches(threading.local):
159  """Thread local tensor caches."""
160
161  def __init__(self):
162    super(_TensorCaches, self).__init__()
163    self._ones_rank_cache = None
164    self._zeros_cache = None
165
166  @property
167  def ones_rank_cache(self):
168    if not self._ones_rank_cache:
169      self._ones_rank_cache = _EagerTensorCache()
170    return self._ones_rank_cache
171
172  @property
173  def zeros_cache(self):
174    if not self._zeros_cache:
175      self._zeros_cache = _EagerTensorCache()
176    return self._zeros_cache
177
178
179class _ThreadLocalData(threading.local):
180  """Thread local storage for the eager context."""
181
182  def __init__(self):
183    super(_ThreadLocalData, self).__init__()
184    self.device_spec = _starting_device_spec
185    self.device_name = ""
186    self.is_eager = default_execution_mode == EAGER_MODE
187    self.scope_name = ""
188    self.function_call_options = None
189    self.executor = None
190    self.op_callbacks = []
191    self.invoking_op_callbacks = False
192
193
194ContextSwitch = collections.namedtuple(
195    "ContextSwitch", ["is_building_function", "enter_context_fn",
196                      "device_stack"])
197
198
199# `_ContextSwitchStack` is a `threading.local` to match the semantics of
200# ``DefaultGraphStack`, which is also a `threading.local`.
201class _ContextSwitchStack(threading.local):
202  """A thread-local stack of context switches."""
203
204  def __init__(self, eager):
205    super(_ContextSwitchStack, self).__init__()
206    self.stack = []
207    if eager:
208      # Initialize the stack with a pointer to enter the eager context; this
209      # ensures that the fact that eager execution was enabled is propagated
210      # across threads, since (1) `enable_eager_execution` modifies a
211      # process-level flag (`default_execution_mode`) and (2) `__init__` is
212      # called each time a threading.local object is used in a separate thread.
213      self.push(is_building_function=False, enter_context_fn=eager_mode,
214                device_stack=None)
215
216  def push(self, is_building_function, enter_context_fn, device_stack):
217    """Push metadata about a context switch onto the stack.
218
219    A context switch can take any one of the two forms: installing a graph as
220    the default graph, or entering the eager context. For each context switch,
221    we record whether or not the entered context is building a function.
222
223    Args:
224      is_building_function: (bool.) Whether the context is building a function.
225      enter_context_fn: (function.) A callable that executes the context switch.
226        For example, `graph.as_default` or `eager_mode`.
227      device_stack: If applicable, the device function stack for this
228        graph. When breaking out of graphs in init_scope, the innermost nonempty
229        device stack is used. Eager contexts put `None` here and the value is
230        never used.
231    """
232
233    self.stack.append(
234        ContextSwitch(is_building_function, enter_context_fn, device_stack))
235
236  def pop(self):
237    """Pop the stack."""
238
239    self.stack.pop()
240
241
242@tf_export("config.LogicalDevice")
243class LogicalDevice(
244    collections.namedtuple("LogicalDevice", ["name", "device_type"])):
245  """Abstraction for a logical device initialized by the runtime.
246
247  A `tf.config.LogicalDevice` corresponds to an initialized logical device on a
248  `tf.config.PhysicalDevice` or a remote device visible to the cluster. Tensors
249  and operations can be placed on a specific logical device by calling
250  `tf.device` with a specified `tf.config.LogicalDevice`.
251
252  Fields:
253    name: The fully qualified name of the device. Can be used for Op or function
254      placement.
255    device_type: String declaring the type of device such as "CPU" or "GPU".
256  """
257  pass
258
259
260@tf_export("config.LogicalDeviceConfiguration",
261           "config.experimental.VirtualDeviceConfiguration")
262class LogicalDeviceConfiguration(
263    collections.namedtuple("LogicalDeviceConfiguration", ["memory_limit"])):
264  """Configuration class for a logical devices.
265
266  The class specifies the parameters to configure a `tf.config.PhysicalDevice`
267  as it is initialized to a `tf.config.LogicalDevice` during runtime
268  initialization. Not all fields are valid for all device types.
269
270  See `tf.config.get_logical_device_configuration` and
271  `tf.config.set_logical_device_configuration` for usage examples.
272
273  Fields:
274    memory_limit: (optional) Maximum memory (in MB) to allocate on the virtual
275      device. Currently only supported for GPUs.
276  """
277
278  def __new__(cls, memory_limit=None):
279    return super(LogicalDeviceConfiguration, cls).__new__(cls, memory_limit)
280
281
282@tf_export("config.PhysicalDevice")
283class PhysicalDevice(
284    collections.namedtuple("PhysicalDevice", ["name", "device_type"])):
285  """Abstraction for a locally visible physical device.
286
287  TensorFlow can utilize various devices such as the CPU or multiple GPUs
288  for computation. Before initializing a local device for use, the user can
289  customize certain properties of the device such as it's visibility or memory
290  configuration.
291
292  Once a visible `tf.config.PhysicalDevice` is initialized one or more
293  `tf.config.LogicalDevice` objects are created. Use
294  `tf.config.set_visible_devices` to configure the visibility of a physical
295  device and `tf.config.set_logical_device_configuration` to configure multiple
296  `tf.config.LogicalDevice` objects for a `tf.config.PhysicalDevice`. This is
297  useful when separation between models is needed or to simulate a multi-device
298  environment.
299
300  Fields:
301    name: Unique identifier for device.
302    device_type: String declaring the type of device such as "CPU" or "GPU".
303  """
304  pass
305
306
307class _AtomicCounter(object):
308  """A simple atomic counter."""
309
310  def __init__(self):
311    self._value = 0
312    self._lock = threading.Lock()
313
314  def increment_and_get(self):
315    with self._lock:
316      self._value += 1
317      return self._value
318
319
320_context_id_counter = _AtomicCounter()
321
322
323class _TensorCacheDeleter(object):
324  """Deletes tensor caches for a given context."""
325
326  def __init__(self, context_id):
327    self._context_id = context_id
328
329  def __del__(self):
330    if _tensor_caches_map is None:
331      return
332    if self._context_id in _tensor_caches_map:
333      del _tensor_caches_map[self._context_id]
334
335
336# TODO(agarwal): rename to EagerContext / EagerRuntime ?
337# TODO(agarwal): consider keeping the corresponding Graph here.
338class Context(object):
339  """Environment in which eager operations execute."""
340
341  # TODO(agarwal): create and link in some documentation for `execution_mode`.
342  # pylint: disable=redefined-outer-name
343  def __init__(self,
344               config=None,
345               device_policy=None,
346               execution_mode=None,
347               server_def=None):
348    """Creates a new Context.
349
350    Args:
351      config: (Optional.) A `ConfigProto` protocol buffer with configuration
352        options for the Context. Note that a lot of these options may be
353        currently unimplemented or irrelevant when eager execution is enabled.
354      device_policy: (Optional.) What policy to use when trying to run an
355        operation on a device with inputs which are not on that device.
356        When set to None, an appropriate value will be picked automatically.
357        The value picked may change between TensorFlow releases.
358
359        Defaults to DEVICE_PLACEMENT_SILENT.
360        Valid values:
361        - DEVICE_PLACEMENT_EXPLICIT: raises an error if the placement is
362          not correct.
363        - DEVICE_PLACEMENT_WARN: copies the tensors which are not on the
364          right device but raises a warning.
365        - DEVICE_PLACEMENT_SILENT: silently copies the tensors. This might
366          hide performance problems.
367        - DEVICE_PLACEMENT_SILENT_FOR_INT32: silently copies int32 tensors,
368          raising errors on the other ones.
369      execution_mode: (Optional.) Policy controlling how operations dispatched
370        are actually executed. When set to None, an appropriate value will be
371        picked automatically. The value picked may change between TensorFlow
372        releases.
373        Valid values:
374        - SYNC: executes each operation synchronously.
375        - ASYNC: executes each operation asynchronously. These
376          operations may return "non-ready" handles.
377      server_def: (Optional.) A tensorflow::ServerDef proto.
378        Enables execution on remote devices. GrpcServers need to be started by
379        creating an identical server_def to this, and setting the appropriate
380        task_indexes, so that the servers can communicate. It will then be
381        possible to execute operations on remote devices.
382
383    Raises:
384     ValueError: If execution_mode is not valid.
385    """
386    # This _id is used only to index the tensor caches.
387    # TODO(iga): Remove this when tensor caches are moved to C++.
388    self._id = _context_id_counter.increment_and_get()
389    self._tensor_cache_deleter = _TensorCacheDeleter(self._id)
390    _tensor_caches_map[self._id] = _TensorCaches()
391
392    self._config = config
393    self._thread_local_data = _ThreadLocalData()
394    self._context_switches = _ContextSwitchStack(self.executing_eagerly())
395    self._context_handle = None
396    self._context_devices = None
397    self._seed = None
398    self._initialize_lock = threading.Lock()
399    self._initialized = False
400    if device_policy is None:
401      device_policy = DEVICE_PLACEMENT_SILENT
402    self._device_policy = device_policy
403    self._mirroring_policy = None
404    if execution_mode not in (None, SYNC, ASYNC):
405      raise ValueError(
406          "execution_mode should be None/SYNC/ASYNC. Got %s" % execution_mode)
407    if execution_mode is None:
408      execution_mode = SYNC
409    self._default_is_async = execution_mode == ASYNC
410    self._lazy_remote_inputs_copy = None
411    self._server_def = server_def
412    self._collective_ops_server_def = None
413    self._collective_leader = None
414    self._collective_scoped_allocator_enabled_ops = None
415    self._collective_use_nccl_communication = None
416    self._collective_device_filters = None
417
418    self._device_lock = threading.Lock()
419    self._physical_devices = None
420    self._visible_device_list = []
421    self._memory_growth_map = None
422    self._virtual_device_map = {}
423
424    # Values set after construction
425    self._optimizer_jit = None
426    self._intra_op_parallelism_threads = None
427    self._inter_op_parallelism_threads = None
428    self._soft_device_placement = None
429    self._log_device_placement = None
430    self._enable_mlir_bridge = None
431    self._optimizer_experimental_options = {}
432
433    _python_eager_context_create_counter.get_cell().increase_by(1)
434  # pylint: enable=redefined-outer-name
435
436  def _set_global_seed(self, seed):
437    """Set a global eager mode seed for random ops."""
438    self._seed = seed
439    # `random.Random(seed)` needs `seed` to be hashable, while values of type
440    # e.g. `np.int64` or `np.ndarray` are not. We use `int(...)` to convert them
441    # to int.
442    try:
443      hash(seed)
444    except TypeError:
445      seed = int(np.array(seed))
446    self._rng = random.Random(seed)
447    # Also clear the kernel cache, to reset any existing seeds
448    if self._context_handle is not None:
449      pywrap_tfe.TFE_ContextClearCaches(self._context_handle)
450
451  def _internal_operation_seed(self):
452    """Returns a fake operation seed.
453
454      In eager mode, user shouldn't set or depend on operation seed.
455      Here, we generate a random seed based on global seed to make
456      operation's randomness different and depend on the global seed.
457
458    Returns:
459      A fake operation seed based on global seed.
460    """
461    return self._rng.randint(0, _MAXINT32)
462
463  def _initialize_logical_devices(self):
464    """Helper to initialize devices."""
465    # Store list of devices
466    logical_devices = []
467    context_devices = []
468    device_list = pywrap_tfe.TFE_ContextListDevices(self._context_handle)
469    try:
470      self._num_gpus = 0
471      for i in range(pywrap_tfe.TF_DeviceListCount(device_list)):
472        dev_name = pywrap_tfe.TF_DeviceListName(device_list, i)
473        context_devices.append(pydev.canonical_name(dev_name))
474        spec = pydev.DeviceSpec.from_string(dev_name)
475        # If the job is localhost, we assume that the cluster has not yet been
476        # configured and thus clear the job, replica & task.
477        if spec.job == "localhost":
478          spec = spec.replace(job=None, replica=None, task=None)
479        logical_devices.append(
480            LogicalDevice(name=spec.to_string(), device_type=spec.device_type))
481        dev_type = pywrap_tfe.TF_DeviceListType(device_list, i)
482        if dev_type == "GPU":
483          self._num_gpus += 1
484
485    finally:
486      self._logical_devices = logical_devices
487      self._context_devices = context_devices
488      pywrap_tfe.TF_DeleteDeviceList(device_list)
489
490  def ensure_initialized(self):
491    """Initialize handle and devices if not already done so."""
492    if self._initialized:
493      return
494    with self._initialize_lock:
495      if self._initialized:
496        return
497      assert self._context_devices is None
498      opts = pywrap_tfe.TFE_NewContextOptions()
499      try:
500        config_str = self.config.SerializeToString()
501        pywrap_tfe.TFE_ContextOptionsSetConfig(opts, config_str)
502        if self._device_policy is not None:
503          pywrap_tfe.TFE_ContextOptionsSetDevicePlacementPolicy(
504              opts, self._device_policy)
505        if self._mirroring_policy is not None:
506          pywrap_tfe.TFE_ContextOptionsSetMirroringPolicy(
507              opts, self._mirroring_policy)
508        if self._default_is_async == ASYNC:
509          pywrap_tfe.TFE_ContextOptionsSetAsync(opts, True)
510        if self._lazy_remote_inputs_copy is not None:
511          pywrap_tfe.TFE_ContextOptionsSetLazyRemoteInputsCopy(
512              opts, self._lazy_remote_inputs_copy)
513        context_handle = pywrap_tfe.TFE_NewContext(opts)
514      finally:
515        pywrap_tfe.TFE_DeleteContextOptions(opts)
516      assert not (self._server_def and self._collective_ops_server_def), (
517          "Cannot enable remote execution as well as collective ops at the "
518          "moment. If this is important to you, please file an issue.")
519      if self._server_def is not None:
520        server_def_str = self._server_def.SerializeToString()
521        pywrap_tfe.TFE_ContextSetServerDef(context_handle, _KEEP_ALIVE_SECS,
522                                           server_def_str)
523      elif self._collective_ops_server_def is not None:
524        server_def_str = self._collective_ops_server_def.SerializeToString()
525        pywrap_tfe.TFE_EnableCollectiveOps(context_handle, server_def_str)
526
527      self._context_handle = context_handle
528      self._initialize_logical_devices()
529      self._initialized = True
530
531  def _clear_caches(self):
532    self.ones_rank_cache().flush()
533    self.zeros_cache().flush()
534    pywrap_tfe.TFE_ClearScalarCache()
535
536  def get_server_def(self):
537    return self._server_def
538
539  def set_server_def(self, server_def, keep_alive_secs=_KEEP_ALIVE_SECS):
540    """Allow setting a server_def on the context.
541
542    When a server def is replaced, it effectively clears a bunch of caches
543    within the context. If you attempt to use a tensor object that was pointing
544    to a tensor on the remote device, it will raise an error.
545
546    Args:
547      server_def: A tensorflow::ServerDef proto.
548        Enables execution on remote devices.
549      keep_alive_secs: Num. seconds after which the remote end will hang up.
550        As long as the client is still alive, the server state for the context
551        will be kept alive. If the client is killed (or there is some failure),
552        the server will clean up its context keep_alive_secs after the final RPC
553        it receives.
554
555    Raises:
556      ValueError: if server_def is None.
557    """
558    if not server_def:
559      raise ValueError("server_def is None.")
560
561    self._server_def = server_def
562
563    if self._context_handle:
564      server_def_str = server_def.SerializeToString()
565      pywrap_tfe.TFE_ContextSetServerDef(self._context_handle, keep_alive_secs,
566                                         server_def_str)
567      self._initialize_logical_devices()
568
569    # Clear all the caches in case there are remote tensors in them.
570    self._clear_caches()
571
572  def update_server_def(self, server_def, keep_alive_secs=_KEEP_ALIVE_SECS):
573    """Update a server_def on the context.
574
575    Args:
576      server_def: A tensorflow::ServerDef proto. Enables execution on remote
577        devices.
578      keep_alive_secs: Num. seconds after which the remote end will hang up. As
579        long as the client is still alive, the server state for the context will
580        be kept alive. If the client is killed (or there is some failure), the
581        server will clean up its context keep_alive_secs after the final RPC it
582        receives.
583
584    Raises:
585      ValueError: if server_def is None.
586    """
587    if not server_def:
588      raise ValueError("server_def is None.")
589
590    self._server_def = server_def
591
592    if self._context_handle:
593      server_def_str = server_def.SerializeToString()
594      # Current executor might have pending nodes that involves updated remote
595      # devices. Wait for them to finish before updating.
596      self.executor.wait()
597      self.executor.clear_error()
598      pywrap_tfe.TFE_ContextUpdateServerDef(self._context_handle,
599                                            keep_alive_secs, server_def_str)
600      self._initialize_logical_devices()
601
602    self._clear_caches()
603
604  def check_alive(self, worker_name):
605    """Checks whether a remote worker is alive or not.
606
607    Args:
608      worker_name: a string representing the remote worker. It must be a fully
609      specified name like "/job:worker/replica:0/task:0".
610
611    Returns:
612      a boolean indicating whether the remote worker is alive or not.
613
614    Raises:
615      ValueError: if context is not initialized.
616    """
617    # TODO(yuefengz): support checking multiple workers.
618    if self._context_handle:
619      return pywrap_tfe.TFE_ContextCheckAlive(self._context_handle, worker_name)
620    else:
621      raise ValueError("Context is not initialized.")
622
623  def enable_collective_ops(self, server_def):
624    """Enable distributed collective ops with an appropriate server_def.
625
626    Args:
627      server_def: A tensorflow::ServerDef proto. Enables execution on remote
628        devices.
629
630    Raises:
631      ValueError: if server_def is None.
632      RuntimeError: if this method is not called at program startup.
633    """
634    if not server_def:
635      raise ValueError("server_def is None.")
636
637    # TODO(b/129298253): Allow creating datasets/tensors before enabling
638    # collective ops.
639    if self._context_handle is not None:
640      logging.warning("Enabling collective ops after program startup may cause "
641                      "error when accessing previously created tensors.")
642
643    self._collective_ops_server_def = server_def
644
645  def configure_collective_ops(
646      self,
647      collective_leader="",
648      scoped_allocator_enabled_ops=("CollectiveReduce",),
649      use_nccl_communication=False,
650      device_filters=None):
651    """Configure collective ops.
652
653      Collective group leader is necessary for collective ops to run, other
654      configurations are mainly for the purpose of performance.
655
656    Args:
657      collective_leader: a device string for collective leader, e.g.
658        "/job:worker/replica:0/task:0"; empty string means local execution of
659          collective ops.
660      scoped_allocator_enabled_ops: a tuple or a list of op names for scoped
661        allocator to run with.
662      use_nccl_communication: whether to use nccl communication for collective
663        ops.
664      device_filters: a tuple or a list of device strings. If set, corresponding
665        task can only see the devices filtered by these device filters.
666
667    Raises:
668      RuntimeError: if this method is not called at program startup.
669    """
670    if self._collective_leader is not None:
671      if (self._collective_leader != collective_leader or
672          self._collective_scoped_allocator_enabled_ops !=
673          scoped_allocator_enabled_ops or
674          self._collective_use_nccl_communication != use_nccl_communication or
675          self._collective_device_filters != device_filters):
676        raise ValueError("Collective ops are already configured.")
677      else:
678        return
679
680    if self._context_handle is not None:
681      raise RuntimeError("Collective ops must be configured at program startup")
682
683    self._collective_leader = collective_leader
684    self._collective_scoped_allocator_enabled_ops = scoped_allocator_enabled_ops
685    self._collective_use_nccl_communication = use_nccl_communication
686    self._collective_device_filters = device_filters
687
688  @property
689  def _handle(self):
690    if self._context_handle is None:
691      raise AssertionError("Context must be initialized first.")
692
693    return self._context_handle
694
695  @property
696  def _devices(self):
697    if self._context_devices is None:
698      raise AssertionError("Context must be initialized first.")
699
700    return self._context_devices
701
702  def __str__(self):
703    if self._context_handle is None:
704      return "Eager TensorFlow Context. Devices currently uninitialized."
705    else:
706      devices = self._devices
707      lines = ["Eager TensorFlow Context with %d devices" % (len(devices))]
708      for i, d in enumerate(devices):
709        lines.append("   Device %d: %s" % (i, d))
710      return "\n".join(lines)
711
712  @tf_contextlib.contextmanager
713  def _mode(self, mode):
714    """A context manager to allow setting the mode to EAGER/GRAPH."""
715    ctx = self._thread_local_data
716    old_is_eager = ctx.is_eager
717    ctx.is_eager = mode == EAGER_MODE
718    if mode == EAGER_MODE:
719      # Entering graph mode does not provide us with sufficient information to
720      # record a context switch; graph-based context switches are only logged
721      # when a graph is registered as the default graph.
722      self.context_switches.push(False, eager_mode, None)
723    try:
724      yield
725    finally:
726      ctx.is_eager = old_is_eager
727      if mode == EAGER_MODE:
728        self.context_switches.pop()
729
730  def executing_eagerly(self):
731    """Returns True if current thread has eager executing enabled."""
732    return self._thread_local_data.is_eager
733
734  def ones_rank_cache(self):
735    """Per-device cache for scalars."""
736    return _tensor_caches_map[self._id].ones_rank_cache
737
738  def zeros_cache(self):
739    """Per-device cache for scalars."""
740    return _tensor_caches_map[self._id].zeros_cache
741
742  @property
743  def scope_name(self):
744    """Returns scope name for the current thread."""
745    return self._thread_local_data.scope_name
746
747  @scope_name.setter
748  def scope_name(self, s):
749    """Sets scope name for the current thread."""
750    self._thread_local_data.scope_name = s
751
752  @property
753  def device_name(self):
754    """Returns the device name for the current thread."""
755    return self._thread_local_data.device_name
756
757  @property
758  def device_spec(self):
759    """Returns the device spec for the current thread."""
760    return self._thread_local_data.device_spec
761
762  def _set_device(self, device_name, device_spec):
763    self._thread_local_data.device_name = device_name
764    self._thread_local_data.device_spec = device_spec
765
766  def device(self, name):
767    """Context-manager to force placement of operations and Tensors on a device.
768
769    Args:
770      name: Name of the device or None to get default placement.
771
772    Returns:
773      Context manager that forces device placement.
774
775    Raises:
776      ValueError: If name is not a string or is an invalid device name.
777      RuntimeError: If device scopes are not properly nested.
778    """
779    if isinstance(name, LogicalDevice):
780      name = name.name
781    elif pydev.is_device_spec(name):
782      name = name.to_string()
783    return _EagerDeviceContext(self, name)
784
785  def devices(self):
786    """List of the names of devices available to execute operations."""
787    return self._devices
788
789  def host_address_space(self):
790    self.ensure_initialized()
791    with c_api_util.tf_buffer() as buffer_:
792      pywrap_tfe.TFE_HostAddressSpace(self._context_handle, buffer_)
793      address_space = pywrap_tf_session.TF_GetBuffer(buffer_).decode("utf-8")
794    return address_space
795
796  # TODO(fishx): remove this property.
797  @property
798  def execution_mode(self):
799    """Gets execution mode for current thread."""
800    return ASYNC if self.is_async() else SYNC
801
802  @execution_mode.setter
803  def execution_mode(self, mode):
804    """Sets execution mode for current thread."""
805    if mode not in (None, SYNC, ASYNC):
806      raise ValueError(
807          "Execution mode should be None/SYNC/ASYNC. Got %s" % mode)
808
809    if mode is None:
810      mode = SYNC
811
812    enable_async = (mode == ASYNC)
813    if self.is_async() != enable_async:
814      # Only set the execution mode if the context has already been initialized
815      if self._context_handle is not None:
816        self.executor.wait()
817        executor_new = executor.new_executor(enable_async)
818        self._thread_local_data.executor = executor_new
819        pywrap_tfe.TFE_ContextSetExecutorForThread(self._context_handle,
820                                                   executor_new.handle())
821      else:
822        self._default_is_async = enable_async
823
824  def is_async(self):
825    if self._context_handle is not None:
826      return self.executor.is_async()
827    else:
828      return self._default_is_async
829
830  @property
831  def executor(self):
832    ensure_initialized()
833    return executor.Executor(
834        pywrap_tfe.TFE_ContextGetExecutorForThread(self._context_handle))
835
836  @executor.setter
837  def executor(self, e):
838    ensure_initialized()
839    pywrap_tfe.TFE_ContextSetExecutorForThread(self._context_handle, e.handle())
840
841  @property
842  def config(self):
843    """Return the ConfigProto with all runtime deltas applied."""
844    # Ensure physical devices have been discovered and config has been imported
845    self._initialize_physical_devices()
846
847    config = config_pb2.ConfigProto()
848    if self._config is not None:
849      config.CopyFrom(self._config)
850
851    if self._optimizer_jit is not None:
852      config.graph_options.optimizer_options.global_jit_level = (
853          config_pb2.OptimizerOptions.ON_1
854          if self._optimizer_jit else config_pb2.OptimizerOptions.OFF)
855    if self._intra_op_parallelism_threads is not None:
856      config.intra_op_parallelism_threads = self._intra_op_parallelism_threads
857    if self._inter_op_parallelism_threads is not None:
858      config.inter_op_parallelism_threads = self._inter_op_parallelism_threads
859
860    if self._soft_device_placement is not None:
861      config.allow_soft_placement = self._soft_device_placement
862    else:
863      config.allow_soft_placement = self.executing_eagerly()
864
865    if self._log_device_placement is not None:
866      config.log_device_placement = self._log_device_placement
867
868    if self._enable_mlir_bridge is not None:
869      config.experimental.enable_mlir_bridge = self._enable_mlir_bridge
870
871    def rewriter_toggle(option):
872      toggle = self._optimizer_experimental_options.get(option, None)
873      if toggle is None:
874        return
875
876      setattr(config.graph_options.rewrite_options,
877              option,
878              (rewriter_config_pb2.RewriterConfig.ON
879               if toggle else rewriter_config_pb2.RewriterConfig.OFF))
880
881    def rewriter_bool(option):
882      toggle = self._optimizer_experimental_options.get(option, None)
883      if toggle is None:
884        return
885
886      setattr(config.graph_options.rewrite_options,
887              option,
888              toggle)
889
890    rewriter_toggle("layout_optimizer")
891    rewriter_toggle("constant_folding")
892    rewriter_toggle("shape_optimization")
893    rewriter_toggle("remapping")
894    rewriter_toggle("arithmetic_optimization")
895    rewriter_toggle("dependency_optimization")
896    rewriter_toggle("loop_optimization")
897    rewriter_toggle("function_optimization")
898    rewriter_toggle("debug_stripper")
899    rewriter_bool("disable_model_pruning")
900    rewriter_toggle("scoped_allocator_optimization")
901    rewriter_toggle("pin_to_host_optimization")
902    rewriter_toggle("implementation_selector")
903    rewriter_toggle("auto_mixed_precision")
904    rewriter_bool("disable_meta_optimizer")
905    nodes = self._optimizer_experimental_options.get("min_graph_nodes", None)
906    if nodes is not None:
907      config.graph_options.rewrite_options.min_graph_nodes = nodes
908
909    # Compute device counts
910    config.device_count["CPU"] = 0
911    config.device_count["GPU"] = 0
912    for dev in self._physical_devices:
913      if dev not in self._visible_device_list:
914        continue
915
916      virtual_devices = self._virtual_device_map.get(dev)
917      if virtual_devices is None:
918        config.device_count[dev.device_type] += 1
919      else:
920        config.device_count[dev.device_type] += len(virtual_devices)
921
922    # Configure gpu_options
923    gpu_options = self._compute_gpu_options()
924    config.gpu_options.MergeFrom(gpu_options)
925
926    # Configure collective ops
927    if self._collective_leader:
928      config.experimental.collective_group_leader = self._collective_leader
929    if self._collective_scoped_allocator_enabled_ops:
930      rewrite_options = config.graph_options.rewrite_options
931      rewrite_options.scoped_allocator_optimization = (
932          rewriter_config_pb2.RewriterConfig.ON)
933      del rewrite_options.scoped_allocator_opts.enable_op[:]
934      for op in self._collective_scoped_allocator_enabled_ops:
935        rewrite_options.scoped_allocator_opts.enable_op.append(op)
936    if self._collective_use_nccl_communication:
937      config.experimental.collective_nccl = True
938    if self._collective_device_filters:
939      del config.device_filters[:]
940      for f in self._collective_device_filters:
941        config.device_filters.append(f)
942
943    return config
944
945  def _compute_gpu_options(self):
946    """Build the GPUOptions proto."""
947    visible_device_list = []
948    virtual_devices = []
949    gpu_index = -1
950    memory_growths = set()
951    for dev in self.list_physical_devices("GPU"):
952      gpu_index += 1
953
954      if dev not in self._visible_device_list:
955        continue
956
957      growth = self._memory_growth_map[dev]
958      memory_growths.add(growth)
959      visible_device_list.append(str(gpu_index))
960
961      if self._virtual_device_map:
962        vdevs = self._virtual_device_map.get(dev, [])
963        device_limits = []
964        for virt_dev in vdevs:
965          device_limits.append(virt_dev.memory_limit)
966
967        virtual_devices.append(
968            config_pb2.GPUOptions.Experimental.VirtualDevices(
969                memory_limit_mb=device_limits))
970
971    # Only compute growth if virtual devices have not been configured and we
972    # have GPUs
973    if not virtual_devices and memory_growths:
974      if len(memory_growths) > 1:
975        raise ValueError("Memory growth cannot differ between GPU devices")
976      allow_growth = memory_growths.pop()
977    else:
978      allow_growth = None
979
980    return config_pb2.GPUOptions(
981        allow_growth=allow_growth,
982        visible_device_list=",".join(visible_device_list),
983        experimental=config_pb2.GPUOptions.Experimental(
984            virtual_devices=virtual_devices))
985
986  @property
987  def function_call_options(self):
988    """Returns function call options for current thread.
989
990    Note that the returned object is still referenced by the eager context.
991
992    Returns: the FunctionCallOptions for current thread.
993    """
994    if self._thread_local_data.function_call_options is None:
995      config = self.config
996
997      # Default to soft placement for functions unless specified
998      if self._soft_device_placement is None:
999        config.allow_soft_placement = True
1000      self._thread_local_data.function_call_options = FunctionCallOptions(
1001          config_proto=config)
1002
1003    return self._thread_local_data.function_call_options
1004
1005  @function_call_options.setter
1006  def function_call_options(self, options):
1007    """Returns function call options for current thread."""
1008    self._thread_local_data.function_call_options = options
1009
1010  def num_gpus(self):
1011    """The number of GPUs available to execute operations."""
1012    self.ensure_initialized()
1013    return self._num_gpus
1014
1015  def add_function(self, fn):
1016    """Add a function definition to the context.
1017
1018    Once added, the function (identified by its name) can be executed like any
1019    other operation.
1020
1021    Args:
1022      fn: A wrapped TF_Function (returned from TF_GraphToFunction_wrapper).
1023    """
1024    self.ensure_initialized()
1025    pywrap_tfe.TFE_ContextAddFunction(self._handle, fn)
1026
1027  def add_function_def(self, fdef):
1028    """Add a function definition to the context.
1029
1030    Once added, the function (identified by its name) can be executed like any
1031    other operation.
1032
1033    Args:
1034      fdef: A FunctionDef protocol buffer message.
1035    """
1036    self.ensure_initialized()
1037    fdef_string = fdef.SerializeToString()
1038    pywrap_tfe.TFE_ContextAddFunctionDef(self._handle, fdef_string,
1039                                         len(fdef_string))
1040
1041  def remove_function(self, name):
1042    """Remove a function from the context.
1043
1044    Once removed, the function cannot be executed anymore.
1045
1046    Args:
1047      name: function signature name.
1048    """
1049    self.ensure_initialized()
1050    pywrap_tfe.TFE_ContextRemoveFunction(self._handle, name)
1051
1052  def has_function(self, name):
1053    """Check if a function `name` is registered."""
1054    self.ensure_initialized()
1055    return bool(pywrap_tfe.TFE_ContextHasFunction(self._handle, name))
1056
1057  def add_op_callback(self, callback):
1058    """Add a post-op callback to the context.
1059
1060    A post-op callback is invoked immediately after an eager operation or
1061    function has finished execution or after a op has been added to a graph,
1062    providing access to the op's type, name input and output tensors. Multiple
1063    op callbacks can be added, in which case the callbacks will be invoked in
1064    the order in which they are added.
1065
1066    Args:
1067      callback: a callable of the signature
1068        `f(op_type, inputs, attrs, outputs, op_name=None, graph=None)`.
1069        See doc strings in `op_callbacks.py` for details on the function
1070        signature and its semantics.
1071    """
1072    if callback not in self._thread_local_data.op_callbacks:
1073      self._thread_local_data.op_callbacks.append(callback)
1074
1075  def remove_op_callback(self, callback):
1076    """Remove an already-registered op callback.
1077
1078    Args:
1079      callback: The op callback to be removed.
1080
1081    Raises:
1082      KeyError: If `callback` is not already registered.
1083    """
1084    if callback not in self._thread_local_data.op_callbacks:
1085      raise KeyError(
1086          "The specified op callback has not been registered, "
1087          "and hence cannot be removed.")
1088    del self._thread_local_data.op_callbacks[
1089        self._thread_local_data.op_callbacks.index(callback)]
1090
1091  @property
1092  def op_callbacks(self):
1093    return self._thread_local_data.op_callbacks
1094
1095  @property
1096  def invoking_op_callbacks(self):
1097    return self._thread_local_data.invoking_op_callbacks
1098
1099  @invoking_op_callbacks.setter
1100  def invoking_op_callbacks(self, value):
1101    self._thread_local_data.invoking_op_callbacks = value
1102
1103  def _initialize_physical_devices(self):
1104    """Get local devices visible to the system."""
1105    # We lazy initialize self._physical_devices since we do not want to do this
1106    # the constructor since the backend may not be initialized yet.
1107    with self._device_lock:
1108      if self._physical_devices is not None:
1109        return
1110
1111      devs = pywrap_tfe.TF_ListPhysicalDevices()
1112      self._physical_devices = [
1113          PhysicalDevice(name=d.decode(),
1114                         device_type=d.decode().split(":")[1]) for d in devs]
1115      # Construct the visible device list from all physical devices but ignore
1116      # XLA devices
1117      self._visible_device_list = [
1118          d for d in self._physical_devices
1119          if not d.device_type.startswith("XLA")
1120      ]
1121      self._memory_growth_map = {
1122          d: None for d in self._physical_devices if d.device_type == "GPU"
1123      }
1124
1125    # Import device settings that may have been passed into the constructor
1126    self._import_config()
1127
1128  def list_physical_devices(self, device_type=None):
1129    """List local devices visible to the system.
1130
1131    This API allows a client to query the devices before they have been
1132    initialized by the eager runtime. Additionally a user can filter by device
1133    type, to get only CPUs or GPUs.
1134
1135    Args:
1136      device_type: Optional device type to limit results to
1137
1138    Returns:
1139      List of PhysicalDevice objects.
1140    """
1141    self._initialize_physical_devices()
1142
1143    if device_type is None:
1144      return list(self._physical_devices)
1145
1146    return [d for d in self._physical_devices if d.device_type == device_type]
1147
1148  def _import_config(self):
1149    """Import config if passed in during construction.
1150
1151    If Context was created with a ConfigProto such as when calling
1152    tf.compat.v1.enable_eager_execution(), then we need to pull out the
1153    various pieces we might be replacing and import then into our internal
1154    class representation.
1155    """
1156    if self._config is None:
1157      return
1158
1159    num_cpus = self._config.device_count.get("CPU", 1)
1160    if num_cpus != 1:
1161      cpus = [d for d in self._physical_devices if d.device_type == "CPU"]
1162      if num_cpus == 0:
1163        self.set_visible_devices([], "CPU")
1164      elif num_cpus > 1:
1165        self.set_logical_device_configuration(
1166            cpus[0], [LogicalDeviceConfiguration() for _ in range(num_cpus)])
1167
1168    # Parse GPU options
1169    gpus = [d for d in self._physical_devices if d.device_type == "GPU"]
1170
1171    # If there are no GPUs detected, simply ignore all the GPU options passed in
1172    # rather than doing any validation checks.
1173    if not gpus:
1174      return
1175
1176    gpu_count = self._config.device_count.get("GPU", None)
1177
1178    visible_gpus = []
1179    # TODO(gjn): Handle importing existing virtual GPU configuration
1180    visible_indices = self._config.gpu_options.visible_device_list
1181    if visible_indices:
1182      for index in visible_indices.split(","):
1183        if int(index) >= len(gpus):
1184          raise ValueError("Invalid visible device index: %s" % index)
1185        visible_gpus.append(gpus[int(index)])
1186    else:
1187      visible_gpus = gpus
1188
1189    if gpu_count is not None:
1190      visible_gpus = visible_gpus[:gpu_count]
1191
1192    self.set_visible_devices(visible_gpus, "GPU")
1193
1194  def list_logical_devices(self, device_type=None):
1195    """Return logical devices."""
1196    self.ensure_initialized()
1197    if device_type is None:
1198      return list(self._logical_devices)
1199
1200    return [d for d in self._logical_devices if d.device_type == device_type]
1201
1202  def get_visible_devices(self, device_type=None):
1203    """Get the list of visible devices."""
1204    self._initialize_physical_devices()
1205
1206    if device_type is None:
1207      return list(self._visible_device_list)
1208
1209    return [
1210        d for d in self._visible_device_list if d.device_type == device_type
1211    ]
1212
1213  def set_visible_devices(self, devices, device_type=None):
1214    """Set the list of visible devices."""
1215    self._initialize_physical_devices()
1216
1217    if not isinstance(devices, list):
1218      devices = [devices]
1219
1220    for d in devices:
1221      if d not in self._physical_devices:
1222        raise ValueError("Unrecognized device: %s" % repr(d))
1223      if device_type is not None and d.device_type != device_type:
1224        raise ValueError("Unrecognized device: %s" % repr(d))
1225
1226    visible_device_list = []
1227    if device_type is not None:
1228      visible_device_list = [
1229          d for d in self._visible_device_list if d.device_type != device_type
1230      ]
1231
1232    visible_device_list += devices
1233
1234    if self._visible_device_list == visible_device_list:
1235      return
1236
1237    if self._context_handle is not None:
1238      raise RuntimeError(
1239          "Visible devices cannot be modified after being initialized")
1240
1241    self._visible_device_list = visible_device_list
1242
1243  def get_memory_growth(self, dev):
1244    """Get if memory growth is enabled for a PhysicalDevice."""
1245    self._initialize_physical_devices()
1246
1247    if dev not in self._physical_devices:
1248      raise ValueError("Unrecognized device: %s" % repr(dev))
1249
1250    return self._memory_growth_map[dev]
1251
1252  def set_memory_growth(self, dev, enable):
1253    """Set if memory growth should be enabled for a PhysicalDevice."""
1254    self._initialize_physical_devices()
1255
1256    if dev not in self._physical_devices:
1257      raise ValueError("Unrecognized device: %s" % repr(dev))
1258
1259    if dev in self._virtual_device_map:
1260      raise ValueError(
1261          "Cannot set memory growth on device when virtual devices configured")
1262
1263    if dev.device_type != "GPU":
1264      raise ValueError("Cannot set memory growth on non-GPU devices")
1265
1266    if self._memory_growth_map.get(dev) == enable:
1267      return
1268
1269    if self._context_handle is not None:
1270      raise RuntimeError(
1271          "Physical devices cannot be modified after being initialized")
1272
1273    self._memory_growth_map[dev] = enable
1274
1275  def get_logical_device_configuration(self, dev):
1276    """Get the virtual device configuration for a PhysicalDevice."""
1277    self._initialize_physical_devices()
1278
1279    if dev not in self._physical_devices:
1280      raise ValueError("Unrecognized device: %s" % repr(dev))
1281
1282    return self._virtual_device_map.get(dev)
1283
1284  def set_logical_device_configuration(self, dev, virtual_devices):
1285    """Set the virtual device configuration for a PhysicalDevice."""
1286    self._initialize_physical_devices()
1287
1288    if dev not in self._physical_devices:
1289      raise ValueError("Unrecognized device: %s" % repr(dev))
1290
1291    if dev.device_type == "CPU":
1292      for vdev in virtual_devices:
1293        if vdev.memory_limit is not None:
1294          raise ValueError("Setting memory limit on CPU virtual devices is "
1295                           "currently not supported")
1296    elif dev.device_type == "GPU":
1297      for vdev in virtual_devices:
1298        if vdev.memory_limit is None:
1299          raise ValueError(
1300              "Setting memory limit is required for GPU virtual devices")
1301    else:
1302      raise ValueError("Virtual devices are not supported for %s" %
1303                       dev.device_type)
1304
1305    if self._virtual_device_map.get(dev) == virtual_devices:
1306      return
1307
1308    if self._context_handle is not None:
1309      raise RuntimeError(
1310          "Virtual devices cannot be modified after being initialized")
1311
1312    self._virtual_device_map[dev] = virtual_devices
1313
1314  @property
1315  def enable_mlir_bridge(self):
1316    return self._enable_mlir_bridge
1317
1318  @enable_mlir_bridge.setter
1319  def enable_mlir_bridge(self, enabled):
1320    self._enable_mlir_bridge = enabled
1321
1322    self._thread_local_data.function_call_options = None
1323
1324  @property
1325  def optimizer_jit(self):
1326    level = self.config.graph_options.optimizer_options.global_jit_level
1327    return (level == config_pb2.OptimizerOptions.ON_1 or
1328            level == config_pb2.OptimizerOptions.ON_2)
1329
1330  @optimizer_jit.setter
1331  def optimizer_jit(self, enabled):
1332    self._optimizer_jit = enabled
1333
1334    self._thread_local_data.function_call_options = None
1335
1336  def get_optimizer_experimental_options(self):
1337    """Get experimental options for the optimizer.
1338
1339    Returns:
1340      Dictionary of current option values
1341    """
1342    rewrite_options = self.config.graph_options.rewrite_options
1343    options = {}
1344
1345    def rewriter_toggle(option):
1346      attr = getattr(rewrite_options, option)
1347      if attr != 0:
1348        options[option] = (attr == rewriter_config_pb2.RewriterConfig.ON)
1349
1350    def rewriter_bool(option):
1351      options[option] = getattr(rewrite_options, option)
1352
1353    rewriter_toggle("layout_optimizer")
1354    rewriter_toggle("constant_folding")
1355    rewriter_toggle("shape_optimization")
1356    rewriter_toggle("remapping")
1357    rewriter_toggle("arithmetic_optimization")
1358    rewriter_toggle("dependency_optimization")
1359    rewriter_toggle("loop_optimization")
1360    rewriter_toggle("function_optimization")
1361    rewriter_toggle("debug_stripper")
1362    rewriter_bool("disable_model_pruning")
1363    rewriter_toggle("scoped_allocator_optimization")
1364    rewriter_toggle("pin_to_host_optimization")
1365    rewriter_toggle("implementation_selector")
1366    rewriter_toggle("auto_mixed_precision")
1367    rewriter_bool("disable_meta_optimizer")
1368
1369    if rewrite_options.min_graph_nodes != 0:
1370      options["min_graph_nodes"] = rewrite_options.min_graph_nodes
1371
1372    return options
1373
1374  def set_optimizer_experimental_options(self, options):
1375    """Set experimental options for the optimizer.
1376
1377    Args:
1378      options: Dictionary of options to modify
1379    """
1380    self._optimizer_experimental_options.update(options)
1381
1382    self._thread_local_data.function_call_options = None
1383
1384  @property
1385  def intra_op_parallelism_threads(self):
1386    return self.config.intra_op_parallelism_threads
1387
1388  @intra_op_parallelism_threads.setter
1389  def intra_op_parallelism_threads(self, num_threads):
1390    if self._intra_op_parallelism_threads == num_threads:
1391      return
1392
1393    if self._context_handle is not None:
1394      raise RuntimeError(
1395          "Intra op parallelism cannot be modified after initialization.")
1396
1397    self._intra_op_parallelism_threads = num_threads
1398
1399  @property
1400  def inter_op_parallelism_threads(self):
1401    return self.config.inter_op_parallelism_threads
1402
1403  @inter_op_parallelism_threads.setter
1404  def inter_op_parallelism_threads(self, num_threads):
1405    if self._inter_op_parallelism_threads == num_threads:
1406      return
1407
1408    if self._context_handle is not None:
1409      raise RuntimeError(
1410          "Inter op parallelism cannot be modified after initialization.")
1411
1412    self._inter_op_parallelism_threads = num_threads
1413
1414  @property
1415  def soft_device_placement(self):
1416    return self.config.allow_soft_placement
1417
1418  @soft_device_placement.setter
1419  def soft_device_placement(self, enabled):
1420    self._soft_device_placement = enabled
1421
1422    self._thread_local_data.function_call_options = None
1423
1424  @property
1425  def log_device_placement(self):
1426    return self.config.log_device_placement
1427
1428  @log_device_placement.setter
1429  def log_device_placement(self, enabled):
1430    if self._log_device_placement == enabled:
1431      return
1432
1433    if self._context_handle is not None:
1434      raise RuntimeError(
1435          "Device placement logging must be set at program startup")
1436
1437    self._log_device_placement = enabled
1438    self._thread_local_data.function_call_options = None
1439
1440  @property
1441  def device_policy(self):
1442    # Only get the policy from the context if it has already been initialized
1443    if self._context_handle is not None:
1444      return pywrap_tfe.TFE_ContextGetDevicePlacementPolicy(self._handle)
1445
1446    return self._device_policy
1447
1448  @device_policy.setter
1449  def device_policy(self, policy):
1450    if policy is None:
1451      policy = DEVICE_PLACEMENT_SILENT
1452
1453    if self._device_policy != policy:
1454      self._device_policy = policy
1455
1456      # Only set the policy if the context has already been initialized
1457      if self._context_handle is not None:
1458        pywrap_tfe.TFE_ContextSetThreadLocalDevicePlacementPolicy(
1459            self._handle, self._device_policy)
1460
1461  @property
1462  def mirroring_policy(self):
1463    # Only get the policy from the context if it has already been initialized
1464    if self._context_handle is not None:
1465      return pywrap_tfe.TFE_ContextGetMirroringPolicy(self._handle)
1466
1467    return self._mirroring_policy
1468
1469  @mirroring_policy.setter
1470  def mirroring_policy(self, policy):
1471    if policy is None:
1472      policy = MIRRORING_NONE
1473
1474    if self._mirroring_policy is None or self._mirroring_policy != policy:
1475      self._mirroring_policy = policy
1476
1477      # Only set the policy if the context has already been initialized
1478      if self._context_handle is not None:
1479        pywrap_tfe.TFE_ContextSetThreadLocalMirroringPolicy(
1480            self._handle, self._mirroring_policy)
1481
1482  @property
1483  def lazy_remote_inputs_copy(self):
1484    return self._lazy_remote_inputs_copy
1485
1486  @lazy_remote_inputs_copy.setter
1487  def lazy_remote_inputs_copy(self, lazy_copy):
1488    """Sets whether to copy remote inputs lazily for functions."""
1489    if not isinstance(lazy_copy, bool):
1490      raise ValueError("Expecting a boolean but got %s" % type(lazy_copy))
1491
1492    if self._lazy_remote_inputs_copy != lazy_copy:
1493      if self._initialized:
1494        raise ValueError(
1495            "lazy_remote_inputs_copy should be set before being initialized.")
1496      self._lazy_remote_inputs_copy = lazy_copy
1497
1498  def enable_run_metadata(self):
1499    """Enables tracing of op execution via RunMetadata.
1500
1501    To retrieve the accumulated metadata call context.export_run_metadata()
1502    and to stop tracing call context.disable_run_metadata().
1503    """
1504    self.ensure_initialized()
1505    pywrap_tfe.TFE_ContextEnableRunMetadata(self._handle)
1506
1507  def disable_run_metadata(self):
1508    """Disables tracing of op execution via RunMetadata."""
1509    if not self._context_handle:
1510      return
1511    pywrap_tfe.TFE_ContextDisableRunMetadata(self._context_handle)
1512
1513  def enable_graph_collection(self):
1514    """Enables graph collection of executed functions.
1515
1516    To retrieve the accumulated graphs call context.export_run_metadata()
1517    and to stop collecting graphs call context.disable_graph_collection().
1518    """
1519    self.ensure_initialized()
1520    pywrap_tfe.TFE_ContextEnableGraphCollection(self._handle)
1521
1522  def disable_graph_collection(self):
1523    """Disables graph collection of executed functions."""
1524    if not self._context_handle:
1525      return
1526    pywrap_tfe.TFE_ContextDisableGraphCollection(self._context_handle)
1527
1528  def export_run_metadata(self):
1529    """Returns a RunMetadata proto with accumulated information.
1530
1531    The returned protocol buffer contains information since the most recent call
1532    to either enable_run_metadata or export_run_metadata.
1533
1534    Returns:
1535      A RunMetadata protocol buffer. Or None if not enabled.
1536    """
1537    if not self._context_handle:
1538      return None
1539    with c_api_util.tf_buffer() as buffer_:
1540      pywrap_tfe.TFE_ContextExportRunMetadata(self._context_handle, buffer_)
1541      proto_data = pywrap_tf_session.TF_GetBuffer(buffer_)
1542    run_metadata = config_pb2.RunMetadata()
1543    run_metadata.ParseFromString(compat.as_bytes(proto_data))
1544    return run_metadata
1545
1546  @property
1547  def context_switches(self):
1548    """Returns a stack of context switches."""
1549    return self._context_switches
1550
1551  def start_step(self):
1552    pywrap_tfe.TFE_ContextStartStep(self._handle)
1553
1554  def end_step(self):
1555    pywrap_tfe.TFE_ContextEndStep(self._handle)
1556
1557
1558class _EagerDeviceContext(object):
1559  """Context-manager forcing placement of ops and Tensors on a device."""
1560
1561  def __init__(self, ctx, device_name):
1562    self._device_name = device_name
1563    self._ctx = ctx
1564    self._stack = []
1565
1566  def __enter__(self):
1567    ctx = self._ctx
1568    old_device_name = ctx.device_name
1569    old_device_spec = ctx.device_spec
1570    new_device_name = self._device_name
1571    cache_key = (old_device_name, new_device_name)
1572    try:
1573      new_device_name, new_device_spec = _device_parsing_cache[cache_key]
1574    except TypeError:
1575      # Error while trying to compute the cache key.
1576      raise ValueError("Expecting a string device name. Got %s(%s)" %
1577                       (type(new_device_name), new_device_name))
1578    except KeyError:
1579      # Handle a cache miss.
1580      if new_device_name is not None:
1581        if not isinstance(new_device_name, six.string_types):
1582          raise ValueError("Expecting a string device name. Got %s(%s)" %
1583                           (type(new_device_name), new_device_name))
1584        device_spec = pydev.DeviceSpec.from_string(new_device_name)
1585        if old_device_name:
1586          new_device_spec = copy.copy(old_device_spec)
1587        else:
1588          ctx.ensure_initialized()
1589          new_device_spec = pydev.DeviceSpec.from_string(
1590              ctx._context_devices[0])  # pylint: disable=protected-access
1591        new_device_spec = new_device_spec.make_merged_spec(device_spec)
1592      else:
1593        new_device_spec = pydev.DeviceSpec.from_string("")
1594      new_device_name = new_device_spec.to_string()
1595      _device_parsing_cache[cache_key] = (new_device_name, new_device_spec)
1596
1597    ctx._set_device(new_device_name, new_device_spec)  # pylint: disable=protected-access
1598    self._stack.append((old_device_name, old_device_spec, new_device_spec))
1599
1600  def __exit__(self, *ex_info):
1601    ctx = self._ctx
1602    old_device_name, old_device_spec, new_device_spec = self._stack[-1]
1603    if ctx.device_spec is not new_device_spec:
1604      raise RuntimeError(
1605          "Exiting device scope without proper scope nesting")
1606    del self._stack[-1]
1607    ctx._set_device(old_device_name, old_device_spec)  # pylint: disable=protected-access
1608
1609
1610# Do not set directly. Use _set_context.
1611_context = None
1612_context_lock = threading.Lock()
1613
1614
1615def _set_context_locked(ctx):
1616  global _context
1617  pywrap_tfe.TFE_Py_SetEagerContext(ctx)
1618  _context = ctx
1619
1620
1621def _set_context(ctx):
1622  with _context_lock:
1623    _set_context_locked(ctx)
1624
1625
1626def _create_context():
1627  with _context_lock:
1628    if _context is None:
1629      ctx = Context()
1630      _set_context_locked(ctx)
1631
1632
1633def _reset_context():
1634  """Clears and re-initializes the singleton context.
1635
1636  Should only be used for testing.
1637  """
1638  global _context
1639  with _context_lock:
1640    if _context is not None:
1641      _context._clear_caches()
1642      _context = None
1643  _create_context()
1644  pywrap_tfe.TFE_ClearScalarCache()
1645
1646
1647def context():
1648  """Returns a singleton context object."""
1649  if _context is None:
1650    _create_context()
1651  return _context
1652
1653
1654def context_safe():
1655  """Returns current context (or None if one hasn't been initialized)."""
1656  return _context
1657
1658
1659def ensure_initialized():
1660  """Initialize the context."""
1661  context().ensure_initialized()
1662
1663
1664def set_global_seed(seed):
1665  """Sets the eager mode seed."""
1666  context()._set_global_seed(seed)  # pylint: disable=protected-access
1667
1668
1669def global_seed():
1670  """Returns the eager mode seed."""
1671  return context()._seed  # pylint: disable=protected-access
1672
1673
1674def internal_operation_seed():
1675  """Returns the operation seed generated based on global seed."""
1676  return context()._internal_operation_seed()  # pylint: disable=protected-access
1677
1678
1679@tf_export("executing_eagerly", v1=[])
1680def executing_eagerly():
1681  """Checks whether the current thread has eager execution enabled.
1682
1683  Eager execution is enabled by default and this API returns `True`
1684  in most of cases. However, this API might return `False` in the following use
1685  cases.
1686
1687  *  Executing inside `tf.function`, unless under `tf.init_scope` or
1688     `tf.config.experimental_run_functions_eagerly(True)` is previously called.
1689  *  Executing inside a transformation function for `tf.dataset`.
1690  *  `tf.compat.v1.disable_eager_execution()` is called.
1691
1692  General case:
1693
1694  >>> print(tf.executing_eagerly())
1695  True
1696
1697  Inside `tf.function`:
1698
1699  >>> @tf.function
1700  ... def fn():
1701  ...   with tf.init_scope():
1702  ...     print(tf.executing_eagerly())
1703  ...   print(tf.executing_eagerly())
1704  >>> fn()
1705  True
1706  False
1707
1708  Inside `tf.function` after
1709
1710  `tf.config.experimental_run_functions_eagerly(True)` is called:
1711  >>> tf.config.experimental_run_functions_eagerly(True)
1712  >>> @tf.function
1713  ... def fn():
1714  ...   with tf.init_scope():
1715  ...     print(tf.executing_eagerly())
1716  ...   print(tf.executing_eagerly())
1717  >>> fn()
1718  True
1719  True
1720  >>> tf.config.experimental_run_functions_eagerly(False)
1721
1722  Inside a transformation function for `tf.dataset`:
1723
1724  >>> def data_fn(x):
1725  ...   print(tf.executing_eagerly())
1726  ...   return x
1727  >>> dataset = tf.data.Dataset.range(100)
1728  >>> dataset = dataset.map(data_fn)
1729  False
1730
1731  Returns:
1732    `True` if the current thread has eager execution enabled.
1733  """
1734  ctx = context_safe()
1735  if ctx is None:
1736    return default_execution_mode == EAGER_MODE
1737
1738  return ctx.executing_eagerly()
1739
1740
1741@tf_export(v1=["executing_eagerly"])
1742def executing_eagerly_v1():
1743  """Checks whether the current thread has eager execution enabled.
1744
1745  Eager execution is typically enabled via
1746  `tf.compat.v1.enable_eager_execution`, but may also be enabled within the
1747  context of a Python function via tf.contrib.eager.py_func.
1748
1749  When eager execution is enabled, returns `True` in most cases. However,
1750  this API might return `False` in the following use cases.
1751
1752  *  Executing inside `tf.function`, unless under `tf.init_scope` or
1753     `tf.config.experimental_run_functions_eagerly(True)` is previously called.
1754  *  Executing inside a transformation function for `tf.dataset`.
1755  *  `tf.compat.v1.disable_eager_execution()` is called.
1756
1757  >>> tf.compat.v1.enable_eager_execution()
1758
1759  General case:
1760
1761  >>> print(tf.executing_eagerly())
1762  True
1763
1764  Inside `tf.function`:
1765
1766  >>> @tf.function
1767  ... def fn():
1768  ...   with tf.init_scope():
1769  ...     print(tf.executing_eagerly())
1770  ...   print(tf.executing_eagerly())
1771  >>> fn()
1772  True
1773  False
1774
1775  Inside `tf.function`
1776  after  `tf.config.experimental_run_functions_eagerly(True)` is called:
1777
1778  >>> tf.config.experimental_run_functions_eagerly(True)
1779  >>> @tf.function
1780  ... def fn():
1781  ...   with tf.init_scope():
1782  ...     print(tf.executing_eagerly())
1783  ...   print(tf.executing_eagerly())
1784  >>> fn()
1785  True
1786  True
1787  >>> tf.config.experimental_run_functions_eagerly(False)
1788
1789  Inside a transformation function for `tf.dataset`:
1790
1791  >>> def data_fn(x):
1792  ...   print(tf.executing_eagerly())
1793  ...   return x
1794  >>> dataset = tf.data.Dataset.range(100)
1795  >>> dataset = dataset.map(data_fn)
1796  False
1797
1798  Returns:
1799    `True` if the current thread has eager execution enabled.
1800  """
1801  return executing_eagerly()
1802
1803
1804def in_eager_mode():
1805  """Use executing_eagerly() instead. This function will be removed."""
1806  return executing_eagerly()
1807
1808
1809def shared_name(name=None):
1810  """Returns the anonymous shared name GUID if no shared name is specified.
1811
1812  In eager mode we need to use a unique shared name to avoid spurious sharing
1813  issues. The runtime generates a unique name on our behalf when the reserved
1814  GUID is used as a shared name.
1815
1816  Args:
1817    name: Optional shared name
1818
1819  Returns:
1820    Eager compatible shared name.
1821  """
1822  if name or not executing_eagerly():
1823    return name
1824
1825  # Ensure a unique name when eager execution is enabled to avoid spurious
1826  # sharing issues.
1827  return "cd2c89b7-88b7-44c8-ad83-06c2a9158347"
1828
1829
1830def graph_mode():
1831  """Context-manager to disable eager execution for the current thread."""
1832  return context()._mode(GRAPH_MODE)  # pylint: disable=protected-access
1833
1834
1835def eager_mode():
1836  """Context-manager to enable eager execution for the current thread."""
1837  return context()._mode(EAGER_MODE)  # pylint: disable=protected-access
1838
1839
1840def scope_name():
1841  """Name of the current scope."""
1842  return context().scope_name
1843
1844
1845def device(name):
1846  """Context-manager to force placement of operations and Tensors on a device.
1847
1848  Example:
1849  ```python
1850  with tf.device('gpu:0'):
1851    with tf.device('cpu:0'):
1852      shape = tf.constant([], dtype=tf.int32)
1853    x = tf.random.truncated_normal(shape, tf.float32)
1854  ```
1855  will ensure that the `shape` Tensor is on CPU but the `truncated_normal`
1856  operation runs on GPU 0.
1857
1858  Args:
1859    name: Name of the device (see context().devices()), or None to
1860      perform automatic placement.
1861
1862  Returns:
1863    Context manager for setting the device.
1864  """
1865  ensure_initialized()
1866  return context().device(name)
1867
1868
1869@tf_export("debugging.get_log_device_placement")
1870def get_log_device_placement():
1871  """Get if device placements are logged.
1872
1873  Returns:
1874    If device placements are logged.
1875  """
1876  return context().log_device_placement
1877
1878
1879@tf_export("debugging.set_log_device_placement")
1880def set_log_device_placement(enabled):
1881  """Set if device placements should be logged.
1882
1883  Args:
1884    enabled: Whether to enabled device placement logging.
1885  """
1886  context().log_device_placement = enabled
1887
1888
1889@tf_contextlib.contextmanager
1890def device_policy(policy):
1891  """Context manager for setting device placement policy for current thread."""
1892  ctx = context()
1893  old_policy = ctx.device_policy
1894  try:
1895    ctx.device_policy = policy
1896    yield
1897  finally:
1898    ctx.device_policy = old_policy
1899
1900
1901@tf_contextlib.contextmanager
1902def mirroring_policy(policy):
1903  """Context manager for setting mirroring policy for current thread."""
1904  ctx = context()
1905  old_policy = ctx.mirroring_policy
1906  try:
1907    ctx.mirroring_policy = policy
1908    yield
1909  finally:
1910    ctx.mirroring_policy = old_policy
1911
1912
1913def set_execution_mode(mode):
1914  """Sets execution mode for the current thread."""
1915  context().execution_mode = mode
1916
1917
1918# TODO(fishx): remove this method.
1919@tf_contextlib.contextmanager
1920def execution_mode(mode):
1921  """Context manager for setting execution mode for current thread."""
1922  if mode is None:
1923    yield
1924  else:
1925    ctx = context()
1926    executor_new = executor.new_executor(mode == ASYNC)
1927    executor_old = ctx.executor
1928    try:
1929      executor_old.wait()
1930      ctx.executor = executor_new
1931      yield
1932    finally:
1933      ctx.executor = executor_old
1934      executor_new.wait()
1935
1936
1937@tf_contextlib.contextmanager
1938def executor_scope(e):
1939  """Context manager for changing executor for current thread.
1940
1941  Args:
1942    e: A Executor to execute eager ops under this scope. Setting it to None will
1943      switch back to use the default executor for the context.
1944
1945  Yields:
1946    Context manager for setting the executor for current thread.
1947  """
1948  ctx = context()
1949  executor_old = ctx.executor
1950  try:
1951    ctx.executor = e
1952    yield
1953  finally:
1954    ctx.executor = executor_old
1955
1956
1957@tf_export("experimental.function_executor_type")
1958@tf_contextlib.contextmanager
1959def function_executor_type(executor_type):
1960  """Context manager for setting the executor of eager defined functions.
1961
1962  Eager defined functions are functions decorated by tf.contrib.eager.defun.
1963
1964  Args:
1965    executor_type: a string for the name of the executor to be used to execute
1966      functions defined by tf.contrib.eager.defun.
1967
1968  Yields:
1969    Context manager for setting the executor of eager defined functions.
1970  """
1971  current_options = context().function_call_options
1972  old_options = copy.copy(current_options)
1973  try:
1974    current_options.executor_type = executor_type
1975    yield
1976  finally:
1977    context().function_call_options = old_options
1978
1979
1980def is_async():
1981  """Returns true if current thread is in async mode."""
1982  return context().is_async()
1983
1984
1985def async_wait():
1986  """Waits for ops dispatched in ASYNC mode to finish."""
1987  return context().executor.wait()
1988
1989
1990def async_clear_error():
1991  """Clears errors raised during ASYNC execution mode."""
1992  return context().executor.clear_error()
1993
1994
1995def num_gpus():
1996  """Get the number of available GPU devices.
1997
1998  Returns:
1999    The number of available GPU devices.
2000  """
2001  return context().num_gpus()
2002
2003
2004def enable_run_metadata():
2005  """Enables tracing of op execution via RunMetadata.
2006
2007  To retrieve the accumulated metadata call context.export_run_metadata()
2008  and to stop tracing call context.disable_run_metadata().
2009  """
2010  context().enable_run_metadata()
2011
2012
2013def disable_run_metadata():
2014  """Disables tracing of op execution via RunMetadata."""
2015  context().disable_run_metadata()
2016
2017
2018def enable_graph_collection():
2019  """Enables graph collection of executed functions.
2020
2021  To retrieve the accumulated graphs call context.export_run_metadata()
2022  and to stop collecting graphs call context.disable_graph_collection().
2023  """
2024  context().enable_graph_collection()
2025
2026
2027def disable_graph_collection():
2028  """Disables graph collection of executed functions."""
2029  context().disable_graph_collection()
2030
2031
2032def export_run_metadata():
2033  """Returns a RunMetadata proto with accumulated information.
2034
2035  The returned protocol buffer contains information since the most recent call
2036  to either enable_run_metadata or export_run_metadata.
2037
2038  Returns:
2039    A RunMetadata protocol buffer.
2040  """
2041  return context().export_run_metadata()
2042
2043
2044@contextlib.contextmanager
2045def collect_graphs(optimized=True):
2046  """Collects a flat list of pre- or post-optimization graphs.
2047
2048  The collected graphs include device placements, which can be useful for
2049  testing.
2050
2051  Usage:
2052
2053  ```
2054  @def_function.function
2055  def f(x):
2056    return x + constant_op.constant(1.)
2057
2058  with context.collect_graphs() as graphs:
2059    with ops.device("CPU:0"):
2060      f(constant_op.constant(1.))
2061
2062  graph, = graphs  # `graph` contains a single GraphDef for inspection
2063  ```
2064
2065  Args:
2066    optimized: whether to collect optimized graphs or non-optimized graphs
2067  Yields:
2068    A list of GraphDefs, populated when the context manager exits.
2069  """
2070  ctx = context()
2071  ctx.enable_graph_collection()
2072  try:
2073    graphs = []
2074    yield graphs
2075    metadata = ctx.export_run_metadata()
2076  finally:
2077    ctx.disable_graph_collection()
2078  for graph in metadata.function_graphs:
2079    if optimized:
2080      graphs.append(graph.post_optimization_graph)
2081    else:
2082      graphs.append(graph.pre_optimization_graph)
2083
2084
2085def get_server_def():
2086  return context().get_server_def()
2087
2088
2089def set_server_def(server_def):
2090  context().set_server_def(server_def)
2091
2092
2093def update_server_def(server_def):
2094  context().update_server_def(server_def)
2095
2096
2097def check_alive(worker_name):
2098  return context().check_alive(worker_name)
2099
2100
2101def add_function(fdef):
2102  """Add a function definition to the context."""
2103  context().add_function(fdef)
2104
2105
2106def remove_function(name):
2107  """Remove a function from the context."""
2108  context().remove_function(name)
2109
2110
2111# Not every user creates a Context via context.context()
2112# (for example, enable_eager_execution in python/framework/ops.py),
2113# but they do all import this file.  Note that IS_IN_GRAPH_MODE and
2114# in_graph_mode are both parameterless functions.
2115def _tmp_in_graph_mode():
2116  if context_safe() is None:
2117    # Context not yet initialized. Assume graph mode following the
2118    # default implementation in `is_in_graph_mode`.
2119    return True
2120  return not executing_eagerly()
2121
2122
2123is_in_graph_mode.IS_IN_GRAPH_MODE = _tmp_in_graph_mode
2124