'Infer shape equivalence of arguments based on Numpy broadcast rules
and return shape of output
https://docs.scipy.org/doc/numpy/user/basics.broadcasting.html'

def _broadcast_and_match_shapes(self, args):

    assert any([self._isarray(a) for a in args])
    eqs = []
    for a in args:
        if self._isarray(a):
            eqs.append(copy.copy(self.array_shape_classes[a]))
        else:
            eqs.append([CONST_CLASS])
    ndims = max([len(e) for e in eqs])
    for e in eqs:
        while (len(e) < ndims):
            e.insert(0, CONST_CLASS)
    out_eq = [(-1) for i in range(ndims)]
    for i in range(ndims):
        c = eqs[0][i]
        for e in eqs:
            if ((e[i] != CONST_CLASS) and (e[i] != c)):
                if (c == CONST_CLASS):
                    c = e[i]
                else:
                    c = self._merge_classes(c, e[i])
        out_eq[i] = c
    return out_eq
