'Build OpTreeNode.
Arguments:
a (OpTreeNode, Tensor, numeric): left-hand side operand.
b (OpTreeNode, Tensor, numeric): right-hand side operand.
out (Tensor, optional): where the result will be stored. If out is
not None, the op-tree will be executed.
kwargs: optional argument such as axis of the reducion.'

@staticmethod
def build(op, a, b, out=None, **kwargs):

    for arg in (a, b):
        if (not isinstance(arg, (int, float, Tensor, OpTreeNode, type(None)))):
            return NotImplemented
    out_shape = [1, 1]
    if isinstance(a, (OpTreeNode, Tensor)):
        a_shape = a.shape
    elif isinstance(a, (float, int)):
        a_shape = [1, 1]
    else:
        a_shape = [0, 0]
    if isinstance(b, (OpTreeNode, Tensor)):
        b_shape = b.shape
    elif isinstance(b, (float, int)):
        b_shape = [1, 1]
    else:
        b_shape = [0, 0]
    if (len(a_shape) == 1):
        a_shape = (a_shape + (1,))
    if (len(b_shape) == 1):
        b_shape = (b_shape + (1,))
    if (op in OpCollection.ew_ops):
        for i in range(2):
            out_shape[i] = max(a_shape[i], b_shape[i])
    elif (op in OpCollection.reduction_ops):
        if ('axis' in kwargs):
            out_shape = list(a_shape)
            out_shape[kwargs['axis']] = 1
        else:
            pass
    elif (op == 'assign'):
        out_shape = a_shape
    elif (op == 'dot'):
        assert ((len(a_shape) == len(b_shape)) and (len(b_shape) == 2) and (a_shape[1] == b_shape[0]))
        out_shape = (a_shape[0], b_shape[1])
    elif (op == 'transpose'):
        assert (b is None)
        out_shape = tuple(reversed(a_shape))
    else:
        raise TypeError(('%s    is    not    a    valid    operation' % op))
    out_shape = tuple(out_shape)
    op_dict = {'op': op, 'shape': out_shape}
    op_dict.update(kwargs)
    node = OpTreeNode(op_dict, a, b)
    if (op == 'assign'):
        return node.execute()
    if (out is not None):
        return OpTreeNode({'op': 'assign'}, out, node).execute()
    return node
