def test_column_vector(self):
        R_function = pnl.Reduce(operation=pnl.SUM)
        R_mechanism = pnl.ProcessingMechanism(
            function=pnl.Reduce(operation=pnl.SUM),
            default_variable=[[1], [2], [3], [4], [5]],
            name="R_mechanism")

        assert np.allclose(R_function.execute([[1], [2], [3], [4], [5]]),
                           [1, 2, 3, 4, 5])
        # assert np.allclose(R_function.execute([[1], [2], [3], [4], [5]]), [15.0])
        assert np.allclose(R_function.execute([[[1], [2], [3], [4], [5]]]),
                           [15.0])

        assert np.allclose(R_mechanism.execute([[1], [2], [3], [4], [5]]),
                           [1, 2, 3, 4, 5])
    def test_single_array(self):
        R_function = pnl.Reduce(operation=pnl.SUM)
        R_mechanism = pnl.ProcessingMechanism(
            function=pnl.Reduce(operation=pnl.SUM),
            default_variable=[[1, 2, 3, 4, 5]],
            name="R_mechanism")

        assert np.allclose(R_function.execute([1, 2, 3, 4, 5]), [15.0])
        assert np.allclose(R_function.execute([[1, 2, 3, 4, 5]]), [15.0])
        assert np.allclose(R_function.execute([[[1, 2, 3, 4, 5]]]),
                           [1, 2, 3, 4, 5])
        # assert np.allclose(R_function.execute([[[1, 2, 3, 4, 5]]]), [15.0])

        assert np.allclose(R_mechanism.execute([1, 2, 3, 4, 5]), [[15.0]])
        assert np.allclose(R_mechanism.execute([[1, 2, 3, 4, 5]]), [[15.0]])
        assert np.allclose(R_mechanism.execute([1, 2, 3, 4, 5]), [15.0])
    def test_matrix(self):
        R_function = pnl.Reduce(operation=pnl.SUM)
        R_mechanism = pnl.ProcessingMechanism(
            function=pnl.Reduce(operation=pnl.SUM),
            default_variable=[[1, 2, 3], [4, 5, 6], [7, 8, 9]],
            name="R_mechanism")

        assert np.allclose(
            R_function.execute([[1, 2, 3], [4, 5, 6], [7, 8, 9]]), [6, 15, 24])
        assert np.allclose(
            R_function.execute([[[1, 2, 3], [4, 5, 6], [7, 8, 9]]]),
            [12, 15, 18])

        assert np.allclose(
            R_mechanism.execute([[1, 2, 3], [4, 5, 6], [7, 8, 9]]),
            [6, 15, 24])
def test_reduce_function(variable, operation, exponents, weights, scale,
                         offset, mode, benchmark):
    if weights == 'VC':
        weights = [[(-1)**i] for i, v in enumerate(variable)]
    if weights == 'VR':
        weights = [(-1)**i for i, v in enumerate(variable[0])]
    if exponents == 'V':
        exponents = [[v[0]] for v in variable]

    try:
        f = pnl.Reduce(default_variable=variable,
                       operation=operation,
                       exponents=exponents,
                       weights=weights,
                       scale=scale,
                       offset=offset)
    except ValueError as e:
        if not np.isscalar(scale) and "The truth value of an array" in str(e):
            pytest.xfail("vector scale is not supported")
        if not np.isscalar(offset) and "The truth value of an array" in str(e):
            pytest.xfail("vector offset is not supported")
        raise e from None

    if mode == 'Python':
        EX = f.function
    elif mode == 'LLVM':
        e = pnlvm.execution.FuncExecution(f)
        EX = e.execute
    elif mode == 'PTX':
        e = pnlvm.execution.FuncExecution(f)
        EX = e.cuda_execute

    res = benchmark(EX, variable)

    scale = 1.0 if scale is None else scale
    offset = 0.0 if offset is None else offset
    exponent = 1.0 if exponents is None else exponents
    weights = 1.0 if weights is None else weights

    tmp = (variable**exponent) * weights
    if operation == pnl.SUM:
        expected = np.sum(tmp, axis=1) * scale + offset
    if operation == pnl.PRODUCT:
        expected = np.product(tmp, axis=1) * scale + offset

    assert np.allclose(res, expected)
        rate=0.5,
        default_variable=[[0.0, 0.0]],
    ),
    output_ports=["RESULTS"],
    termination_comparison_op=">=",
    default_variable=[[0.0, 0.0]],
)
DECISION = pnl.DDM(
    name="DECISION",
    function=pnl.DriftDiffusionAnalytical(default_variable=[[0.0]]),
    input_ports=[{
        pnl.NAME:
        pnl.ARRAY,
        pnl.VARIABLE: [[0.0, 0.0]],
        pnl.FUNCTION:
        pnl.Reduce(default_variable=[[0.0, 0.0]], weights=[1, -1]),
    }],
)
Conflict_Monitor = pnl.ObjectiveMechanism(
    name="Conflict_Monitor",
    function=pnl.Energy(matrix=[[0, -2.5], [-2.5, 0]],
                        default_variable=[[0.0, 0.0]]),
    monitor=[OUTPUT],
    default_variable=[[0.0, 0.0]],
)

CONTROL = pnl.ControlMechanism(
    name="CONTROL",
    default_allocation=[0.5],
    function=pnl.DefaultAllocationFunction(default_variable=[[1.0]]),
    monitor_for_control=[],