def compare_outputs(expected, output, **kwargs):
    """
    Compares expected values and output.
    Returns None if no error, an exception message otherwise.
    """
    SkipDim1 = kwargs.pop("SkipDim1", False)
    NoProb = kwargs.pop("NoProb", False)
    Dec4 = kwargs.pop("Dec4", False)
    Dec3 = kwargs.pop("Dec3", False)
    Dec2 = kwargs.pop("Dec2", False)
    Disc = kwargs.pop("Disc", False)
    Mism = kwargs.pop("Mism", False)
    Opp = kwargs.pop("Opp", False)
    if Opp and not NoProb:
        raise ValueError("Opp is only available if NoProb is True")

    if Dec4:
        kwargs["decimal"] = min(kwargs["decimal"], 4)
    if Dec3:
        kwargs["decimal"] = min(kwargs["decimal"], 3)
    if Dec2:
        kwargs["decimal"] = min(kwargs["decimal"], 2)
        
    if isinstance(expected, numpy.ndarray) and isinstance(output, numpy.ndarray):
        if SkipDim1:
            # Arrays like (2, 1, 2, 3) becomes (2, 2, 3) as one dimension is useless.
            expected = expected.reshape(tuple([d for d in expected.shape if d > 1]))
            output = output.reshape(tuple([d for d in expected.shape if d > 1]))
        if NoProb:
            # One vector is (N,) with scores, negative for class 0
            # positive for class 1
            # The other vector is (N, 2) score in two columns.
            if len(output.shape) == 2 and output.shape[1] == 2 and len(expected.shape) == 1:
                output = output[:, 1]                
            elif len(output.shape) == 1 and len(expected.shape) == 1:
                pass
            elif len(expected.shape) == 1 and len(output.shape) == 2 and \
                    expected.shape[0] == output.shape[0] and output.shape[1] == 1:
                output = output[:, 0]
            elif expected.shape != output.shape:
                raise NotImplementedError("No good shape: {0} != {1}".format(expected.shape, output.shape))
            if Opp:
                output = -output
        if len(expected.shape) == 1 and len(output.shape) == 2 and output.shape[1] == 1:
            output = output.ravel()
        if len(expected.shape) == 2 and len(output.shape) == 1 and expected.shape[1] == 1:
            expected = expected.ravel()
        if not numpy.issubdtype(expected.dtype, numpy.number):
            try:
                assert_array_equal(expected, output)
            except Exception as e:
                if Disc:
                    # Bug to be fixed later.
                    return ExpectedAssertionError(str(e))
                else:
                    return OnnxRuntimeAssertionError(str(e))
        else:
            try:
                assert_array_almost_equal(expected, output, **kwargs)
            except Exception as e:
                expected_ = expected.ravel()
                output_ = output.ravel()
                if len(expected_) == len(output_):
                    diff = numpy.abs(expected_ - output_).max()
                elif Mism:
                    return ExpectedAssertionError("dimension mismatch={0}, {1}\n{2}".format(expected.shape, output.shape, e))
                else:
                    return OnnxRuntimeAssertionError("dimension mismatch={0}, {1}\n{2}".format(expected.shape, output.shape, e))
                if Disc:
                    # Bug to be fixed later.
                    return ExpectedAssertionError("max diff(expected, output)={0}\n{1}".format(diff, e))
                else:
                    return OnnxRuntimeAssertionError("max diff(expected, output)={0}\n{1}".format(diff, e))
    else:
        return OnnxRuntimeAssertionError("Unexpected types {0} != {1}".format(type(expected), type(output)))
    return None
Compares expected values and output.
    Returns None if no error, an exception message otherwise.