Skip to content
This repository has been archived by the owner on Nov 17, 2023. It is now read-only.

Commit

Permalink
[Numpy] Numpy behavior random.uniform() (#15858)
Browse files Browse the repository at this point in the history
* numpy behavior uniform sampling implemented

* improve code style according to review comment

* remove rebundant namespace
  • Loading branch information
xidulu authored and haojin2 committed Aug 21, 2019
1 parent 0b5526b commit 521e925
Show file tree
Hide file tree
Showing 8 changed files with 676 additions and 3 deletions.
62 changes: 61 additions & 1 deletion python/mxnet/ndarray/numpy/random.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,65 @@

"""Namespace for operators used in Gluon dispatched by F=ndarray."""
from __future__ import absolute_import
from ...context import current_context
from . import _internal as _npi

__all__ = []
__all__ = ['uniform']


def uniform(low=0.0, high=1.0, size=None, dtype=None, ctx=None, out=None):
"""Draw samples from a uniform distribution.
Samples are uniformly distributed over the half-open interval
``[low, high)`` (includes low, but excludes high). In other words,
any value within the given interval is equally likely to be drawn
by `uniform`.
Parameters
----------
low : float, ndarray, optional
Lower boundary of the output interval. All values generated will be
greater than or equal to low. The default value is 0.
high : float, ndarray, optional
Upper boundary of the output interval. All values generated will be
less than high. The default value is 1.0.
size : int or tuple of ints, optional
Output shape. If the given shape is, e.g., ``(m, n, k)``, then
``m * n * k`` samples are drawn. If size is ``None`` (default),
a scalar tensor containing a single value is returned if
``low`` and ``high`` are both scalars.
dtype : {'float16', 'float32', 'float64'}, optional
Data type of output samples. Default is 'float32'
ctx : Context, optional
Device context of output. Default is current context.
Returns
-------
out : ndarray
Drawn samples from the parameterized uniform distribution.
"""
from ...numpy import ndarray as np_ndarray
input_type = (isinstance(low, np_ndarray), isinstance(high, np_ndarray))
if dtype is None:
dtype = 'float32'
if ctx is None:
ctx = current_context()
if out is not None:
size = out.shape
if size == ():
size = None
if input_type == (True, True):
return _npi.uniform(low, high, low=None, high=None, size=size,
ctx=ctx, dtype=dtype, out=out)
elif input_type == (False, True):
return _npi.uniform(high, low=low, high=None, size=size,
ctx=ctx, dtype=dtype, out=out)
elif input_type == (True, False):
return _npi.uniform(low, low=None, high=high, size=size,
ctx=ctx, dtype=dtype, out=out)
else:
return _npi.uniform(low=low, high=high, size=size,
ctx=ctx, dtype=dtype, out=out)

raise ValueError(
"Distribution parameters must be either mxnet.numpy.ndarray or numbers")
37 changes: 36 additions & 1 deletion python/mxnet/numpy/random.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,40 @@
"""Namespace for ops used in imperative programming."""

from __future__ import absolute_import
from ..ndarray import numpy as _mx_nd_np

__all__ = []
__all__ = ['uniform']


def uniform(low=0.0, high=1.0, size=None, dtype=None, ctx=None, out=None):
"""Draw samples from a uniform distribution.
Samples are uniformly distributed over the half-open interval
``[low, high)`` (includes low, but excludes high). In other words,
any value within the given interval is equally likely to be drawn
by `uniform`.
Parameters
----------
low : float, ndarray, optional
Lower boundary of the output interval. All values generated will be
greater than or equal to low. The default value is 0.
high : float, ndarray, optional
Upper boundary of the output interval. All values generated will be
less than high. The default value is 1.0.
size : int or tuple of ints, optional
Output shape. If the given shape is, e.g., ``(m, n, k)``, then
``m * n * k`` samples are drawn. If size is ``None`` (default),
a scalar tensor containing a single value is returned if
``low`` and ``high`` are both scalars.
dtype : {'float16', 'float32', 'float64'}, optional
Data type of output samples. Default is 'float32'
ctx : Context, optional
Device context of output. Default is current context.
Returns
-------
out : ndarray
Drawn samples from the parameterized uniform distribution.
"""
return _mx_nd_np.random.uniform(low, high, size=size, ctx=ctx, dtype=dtype, out=out)
62 changes: 61 additions & 1 deletion python/mxnet/symbol/numpy/random.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,65 @@
"""Namespace for operators used in Gluon dispatched by F=symbol."""

from __future__ import absolute_import
from ...context import current_context
from . import _internal as _npi

__all__ = []
__all__ = ['uniform']


def uniform(low=0.0, high=1.0, size=None, dtype=None, ctx=None, out=None):
"""Draw samples from a uniform distribution.
Samples are uniformly distributed over the half-open interval
``[low, high)`` (includes low, but excludes high). In other words,
any value within the given interval is equally likely to be drawn
by `uniform`.
Parameters
----------
low : float, ndarray, optional
Lower boundary of the output interval. All values generated will be
greater than or equal to low. The default value is 0.
high : float, ndarray, optional
Upper boundary of the output interval. All values generated will be
less than high. The default value is 1.0.
size : int or tuple of ints, optional
Output shape. If the given shape is, e.g., ``(m, n, k)``, then
``m * n * k`` samples are drawn. If size is ``None`` (default),
a scalar tensor containing a single value is returned if
``low`` and ``high`` are both scalars.
dtype : {'float16', 'float32', 'float64'}, optional
Data type of output samples. Default is 'float32'
ctx : Context, optional
Device context of output. Default is current context.
Returns
-------
out : ndarray
Drawn samples from the parameterized uniform distribution.
"""
from ._symbol import _Symbol as np_symbol
input_type = (isinstance(low, np_symbol), isinstance(high, np_symbol))
if dtype is None:
dtype = 'float32'
if ctx is None:
ctx = current_context()
if out is not None:
size = out.shape
if size == ():
size = None
if input_type == (True, True):
return _npi.uniform(low, high, low=None, high=None, size=size,
ctx=ctx, dtype=dtype, out=out)
elif input_type == (False, True):
return _npi.uniform(high, low=low, high=None, size=size,
ctx=ctx, dtype=dtype, out=out)
elif input_type == (True, False):
return _npi.uniform(low, low=None, high=high, size=size,
ctx=ctx, dtype=dtype, out=out)
else:
return _npi.uniform(low=low, high=high, size=size,
ctx=ctx, dtype=dtype, out=out)

raise ValueError(
"Distribution parameters must be either mxnet.numpy.ndarray or numbers")
177 changes: 177 additions & 0 deletions src/operator/numpy/random/dist_common.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

/*!
* Copyright (c) 2015 by Contributors
* \file dist_common.h
* \brief Function definition of common functions for distributions
* \with two parameters.
*/

#ifndef MXNET_OPERATOR_NUMPY_RANDOM_DIST_COMMON_H_
#define MXNET_OPERATOR_NUMPY_RANDOM_DIST_COMMON_H_

#include <mshadow/base.h>
#include <mxnet/operator_util.h>
#include <algorithm>
#include <string>
#include <vector>
#include "../../elemwise_op_common.h"
#include "../../mshadow_op.h"
#include "../../mxnet_op.h"
#include "../../operator_common.h"
#include "../../tensor/elemwise_binary_broadcast_op.h"

namespace mxnet {
namespace op {

inline int FillShape(const mxnet::TShape &lshape, const mxnet::TShape &rshape,
const mxnet::TShape &oshape, mxnet::TShape *new_lshape,
mxnet::TShape *new_rshape, mxnet::TShape *new_oshape) {
const int odim = std::max(oshape.ndim(), broadcast::MAX_DIM);
*new_lshape = mxnet::TShape(odim, 1);
*new_rshape = mxnet::TShape(odim, 1);
*new_oshape = mxnet::TShape(odim, 1);
int bl = oshape.ndim() - lshape.ndim();
int br = oshape.ndim() - rshape.ndim();
int j = 0, lprod = 1, rprod = 1, oprod = 1;
for (int i = 0; i < oshape.ndim(); ++i) {
int l = 1;
int r = 1;
int o = oshape[i];
if (i >= bl) l = lshape[i - bl];
if (i >= br) r = rshape[i - br];
if ((lprod != rprod || lprod != oprod || l != r || l != o) &&
(lprod * l > 1 || rprod * r > 1 || oprod * o > 1)) {
(*new_lshape)[j] = lprod;
(*new_rshape)[j] = rprod;
(*new_oshape)[j] = oprod;
lprod = rprod = oprod = 1;
++j;
}
lprod *= l;
rprod *= r;
oprod *= o;
}
if (lprod > 1 || rprod > 1 || oprod > 1) {
(*new_lshape)[j] = lprod;
(*new_rshape)[j] = rprod;
(*new_oshape)[j] = oprod;
++j;
}
if (j <= broadcast::MAX_DIM) {
BROADCAST_NDIM_SWITCH(j, NDim, {
new_lshape->assign(new_lshape->begin(), new_lshape->begin() + NDim);
new_rshape->assign(new_rshape->begin(), new_rshape->begin() + NDim);
new_oshape->assign(new_oshape->begin(), new_oshape->begin() + NDim);
});
} else {
LOG(FATAL) << "Too many broadcast dimensions with operands " << lshape
<< " " << rshape;
}
return j;
}

inline void CheckBroadcastable(const mxnet::TShape &from,
const mxnet::TShape &to) {
const int bl = to.ndim() - from.ndim();
const int br = 0;
for (int i = 0; i < to.ndim(); ++i) {
int l = 1, r = 1;
if (i >= bl) l = from[i - bl];
if (i >= br) r = to[i - br];
if (!mxnet::dim_size_is_known(l) || !mxnet::dim_size_is_known(r)) continue;
if (l != r) {
// Make it compatible with NumPy.
// For example, (2, 3) cannot broadcast to (2, 0, 3), but (1, 3) can
// broadcast to (2, 0, 3).
CHECK(l == 1 || r == 1)
<< "operands could not be broadcast together with shapes " << from
<< " " << to;
}
}
}

inline void InferBroadcastShape(const mxnet::TShape &lhs,
const mxnet::TShape &rhs,
mxnet::TShape *out_ptr) {
mxnet::TShape &out = (*out_ptr);
const int bl = out.ndim() - lhs.ndim();
const int br = out.ndim() - rhs.ndim();
for (int i = 0; i < out.ndim(); ++i) {
int l = 1, r = 1;
if (i >= bl) l = lhs[i - bl];
if (i >= br) r = rhs[i - br];
if (!mxnet::dim_size_is_known(l) || !mxnet::dim_size_is_known(r)) continue;
if (l != r) {
// Make it compatible with NumPy.
// For example, (2, 3) cannot broadcast to (2, 0, 3), but (1, 3) can
// broadcast to (2, 0, 3).
CHECK(l == 1 || r == 1)
<< "operands could not be broadcast together with shapes " << lhs
<< " " << rhs;
out[i] = (l == 1 ? r : l);
} else {
out[i] = l;
}
}
}

template <typename DistParam>
inline bool TwoparamsDistOpShape(const nnvm::NodeAttrs &attrs,
std::vector<TShape> *in_attrs,
std::vector<TShape> *out_attrs) {
const DistParam &param = nnvm::get<DistParam>(attrs.parsed);
CHECK_EQ(out_attrs->size(), 1U);
if (param.size.has_value()) {
// Size declared.
std::vector<dim_t> oshape_vec;
const mxnet::Tuple<int> &size = param.size.value();
for (int i = 0; i < size.ndim(); ++i) {
oshape_vec.emplace_back(size[i]);
}
SHAPE_ASSIGN_CHECK(*out_attrs, 0, TShape(oshape_vec));
for (size_t input_idx = 0; input_idx < in_attrs->size(); input_idx++) {
CheckBroadcastable((*in_attrs)[input_idx], (*out_attrs)[0]);
}
} else {
// Size undeclared.
if (in_attrs->size() == 2U) {
// Both params from ndarray.
mxnet::TShape &low = (*in_attrs)[0];
mxnet::TShape &high = (*in_attrs)[1];
mxnet::TShape out(std::max(low.ndim(), high.ndim()), -1);
InferBroadcastShape(low, high, &out);
SHAPE_ASSIGN_CHECK(*out_attrs, 0, out);
} else if (in_attrs->size() == 1U) {
// One param from ndarray.
SHAPE_ASSIGN_CHECK(*out_attrs, 0, in_attrs->at(0))
} else if (in_attrs->size() == 0) {
// Two scalar case.
SHAPE_ASSIGN_CHECK(*out_attrs, 0, TShape(0, -1))
return true;
}
}
return out_attrs->at(0).ndim() != 0U;
}

} // namespace op
} // namespace mxnet

#endif // MXNET_OPERATOR_NUMPY_RANDOM_DIST_COMMON_H_ */
Loading

0 comments on commit 521e925

Please sign in to comment.