def average(a, axis=None, weights=None, returned=False): # pylint: disable=missing-docstring if axis is not None and not isinstance(axis, six.integer_types): # TODO(wangpeng): Support tuple of ints as `axis` raise ValueError('`axis` must be an integer. Tuple of ints is not ' 'supported yet. Got type: %s' % type(axis)) a = array_ops.array(a) if weights is None: # Treat all weights as 1 if not np.issubdtype(a.dtype, np.inexact): a = a.astype( utils.result_type(a.dtype, dtypes.default_float_type())) avg = tf.reduce_mean(a.data, axis=axis) if returned: if axis is None: weights_sum = tf.size(a.data) else: weights_sum = tf.shape(a.data)[axis] weights_sum = tf.cast(weights_sum, a.data.dtype) else: if np.issubdtype(a.dtype, np.inexact): out_dtype = utils.result_type(a.dtype, weights) else: out_dtype = utils.result_type(a.dtype, weights, dtypes.default_float_type()) a = array_ops.array(a, out_dtype).data weights = array_ops.array(weights, out_dtype).data def rank_equal_case(): tf.debugging.Assert( tf.reduce_all(tf.shape(a) == tf.shape(weights)), [tf.shape(a), tf.shape(weights)]) weights_sum = tf.reduce_sum(weights, axis=axis) avg = tf.reduce_sum(a * weights, axis=axis) / weights_sum return avg, weights_sum if axis is None: avg, weights_sum = rank_equal_case() else: def rank_not_equal_case(): tf.debugging.Assert(tf.rank(weights) == 1, [tf.rank(weights)]) weights_sum = tf.reduce_sum(weights) axes = tf.convert_to_tensor([[axis], [0]]) avg = tf.tensordot(a, weights, axes) / weights_sum return avg, weights_sum # We condition on rank rather than shape equality, because if we do the # latter, when the shapes are partially unknown but the ranks are known # and different, utils.cond will run shape checking on the true branch, # which will raise a shape-checking error. avg, weights_sum = utils.cond( tf.rank(a) == tf.rank(weights), rank_equal_case, rank_not_equal_case) avg = array_ops.array(avg) if returned: weights_sum = array_ops.broadcast_to(weights_sum, tf.shape(avg.data)) return avg, weights_sum return avg
def array(val, dtype=None, copy=True, ndmin=0): # pylint: disable=redefined-outer-name """Creates an ndarray with the contents of val. Args: val: array_like. Could be an ndarray, a Tensor or any object that can be converted to a Tensor using `tf.convert_to_tensor`. dtype: Optional, defaults to dtype of the `val`. The type of the resulting ndarray. Could be a python type, a NumPy type or a TensorFlow `DType`. copy: Determines whether to create a copy of the backing buffer. Since Tensors are immutable, a copy is made only if val is placed on a different device than the current one. Even if `copy` is False, a new Tensor may need to be built to satisfy `dtype` and `ndim`. This is used only if `val` is an ndarray or a Tensor. ndmin: The minimum rank of the returned array. Returns: An ndarray. """ if dtype: dtype = utils.result_type(dtype) if isinstance(val, arrays_lib.ndarray): result_t = val.data else: result_t = val if copy and isinstance(result_t, tf.Tensor): # Note: In eager mode, a copy of `result_t` is made only if it is not on # the context device. result_t = tf.identity(result_t) if not isinstance(result_t, tf.Tensor): if not dtype: dtype = utils.result_type(result_t) # We can't call `convert_to_tensor(result_t, dtype=dtype)` here because # convert_to_tensor doesn't allow incompatible arguments such as (5.5, int) # while np.array allows them. We need to convert-then-cast. def maybe_data(x): if isinstance(x, arrays_lib.ndarray): return x.data return x # Handles lists of ndarrays result_t = tf.nest.map_structure(maybe_data, result_t) result_t = arrays_lib.convert_to_tensor(result_t) result_t = tf.cast(result_t, dtype=dtype) elif dtype: result_t = tf.cast(result_t, dtype) ndims = tf.rank(result_t) def true_fn(): old_shape = tf.shape(result_t) new_shape = tf.concat([tf.ones(ndmin - ndims, tf.int32), old_shape], axis=0) return tf.reshape(result_t, new_shape) result_t = utils.cond(utils.greater(ndmin, ndims), true_fn, lambda: result_t) return arrays_lib.tensor_to_ndarray(result_t)
def eye(N, M=None, k=0, dtype=float): # pylint: disable=invalid-name,missing-docstring if dtype: dtype = utils.result_type(dtype) if not M: M = N # Making sure N, M and k are `int` N = int(N) M = int(M) k = int(k) if k >= M or -k >= N: # tf.linalg.diag will raise an error in this case return zeros([N, M], dtype=dtype) if k == 0: return arrays_lib.tensor_to_ndarray(tf.eye(N, M, dtype=dtype)) # We need the precise length, otherwise tf.linalg.diag will raise an error diag_len = min(N, M) if k > 0: if N >= M: diag_len -= k elif N + k > M: diag_len = M - k elif k <= 0: if M >= N: diag_len += k elif M - k > N: diag_len = N + k diagonal = tf.ones([diag_len], dtype=dtype) return arrays_lib.tensor_to_ndarray( tf.linalg.diag(diagonal=diagonal, num_rows=N, num_cols=M, k=k))
def linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=float): if dtype: dtype = utils.result_type(dtype) start = array_creation.asarray(start, dtype=dtype) stop = array_creation.asarray(stop, dtype=dtype) if num == 0: return empty(dtype) if num < 0: raise ValueError( 'Number of samples {} must be non-negative.'.format(num)) step = np.nan if endpoint: result = tf.linspace(start.data, stop.data, num) if num > 1: step = (stop - start) / (num - 1) else: # tf.linspace does not support endpoint=False so we manually handle it # here. if num > 1: step = (stop - start) / num result = tf.linspace(start.data, (stop - step).data, num) else: result = tf.linspace(start.data, stop.data, num) if dtype: result = tf.cast(result, dtype) if retstep: return arrays.tensor_to_ndarray(result), step else: return arrays.tensor_to_ndarray(result)
def array(val, dtype=None, copy=True, ndmin=0): """Creates an ndarray with the contents of val. Args: val: array_like. Could be an ndarray, a Tensor or any object that can be converted to a Tensor using `tf.convert_to_tensor`. dtype: Optional, defaults to dtype of the `val`. The type of the resulting ndarray. Could be a python type, a NumPy type or a TensorFlow `DType`. copy: Determines whether to create a copy of the backing buffer. Since Tensors are immutable, a copy is made only if val is placed on a different device than the current one. Even if `copy` is False, a new Tensor may need to be built to satisfy `dtype` and `ndim`. This is used only if `val` is an ndarray or a Tensor. ndmin: The minimum rank of the returned array. Returns: An ndarray. """ if dtype: dtype = utils.result_type(dtype) if isinstance(val, arrays.ndarray): result_t = val.data else: result_t = val if copy and isinstance(result_t, tf.Tensor): # Note: In eager mode, a copy of `result_t` is made only if it is not on # the context device. result_t = tf.identity(result_t) if not isinstance(result_t, tf.Tensor): if not dtype: dtype = utils.result_type(result_t) # We can't call `convert_to_tensor(result_t, dtype=dtype)` here because # convert_to_tensor doesn't allow incompatible arguments such as (5.5, int) # while np.array allows them. We need to convert-then-cast. result_t = arrays.convert_to_tensor(result_t) result_t = tf.cast(result_t, dtype=dtype) elif dtype: result_t = tf.cast(result_t, dtype) ndims = len(result_t.shape) if ndmin > ndims: old_shape = list(result_t.shape) new_shape = [1 for _ in range(ndmin - ndims)] + old_shape result_t = tf.reshape(result_t, new_shape) return arrays.tensor_to_ndarray(result_t)
def concatenate(arys, axis=0): if not isinstance(arys, (list, tuple)): arys = [arys] if not arys: raise ValueError('Need at least one array to concatenate.') dtype = utils.result_type(*arys) arys = [array_ops.array(array, dtype=dtype).data for array in arys] return arrays.tensor_to_ndarray(tf.concat(arys, axis))
def logspace(start, stop, num=50, endpoint=True, base=10.0, dtype=None): if dtype: dtype = utils.result_type(dtype) result = linspace(start, stop, num=num, endpoint=endpoint) result = tf.pow(base, result.data) if dtype: result = tf.cast(result, dtype) return arrays.tensor_to_ndarray(result)
def _reduce(tf_fn, a, axis=None, dtype=None, keepdims=None, promote_int=_TO_INT64, tf_bool_fn=None, preserve_bool=False): """A general reduction function. Args: tf_fn: the TF reduction function. a: the array to be reduced. axis: (optional) the axis along which to do the reduction. If None, all dimensions are reduced. dtype: (optional) the dtype of the result. keepdims: (optional) whether to keep the reduced dimension(s). promote_int: how to promote integer and bool inputs. There are three choices: (1) _TO_INT64: always promote them to int64 or uint64; (2) _TO_FLOAT: always promote them to a float type (determined by dtypes.default_float_type); (3) None: don't promote. tf_bool_fn: (optional) the TF reduction function for bool inputs. It will only be used if `dtype` is explicitly set to `np.bool_` or if `a`'s dtype is `np.bool_` and `preserve_bool` is True. preserve_bool: a flag to control whether to use `tf_bool_fn` if `a`'s dtype is `np.bool_` (some reductions such as np.sum convert bools to integers, while others such as np.max preserve bools. Returns: An ndarray. """ if dtype: dtype = utils.result_type(dtype) if keepdims is None: keepdims = False a = array_creation.asarray(a, dtype=dtype) if ((dtype == np.bool_ or preserve_bool and a.dtype == np.bool_) and tf_bool_fn is not None): return utils.tensor_to_ndarray( tf_bool_fn(input_tensor=a.data, axis=axis, keepdims=keepdims)) if dtype is None: dtype = a.dtype if np.issubdtype(dtype, np.integer) or dtype == np.bool_: if promote_int == _TO_INT64: # If a is an integer/bool type and whose bit width is less than 64, # numpy up-casts it to 64-bit. if dtype == np.bool_: is_signed = True width = 8 # We can use any number here that is less than 64 else: is_signed = np.issubdtype(dtype, np.signedinteger) width = np.iinfo(dtype).bits if width < 64: if is_signed: dtype = np.int64 else: dtype = np.uint64 a = a.astype(dtype) elif promote_int == _TO_FLOAT: a = a.astype(dtypes.default_float_type()) return utils.tensor_to_ndarray( tf_fn(input_tensor=a.data, axis=axis, keepdims=keepdims))
def promote_args_types(a, b): a = asarray(a) b = asarray(b) output_type = utils.result_type(a.dtype, b.dtype) if output_type != a.dtype: a = a.astype(output_type) if output_type != b.dtype: b = b.astype(output_type) return (a, b)
def _comparison(tf_fun, x1, x2, cast_bool_to_int=False): dtype = utils.result_type(x1, x2) # Cast x1 and x2 to the result_type if needed. x1 = array_creation.asarray(x1, dtype=dtype) x2 = array_creation.asarray(x2, dtype=dtype) x1 = x1.data x2 = x2.data if cast_bool_to_int and x1.dtype == tf.bool: x1 = tf.cast(x1, tf.int32) x2 = tf.cast(x2, tf.int32) return utils.tensor_to_ndarray(tf_fun(x1, x2))
def ones_like(a, dtype=None): """Returns an array of ones with the shape and type of the input array. Args: a: array_like. Could be an ndarray, a Tensor or any object that can be converted to a Tensor using `tf.convert_to_tensor`. dtype: Optional, defaults to dtype of the input array. The type of the resulting ndarray. Could be a python type, a NumPy type or a TensorFlow `DType`. Returns: An ndarray. """ if isinstance(a, arrays_lib.ndarray): a = a.data if dtype is None: dtype = utils.result_type(a) else: dtype = utils.result_type(dtype) return arrays_lib.tensor_to_ndarray(tf.ones_like(a, dtype))
def arange(start, stop=None, step=1, dtype=None): """Returns `step`-separated values in the range [start, stop). Args: start: Start of the interval. Included in the range. stop: End of the interval. If not specified, `start` is treated as 0 and `start` value is used as `stop`. If specified, it is not included in the range if `step` is integer. When `step` is floating point, it may or may not be included. step: The difference between 2 consecutive values in the output range. It is recommended to use `linspace` instead of using non-integer values for `step`. dtype: Optional. Type of the resulting ndarray. Could be a python type, a NumPy type or a TensorFlow `DType`. If not provided, the largest type of `start`, `stop`, `step` is used. Raises: ValueError: If step is zero. """ if not step: raise ValueError('step must be non-zero.') if dtype: dtype = utils.to_tf_type(dtype) else: dtype = utils.result_type( utils.array_dtype(start).as_numpy_dtype, utils.array_dtype(step).as_numpy_dtype) if stop is not None: dtype = utils.result_type(dtype, utils.array_dtype(stop).as_numpy_dtype) dtype = utils.to_tf_type(dtype) if step > 0 and ((stop is not None and start > stop) or (stop is None and start < 0)): return array([], dtype=dtype) if step < 0 and ((stop is not None and start < stop) or (stop is None and start > 0)): return array([], dtype=dtype) # TODO(srbs): There are some bugs when start or stop is float type and dtype # is integer type. return utils.tensor_to_ndarray( tf.cast(tf.range(start, limit=stop, delta=step), dtype=dtype))
def full_like(a, fill_value, dtype=None, order='K', subok=True, shape=None): # pylint: disable=missing-docstring,redefined-outer-name """order, subok and shape arguments mustn't be changed.""" if order != 'K': raise ValueError('Non-standard orders are not supported.') if not subok: raise ValueError('subok being False is not supported.') if shape: raise ValueError('Overriding the shape is not supported.') a = asarray(a).data dtype = dtype or utils.result_type(a) fill_value = asarray(fill_value, dtype=dtype) return arrays_lib.tensor_to_ndarray( tf.broadcast_to(fill_value.data, tf.shape(a)))
def trace(a, offset=0, axis1=0, axis2=1, dtype=None): # pylint: disable=missing-docstring if dtype: dtype = utils.result_type(dtype) a = array_ops.asarray(a, dtype).data if offset == 0: a_shape = a.shape if a_shape.rank is not None: rank = len(a_shape) if (axis1 == -2 or axis1 == rank - 2) and (axis2 == -1 or axis2 == rank - 1): return utils.tensor_to_ndarray(tf.linalg.trace(a)) a = array_ops.diagonal(a, offset, axis1, axis2) return array_ops.sum(a, -1, dtype)
def zeros_like(a, dtype=None): """Returns an array of zeros with the shape and type of the input array. Args: a: array_like. Could be an ndarray, a Tensor or any object that can be converted to a Tensor using `tf.convert_to_tensor`. dtype: Optional, defaults to dtype of the input array. The type of the resulting ndarray. Could be a python type, a NumPy type or a TensorFlow `DType`. Returns: An ndarray. """ if isinstance(a, arrays_lib.ndarray): a = a.data if dtype is None: # We need to let utils.result_type decide the dtype, not tf.zeros_like dtype = utils.result_type(a) else: # TF and numpy has different interpretations of Python types such as # `float`, so we let `utils.result_type` decide. dtype = utils.result_type(dtype) dtype = tf.as_dtype(dtype) # Work around b/149877262 return arrays_lib.tensor_to_ndarray(tf.zeros_like(a, dtype))
def logspace(start, stop, num=50, endpoint=True, base=10.0, dtype=None, axis=0): dtype = utils.result_type(start, stop, dtype) result = linspace(start, stop, num=num, endpoint=endpoint, dtype=dtype, axis=axis).data result = tf.pow(tf.cast(base, result.dtype), result) if dtype: result = tf.cast(result, dtype) return arrays.tensor_to_ndarray(result)
def zeros(shape, dtype=float): # pylint: disable=redefined-outer-name """Returns an ndarray with the given shape and type filled with zeros. Args: shape: A fully defined shape. Could be - NumPy array or a python scalar, list or tuple of integers, - TensorFlow tensor/ndarray of integer type and rank <=1. dtype: Optional, defaults to float. The type of the resulting ndarray. Could be a python type, a NumPy type or a TensorFlow `DType`. Returns: An ndarray. """ if dtype: dtype = utils.result_type(dtype) if isinstance(shape, arrays_lib.ndarray): shape = shape.data return arrays_lib.tensor_to_ndarray(tf.zeros(shape, dtype=dtype))
def geomspace(start, stop, num=50, endpoint=True, dtype=float): # pylint: disable=missing-docstring if dtype: dtype = utils.result_type(dtype) if num < 0: raise ValueError('Number of samples {} must be non-negative.'.format(num)) if not num: return empty([0]) step = 1. if endpoint: if num > 1: step = tf.pow((stop / start), 1 / (num - 1)) else: step = tf.pow((stop / start), 1 / num) result = tf.cast(tf.range(num), step.dtype) result = tf.pow(step, result) result = tf.multiply(result, start) if dtype: result = tf.cast(result, dtype=dtype) return arrays_lib.tensor_to_ndarray(result)
def full_like(a, fill_value, dtype=None): """Returns an array with same shape and dtype as `a` filled with `fill_value`. Args: a: array_like. Could be an ndarray, a Tensor or any object that can be converted to a Tensor using `tf.convert_to_tensor`. fill_value: array_like. Could be an ndarray, a Tensor or any object that can be converted to a Tensor using `tf.convert_to_tensor`. dtype: Optional, defaults to dtype of the `a`. The type of the resulting ndarray. Could be a python type, a NumPy type or a TensorFlow `DType`. Returns: An ndarray. Raises: ValueError: if `fill_value` can not be broadcast to shape `shape`. """ a = asarray(a) dtype = dtype or utils.result_type(a) return full(a.shape, fill_value, dtype)
def equal(x1, x2): """Compare two arrays for equality element-wise. Both arrays must either be of the same shape or one should be broadcastable to the other. Args: x1: array_like. Could be an ndarray, a Tensor or any object that can be converted to a Tensor using `tf.convert_to_tensor`. x2: array_like. Could be an ndarray, a Tensor or any object that can be converted to a Tensor using `tf.convert_to_tensor`. Returns: An ndarray of type bool and broadcasted shape of x1 and x2. """ dtype = utils.result_type(x1, x2) # Cast x1 and x2 to the result_type if needed. x1 = array_creation.array(x1, copy=False, dtype=dtype) x2 = array_creation.array(x2, copy=False, dtype=dtype) return utils.tensor_to_ndarray(tf.equal(x1.data, x2.data))
def tri(N, M=None, k=0, dtype=None): # pylint: disable=invalid-name,missing-docstring M = M if M is not None else N if dtype is not None: dtype = utils.result_type(dtype) else: dtype = dtypes.default_float_type() if k < 0: lower = -k - 1 if lower > N: r = tf.zeros([N, M], dtype) else: # Keep as tf bool, since we create an upper triangular matrix and invert # it. o = tf.ones([N, M], dtype=tf.bool) r = tf.cast(tf.math.logical_not(tf.linalg.band_part(o, lower, -1)), dtype) else: o = tf.ones([N, M], dtype) if k > M: r = o else: r = tf.linalg.band_part(o, -1, k) return utils.tensor_to_ndarray(r)
def linspace( # pylint: disable=missing-docstring start, stop, num=50, endpoint=True, retstep=False, dtype=float, axis=0): if dtype: dtype = utils.result_type(dtype) start = array_ops.array(start, dtype=dtype).data stop = array_ops.array(stop, dtype=dtype).data if num < 0: raise ValueError( 'Number of samples {} must be non-negative.'.format(num)) step = tf.convert_to_tensor(np.nan) if endpoint: result = tf.linspace(start, stop, num, axis=axis) if num > 1: step = (stop - start) / (num - 1) else: # tf.linspace does not support endpoint=False so we manually handle it # here. if num > 1: step = ((stop - start) / num) new_stop = tf.cast(stop, step.dtype) - step start = tf.cast(start, new_stop.dtype) result = tf.linspace(start, new_stop, num, axis=axis) else: result = tf.linspace(start, stop, num, axis=axis) if dtype: result = tf.cast(result, dtype) if retstep: return arrays.tensor_to_ndarray(result), arrays.tensor_to_ndarray(step) else: return arrays.tensor_to_ndarray(result)
def asarray(a, dtype=None): if dtype: dtype = utils.result_type(dtype) if isinstance(a, arrays_lib.ndarray) and (not dtype or dtype == a.dtype): return a return array(a, dtype, copy=False)
def concatenate(arys, axis=0): if not arys: raise ValueError('Need at least one array to concatenate.') dtype = utils.result_type(*arys) arys = [array_creation.asarray(array, dtype=dtype).data for array in arys] return arrays.tensor_to_ndarray(tf.concat(arys, axis))
def _promote_dtype(*arrays): dtype = utils.result_type(*arrays) return [asarray(a, dtype=dtype) for a in arrays]