#!/usr/bin/env python3

import io
import unittest
import unittest.mock
from typing import Any, Callable, List, Tuple, Union

import torch
from captum._utils.typing import BaselineType, TensorOrTupleOfTensorsGeneric
from captum.attr._core.kernel_shap import KernelShap
from tests.helpers.basic import (
    BaseTest,
    assertTensorAlmostEqual,
    assertTensorTuplesAlmostEqual,
)
from tests.helpers.basic_models import (
    BasicModel_MultiLayer,
    BasicModel_MultiLayer_MultiInput,
)


class Test(BaseTest):
    def setUp(self) -> None:
        super().setUp()
        try:
            import sklearn  # noqa: F401

            assert (
                sklearn.__version__ >= "0.23.0"
            ), "Must have sklearn version 0.23.0 or higher"
        except (ImportError, AssertionError):
            raise unittest.SkipTest("Skipping KernelShap tests, sklearn not available.")

    def test_linear_kernel_shap(self) -> None:
        net = BasicModel_MultiLayer()
        inp = torch.tensor([[20.0, 50.0, 30.0]], requires_grad=True)
        baseline = torch.tensor([[10.0, 20.0, 10.0]], requires_grad=True)

        self._kernel_shap_test_assert(
            net,
            inp,
            [40.0, 120.0, 80.0],
            n_samples=500,
            baselines=baseline,
            expected_coefs=[40.0, 120.0, 80.0],
        )

    def test_simple_kernel_shap(self) -> None:
        net = BasicModel_MultiLayer()
        inp = torch.tensor([[20.0, 50.0, 30.0]], requires_grad=True)
        self._kernel_shap_test_assert(
            net,
            inp,
            [76.66666, 196.66666, 116.66666],
            perturbations_per_eval=(1, 2, 3),
            n_samples=500,
        )

    def test_simple_kernel_shap_with_mask(self) -> None:
        net = BasicModel_MultiLayer()
        inp = torch.tensor([[20.0, 50.0, 30.0]], requires_grad=True)
        self._kernel_shap_test_assert(
            net,
            inp,
            [275.0, 275.0, 115.0],
            feature_mask=torch.tensor([[0, 0, 1]]),
            perturbations_per_eval=(1, 2, 3),
            expected_coefs=[275.0, 115.0],
        )

    @unittest.mock.patch("sys.stderr", new_callable=io.StringIO)
    def test_simple_kernel_shap_with_show_progress(self, mock_stderr) -> None:
        net = BasicModel_MultiLayer()
        inp = torch.tensor([[20.0, 50.0, 30.0]], requires_grad=True)

        # test progress output for each batch size
        for bsz in (1, 2, 3):
            self._kernel_shap_test_assert(
                net,
                inp,
                [76.66666, 196.66666, 116.66666],
                perturbations_per_eval=(bsz,),
                n_samples=500,
                show_progress=True,
            )
            output = mock_stderr.getvalue()

            # to test if progress calculation aligns with the actual iteration
            # all perturbations_per_eval should reach progress of 100%
            assert (
                "Kernel Shap attribution: 100%" in output
            ), f"Error progress output: {repr(output)}"

            mock_stderr.seek(0)
            mock_stderr.truncate(0)

    def test_simple_kernel_shap_with_baselines(self) -> None:
        net = BasicModel_MultiLayer()
        inp = torch.tensor([[20.0, 50.0, 30.0]])
        self._kernel_shap_test_assert(
            net,
            inp,
            [248.0, 248.0, 104.0],
            feature_mask=torch.tensor([[0, 0, 1]]),
            baselines=4,
            perturbations_per_eval=(1, 2, 3),
        )

    def test_simple_batch_kernel_shap(self) -> None:
        net = BasicModel_MultiLayer()
        inp = torch.tensor([[2.0, 10.0, 3.0], [20.0, 50.0, 30.0]], requires_grad=True)
        self._kernel_shap_test_assert(
            net,
            inp,
            [[7.0, 32.5, 10.5], [76.66666, 196.66666, 116.66666]],
            perturbations_per_eval=(1, 2, 3),
            n_samples=20000,
        )

    def test_simple_batch_kernel_shap_with_mask(self) -> None:
        net = BasicModel_MultiLayer()
        inp = torch.tensor([[2.0, 10.0, 3.0], [20.0, 50.0, 30.0]], requires_grad=True)
        self._kernel_shap_test_assert(
            net,
            inp,
            [[39.5, 39.5, 10.5], [275.0, 275.0, 115.0]],
            feature_mask=torch.tensor([[0, 0, 1], [1, 1, 0]]),
            perturbations_per_eval=(1, 2, 3),
            n_samples=100,
            expected_coefs=[[39.5, 10.5], [115.0, 275.0]],
        )

    def test_multi_input_kernel_shap_without_mask(self) -> None:
        net = BasicModel_MultiLayer_MultiInput()
        inp1 = torch.tensor([[23.0, 0.0, 0.0]])
        inp2 = torch.tensor([[20.0, 0.0, 50.0]])
        inp3 = torch.tensor([[0.0, 100.0, 10.0]])
        expected = (
            [[90, 0, 0]],
            [[78, 0, 198]],
            [[0, 398, 38]],
        )
        self._kernel_shap_test_assert(
            net,
            (inp1, inp2, inp3),
            expected,
            additional_input=(1,),
            n_samples=2000,
        )

    def test_multi_input_kernel_shap_with_mask(self) -> None:
        net = BasicModel_MultiLayer_MultiInput()
        inp1 = torch.tensor([[20.0, 50.0, 30.0]])
        inp2 = torch.tensor([[0.0, 100.0, 0.0]])
        inp3 = torch.tensor([[2.0, 10.0, 3.0]])
        mask1 = torch.tensor([[0, 1, 0]])
        mask2 = torch.tensor([[0, 1, 2]])
        mask3 = torch.tensor([[0, 0, 0]])
        expected = (
            [[255.0, 595.0, 255.0]],
            [[255.0, 595.0, 0.0]],
            [[255.0, 255.0, 255.0]],
        )
        self._kernel_shap_test_assert(
            net,
            (inp1, inp2, inp3),
            expected,
            additional_input=(1,),
            feature_mask=(mask1, mask2, mask3),
        )
        expected_with_baseline = (
            [[184, 580.0, 184]],
            [[184, 580.0, -12.0]],
            [[184, 184, 184]],
        )
        self._kernel_shap_test_assert(
            net,
            (inp1, inp2, inp3),
            expected_with_baseline,
            additional_input=(1,),
            feature_mask=(mask1, mask2, mask3),
            baselines=(2, 3.0, 4),
            perturbations_per_eval=(1, 2, 3),
        )

    def test_multi_input_batch_kernel_shap_without_mask(self) -> None:
        net = BasicModel_MultiLayer_MultiInput()
        inp1 = torch.tensor([[23.0, 0.0, 0.0], [20.0, 50.0, 30.0]])
        inp2 = torch.tensor([[20.0, 0.0, 50.0], [0.0, 100.0, 0.0]])
        inp3 = torch.tensor([[0.0, 100.0, 10.0], [0.0, 10.0, 0.0]])
        expected = (
            [[90, 0, 0], [78.0, 198.0, 118.0]],
            [[78, 0, 198], [0.0, 398.0, 0.0]],
            [[0, 398, 38], [0.0, 38.0, 0.0]],
        )
        self._kernel_shap_test_assert(
            net,
            (inp1, inp2, inp3),
            expected,
            additional_input=(1,),
            n_samples=2500,
            expected_coefs=[
                [90.0, 0, 0, 78, 0, 198, 0, 398, 38],
                [78.0, 198.0, 118.0, 0.0, 398.0, 0.0, 0.0, 38.0, 0.0],
            ],
        )

    def test_multi_input_batch_kernel_shap(self) -> None:
        net = BasicModel_MultiLayer_MultiInput()
        inp1 = torch.tensor([[23.0, 100.0, 0.0], [20.0, 50.0, 30.0]])
        inp2 = torch.tensor([[20.0, 50.0, 30.0], [0.0, 100.0, 0.0]])
        inp3 = torch.tensor([[0.0, 100.0, 10.0], [2.0, 10.0, 3.0]])
        mask1 = torch.tensor([[1, 1, 1], [0, 1, 0]])
        mask2 = torch.tensor([[0, 1, 2]])
        mask3 = torch.tensor([[0, 1, 2], [0, 0, 0]])
        expected = (
            [[1088.6666, 1088.6666, 1088.6666], [255.0, 595.0, 255.0]],
            [[76.6666, 1088.6666, 156.6666], [255.0, 595.0, 0.0]],
            [[76.6666, 1088.6666, 156.6666], [255.0, 255.0, 255.0]],
        )
        self._kernel_shap_test_assert(
            net,
            (inp1, inp2, inp3),
            expected,
            additional_input=(1,),
            feature_mask=(mask1, mask2, mask3),
            n_samples=300,
        )
        expected_with_baseline = (
            [[1040, 1040, 1040], [184, 580.0, 184]],
            [[52, 1040, 132], [184, 580.0, -12.0]],
            [[52, 1040, 132], [184, 184, 184]],
        )
        self._kernel_shap_test_assert(
            net,
            (inp1, inp2, inp3),
            expected_with_baseline,
            additional_input=(1,),
            feature_mask=(mask1, mask2, mask3),
            baselines=(2, 3.0, 4),
            perturbations_per_eval=(1, 2, 3),
        )

    # Remaining tests are for cases where forward function returns a scalar
    # as either a float, integer, 0d tensor or 1d tensor.
    def test_single_kernel_shap_scalar_float(self) -> None:
        net = BasicModel_MultiLayer()
        self._single_input_scalar_kernel_shap_assert(
            lambda inp: torch.sum(net(inp)).item()
        )

    def test_single_kernel_shap_scalar_tensor_0d(self) -> None:
        net = BasicModel_MultiLayer()
        self._single_input_scalar_kernel_shap_assert(lambda inp: torch.sum(net(inp)))

    def test_single_kernel_shap_scalar_tensor_1d(self) -> None:
        net = BasicModel_MultiLayer()
        self._single_input_scalar_kernel_shap_assert(
            lambda inp: torch.sum(net(inp)).reshape(1)
        )

    def test_single_kernel_shap_scalar_int(self) -> None:
        net = BasicModel_MultiLayer()
        self._single_input_scalar_kernel_shap_assert(
            lambda inp: int(torch.sum(net(inp)).item())
        )

    def _single_input_scalar_kernel_shap_assert(self, func: Callable) -> None:
        inp = torch.tensor([[2.0, 10.0, 3.0]], requires_grad=True)
        mask = torch.tensor([[0, 0, 1]])

        self._kernel_shap_test_assert(
            func,
            inp,
            [[79.0, 79.0, 21.0]],
            feature_mask=mask,
            perturbations_per_eval=(1,),
            target=None,
        )

    def test_multi_inp_kernel_shap_scalar_tensor_0d(self) -> None:
        net = BasicModel_MultiLayer_MultiInput()
        self._multi_input_scalar_kernel_shap_assert(lambda *inp: torch.sum(net(*inp)))

    def test_multi_inp_kernel_shap_scalar_tensor_1d(self) -> None:
        net = BasicModel_MultiLayer_MultiInput()
        self._multi_input_scalar_kernel_shap_assert(
            lambda *inp: torch.sum(net(*inp)).reshape(1)
        )

    def test_multi_inp_kernel_shap_scalar_tensor_int(self) -> None:
        net = BasicModel_MultiLayer_MultiInput()
        self._multi_input_scalar_kernel_shap_assert(
            lambda *inp: int(torch.sum(net(*inp)).item())
        )

    def test_multi_inp_kernel_shap_scalar_float(self) -> None:
        net = BasicModel_MultiLayer_MultiInput()
        self._multi_input_scalar_kernel_shap_assert(
            lambda *inp: torch.sum(net(*inp)).item()
        )

    def _multi_input_scalar_kernel_shap_assert(self, func: Callable) -> None:
        inp1 = torch.tensor([[23.0, 100.0, 0.0], [20.0, 50.0, 30.0]])
        inp2 = torch.tensor([[20.0, 50.0, 30.0], [0.0, 100.0, 0.0]])
        inp3 = torch.tensor([[0.0, 100.0, 10.0], [20.0, 10.0, 13.0]])
        mask1 = torch.tensor([[1, 1, 1]])
        mask2 = torch.tensor([[0, 1, 2]])
        mask3 = torch.tensor([[0, 1, 2]])
        expected = (
            [[3850.6666, 3850.6666, 3850.6666]],
            [[306.6666, 3850.6666, 410.6666]],
            [[306.6666, 3850.6666, 410.6666]],
        )

        self._kernel_shap_test_assert(
            func,
            (inp1, inp2, inp3),
            expected,
            additional_input=(1,),
            feature_mask=(mask1, mask2, mask3),
            perturbations_per_eval=(1,),
            target=None,
            n_samples=1500,
        )

    def _kernel_shap_test_assert(
        self,
        model: Callable,
        test_input: TensorOrTupleOfTensorsGeneric,
        expected_attr,
        feature_mask: Union[None, TensorOrTupleOfTensorsGeneric] = None,
        additional_input: Any = None,
        perturbations_per_eval: Tuple[int, ...] = (1,),
        baselines: BaselineType = None,
        target: Union[None, int] = 0,
        n_samples: int = 100,
        delta: float = 1.0,
        expected_coefs: Union[None, List[float], List[List[float]]] = None,
        show_progress: bool = False,
    ) -> None:
        for batch_size in perturbations_per_eval:
            kernel_shap = KernelShap(model)
            attributions = kernel_shap.attribute(
                test_input,
                target=target,
                feature_mask=feature_mask,
                additional_forward_args=additional_input,
                baselines=baselines,
                perturbations_per_eval=batch_size,
                n_samples=n_samples,
                show_progress=show_progress,
            )
            assertTensorTuplesAlmostEqual(
                self, attributions, expected_attr, delta=delta, mode="max"
            )

            if expected_coefs is not None:
                # Test with return_input_shape = False
                attributions = kernel_shap.attribute(
                    test_input,
                    target=target,
                    feature_mask=feature_mask,
                    additional_forward_args=additional_input,
                    baselines=baselines,
                    perturbations_per_eval=batch_size,
                    n_samples=n_samples,
                    return_input_shape=False,
                    show_progress=show_progress,
                )
                assertTensorAlmostEqual(
                    self, attributions, expected_coefs, delta=delta, mode="max"
                )


if __name__ == "__main__":
    unittest.main()

[((71, 5, 71, 64), 'unittest.mock.patch', 'unittest.mock.patch', (), '', False, 'import unittest\n'), ((377, 4, 377, 19), 'unittest.main', 'unittest.main', ({}, {}), '()', False, 'import unittest\n'), ((35, 14, 35, 37), 'tests.helpers.basic_models.BasicModel_MultiLayer', 'BasicModel_MultiLayer', ({}, {}), '()', False, 'from tests.helpers.basic_models import BasicModel_MultiLayer, BasicModel_MultiLayer_MultiInput\n'), ((36, 14, 36, 68), 'torch.tensor', 'torch.tensor', (), '', False, 'import torch\n'), ((37, 19, 37, 73), 'torch.tensor', 'torch.tensor', (), '', False, 'import torch\n'), ((49, 14, 49, 37), 'tests.helpers.basic_models.BasicModel_MultiLayer', 'BasicModel_MultiLayer', ({}, {}), '()', False, 'from tests.helpers.basic_models import BasicModel_MultiLayer, BasicModel_MultiLayer_MultiInput\n'), ((50, 14, 50, 68), 'torch.tensor', 'torch.tensor', (), '', False, 'import torch\n'), ((60, 14, 60, 37), 'tests.helpers.basic_models.BasicModel_MultiLayer', 'BasicModel_MultiLayer', ({}, {}), '()', False, 'from tests.helpers.basic_models import BasicModel_MultiLayer, BasicModel_MultiLayer_MultiInput\n'), ((61, 14, 61, 68), 'torch.tensor', 'torch.tensor', (), '', False, 'import torch\n'), ((73, 14, 73, 37), 'tests.helpers.basic_models.BasicModel_MultiLayer', 'BasicModel_MultiLayer', ({}, {}), '()', False, 'from tests.helpers.basic_models import BasicModel_MultiLayer, BasicModel_MultiLayer_MultiInput\n'), ((74, 14, 74, 68), 'torch.tensor', 'torch.tensor', (), '', False, 'import torch\n'), ((98, 14, 98, 37), 'tests.helpers.basic_models.BasicModel_MultiLayer', 'BasicModel_MultiLayer', ({}, {}), '()', False, 'from tests.helpers.basic_models import BasicModel_MultiLayer, BasicModel_MultiLayer_MultiInput\n'), ((99, 14, 99, 48), 'torch.tensor', 'torch.tensor', ({(99, 27, 99, 47): '[[20.0, 50.0, 30.0]]'}, {}), '([[20.0, 50.0, 30.0]])', False, 'import torch\n'), ((110, 14, 110, 37), 'tests.helpers.basic_models.BasicModel_MultiLayer', 'BasicModel_MultiLayer', ({}, {}), '()', False, 'from tests.helpers.basic_models import BasicModel_MultiLayer, BasicModel_MultiLayer_MultiInput\n'), ((111, 14, 111, 86), 'torch.tensor', 'torch.tensor', (), '', False, 'import torch\n'), ((121, 14, 121, 37), 'tests.helpers.basic_models.BasicModel_MultiLayer', 'BasicModel_MultiLayer', ({}, {}), '()', False, 'from tests.helpers.basic_models import BasicModel_MultiLayer, BasicModel_MultiLayer_MultiInput\n'), ((122, 14, 122, 86), 'torch.tensor', 'torch.tensor', (), '', False, 'import torch\n'), ((134, 14, 134, 48), 'tests.helpers.basic_models.BasicModel_MultiLayer_MultiInput', 'BasicModel_MultiLayer_MultiInput', ({}, {}), '()', False, 'from tests.helpers.basic_models import BasicModel_MultiLayer, BasicModel_MultiLayer_MultiInput\n'), ((135, 15, 135, 47), 'torch.tensor', 'torch.tensor', ({(135, 28, 135, 46): '[[23.0, 0.0, 0.0]]'}, {}), '([[23.0, 0.0, 0.0]])', False, 'import torch\n'), ((136, 15, 136, 48), 'torch.tensor', 'torch.tensor', ({(136, 28, 136, 47): '[[20.0, 0.0, 50.0]]'}, {}), '([[20.0, 0.0, 50.0]])', False, 'import torch\n'), ((137, 15, 137, 49), 'torch.tensor', 'torch.tensor', ({(137, 28, 137, 48): '[[0.0, 100.0, 10.0]]'}, {}), '([[0.0, 100.0, 10.0]])', False, 'import torch\n'), ((152, 14, 152, 48), 'tests.helpers.basic_models.BasicModel_MultiLayer_MultiInput', 'BasicModel_MultiLayer_MultiInput', ({}, {}), '()', False, 'from tests.helpers.basic_models import BasicModel_MultiLayer, BasicModel_MultiLayer_MultiInput\n'), ((153, 15, 153, 49), 'torch.tensor', 'torch.tensor', ({(153, 28, 153, 48): '[[20.0, 50.0, 30.0]]'}, {}), '([[20.0, 50.0, 30.0]])', False, 'import torch\n'), ((154, 15, 154, 48), 'torch.tensor', 'torch.tensor', ({(154, 28, 154, 47): '[[0.0, 100.0, 0.0]]'}, {}), '([[0.0, 100.0, 0.0]])', False, 'import torch\n'), ((155, 15, 155, 47), 'torch.tensor', 'torch.tensor', ({(155, 28, 155, 46): '[[2.0, 10.0, 3.0]]'}, {}), '([[2.0, 10.0, 3.0]])', False, 'import torch\n'), ((156, 16, 156, 41), 'torch.tensor', 'torch.tensor', ({(156, 29, 156, 40): '[[0, 1, 0]]'}, {}), '([[0, 1, 0]])', False, 'import torch\n'), ((157, 16, 157, 41), 'torch.tensor', 'torch.tensor', ({(157, 29, 157, 40): '[[0, 1, 2]]'}, {}), '([[0, 1, 2]])', False, 'import torch\n'), ((158, 16, 158, 41), 'torch.tensor', 'torch.tensor', ({(158, 29, 158, 40): '[[0, 0, 0]]'}, {}), '([[0, 0, 0]])', False, 'import torch\n'), ((187, 14, 187, 48), 'tests.helpers.basic_models.BasicModel_MultiLayer_MultiInput', 'BasicModel_MultiLayer_MultiInput', ({}, {}), '()', False, 'from tests.helpers.basic_models import BasicModel_MultiLayer, BasicModel_MultiLayer_MultiInput\n'), ((188, 15, 188, 67), 'torch.tensor', 'torch.tensor', ({(188, 28, 188, 66): '[[23.0, 0.0, 0.0], [20.0, 50.0, 30.0]]'}, {}), '([[23.0, 0.0, 0.0], [20.0, 50.0, 30.0]])', False, 'import torch\n'), ((189, 15, 189, 67), 'torch.tensor', 'torch.tensor', ({(189, 28, 189, 66): '[[20.0, 0.0, 50.0], [0.0, 100.0, 0.0]]'}, {}), '([[20.0, 0.0, 50.0], [0.0, 100.0, 0.0]])', False, 'import torch\n'), ((190, 15, 190, 67), 'torch.tensor', 'torch.tensor', ({(190, 28, 190, 66): '[[0.0, 100.0, 10.0], [0.0, 10.0, 0.0]]'}, {}), '([[0.0, 100.0, 10.0], [0.0, 10.0, 0.0]])', False, 'import torch\n'), ((209, 14, 209, 48), 'tests.helpers.basic_models.BasicModel_MultiLayer_MultiInput', 'BasicModel_MultiLayer_MultiInput', ({}, {}), '()', False, 'from tests.helpers.basic_models import BasicModel_MultiLayer, BasicModel_MultiLayer_MultiInput\n'), ((210, 15, 210, 69), 'torch.tensor', 'torch.tensor', ({(210, 28, 210, 68): '[[23.0, 100.0, 0.0], [20.0, 50.0, 30.0]]'}, {}), '([[23.0, 100.0, 0.0], [20.0, 50.0, 30.0]])', False, 'import torch\n'), ((211, 15, 211, 68), 'torch.tensor', 'torch.tensor', ({(211, 28, 211, 67): '[[20.0, 50.0, 30.0], [0.0, 100.0, 0.0]]'}, {}), '([[20.0, 50.0, 30.0], [0.0, 100.0, 0.0]])', False, 'import torch\n'), ((212, 15, 212, 67), 'torch.tensor', 'torch.tensor', ({(212, 28, 212, 66): '[[0.0, 100.0, 10.0], [2.0, 10.0, 3.0]]'}, {}), '([[0.0, 100.0, 10.0], [2.0, 10.0, 3.0]])', False, 'import torch\n'), ((213, 16, 213, 52), 'torch.tensor', 'torch.tensor', ({(213, 29, 213, 51): '[[1, 1, 1], [0, 1, 0]]'}, {}), '([[1, 1, 1], [0, 1, 0]])', False, 'import torch\n'), ((214, 16, 214, 41), 'torch.tensor', 'torch.tensor', ({(214, 29, 214, 40): '[[0, 1, 2]]'}, {}), '([[0, 1, 2]])', False, 'import torch\n'), ((215, 16, 215, 52), 'torch.tensor', 'torch.tensor', ({(215, 29, 215, 51): '[[0, 1, 2], [0, 0, 0]]'}, {}), '([[0, 1, 2], [0, 0, 0]])', False, 'import torch\n'), ((247, 14, 247, 37), 'tests.helpers.basic_models.BasicModel_MultiLayer', 'BasicModel_MultiLayer', ({}, {}), '()', False, 'from tests.helpers.basic_models import BasicModel_MultiLayer, BasicModel_MultiLayer_MultiInput\n'), ((253, 14, 253, 37), 'tests.helpers.basic_models.BasicModel_MultiLayer', 'BasicModel_MultiLayer', ({}, {}), '()', False, 'from tests.helpers.basic_models import BasicModel_MultiLayer, BasicModel_MultiLayer_MultiInput\n'), ((257, 14, 257, 37), 'tests.helpers.basic_models.BasicModel_MultiLayer', 'BasicModel_MultiLayer', ({}, {}), '()', False, 'from tests.helpers.basic_models import BasicModel_MultiLayer, BasicModel_MultiLayer_MultiInput\n'), ((263, 14, 263, 37), 'tests.helpers.basic_models.BasicModel_MultiLayer', 'BasicModel_MultiLayer', ({}, {}), '()', False, 'from tests.helpers.basic_models import BasicModel_MultiLayer, BasicModel_MultiLayer_MultiInput\n'), ((269, 14, 269, 66), 'torch.tensor', 'torch.tensor', (), '', False, 'import torch\n'), ((270, 15, 270, 40), 'torch.tensor', 'torch.tensor', ({(270, 28, 270, 39): '[[0, 0, 1]]'}, {}), '([[0, 0, 1]])', False, 'import torch\n'), ((282, 14, 282, 48), 'tests.helpers.basic_models.BasicModel_MultiLayer_MultiInput', 'BasicModel_MultiLayer_MultiInput', ({}, {}), '()', False, 'from tests.helpers.basic_models import BasicModel_MultiLayer, BasicModel_MultiLayer_MultiInput\n'), ((286, 14, 286, 48), 'tests.helpers.basic_models.BasicModel_MultiLayer_MultiInput', 'BasicModel_MultiLayer_MultiInput', ({}, {}), '()', False, 'from tests.helpers.basic_models import BasicModel_MultiLayer, BasicModel_MultiLayer_MultiInput\n'), ((292, 14, 292, 48), 'tests.helpers.basic_models.BasicModel_MultiLayer_MultiInput', 'BasicModel_MultiLayer_MultiInput', ({}, {}), '()', False, 'from tests.helpers.basic_models import BasicModel_MultiLayer, BasicModel_MultiLayer_MultiInput\n'), ((298, 14, 298, 48), 'tests.helpers.basic_models.BasicModel_MultiLayer_MultiInput', 'BasicModel_MultiLayer_MultiInput', ({}, {}), '()', False, 'from tests.helpers.basic_models import BasicModel_MultiLayer, BasicModel_MultiLayer_MultiInput\n'), ((304, 15, 304, 69), 'torch.tensor', 'torch.tensor', ({(304, 28, 304, 68): '[[23.0, 100.0, 0.0], [20.0, 50.0, 30.0]]'}, {}), '([[23.0, 100.0, 0.0], [20.0, 50.0, 30.0]])', False, 'import torch\n'), ((305, 15, 305, 68), 'torch.tensor', 'torch.tensor', ({(305, 28, 305, 67): '[[20.0, 50.0, 30.0], [0.0, 100.0, 0.0]]'}, {}), '([[20.0, 50.0, 30.0], [0.0, 100.0, 0.0]])', False, 'import torch\n'), ((306, 15, 306, 69), 'torch.tensor', 'torch.tensor', ({(306, 28, 306, 68): '[[0.0, 100.0, 10.0], [20.0, 10.0, 13.0]]'}, {}), '([[0.0, 100.0, 10.0], [20.0, 10.0, 13.0]])', False, 'import torch\n'), ((307, 16, 307, 41), 'torch.tensor', 'torch.tensor', ({(307, 29, 307, 40): '[[1, 1, 1]]'}, {}), '([[1, 1, 1]])', False, 'import torch\n'), ((308, 16, 308, 41), 'torch.tensor', 'torch.tensor', ({(308, 29, 308, 40): '[[0, 1, 2]]'}, {}), '([[0, 1, 2]])', False, 'import torch\n'), ((309, 16, 309, 41), 'torch.tensor', 'torch.tensor', ({(309, 29, 309, 40): '[[0, 1, 2]]'}, {}), '([[0, 1, 2]])', False, 'import torch\n'), ((343, 26, 343, 43), 'captum.attr._core.kernel_shap.KernelShap', 'KernelShap', ({(343, 37, 343, 42): 'model'}, {}), '(model)', False, 'from captum.attr._core.kernel_shap import KernelShap\n'), ((354, 12, 356, 13), 'tests.helpers.basic.assertTensorTuplesAlmostEqual', 'assertTensorTuplesAlmostEqual', (), '', False, 'from tests.helpers.basic import BaseTest, assertTensorAlmostEqual, assertTensorTuplesAlmostEqual\n'), ((32, 18, 32, 88), 'unittest.SkipTest', 'unittest.SkipTest', ({(32, 36, 32, 87): '"""Skipping KernelShap tests, sklearn not available."""'}, {}), "('Skipping KernelShap tests, sklearn not available.')", False, 'import unittest\n'), ((66, 25, 66, 50), 'torch.tensor', 'torch.tensor', ({(66, 38, 66, 49): '[[0, 0, 1]]'}, {}), '([[0, 0, 1]])', False, 'import torch\n'), ((104, 25, 104, 50), 'torch.tensor', 'torch.tensor', ({(104, 38, 104, 49): '[[0, 0, 1]]'}, {}), '([[0, 0, 1]])', False, 'import torch\n'), ((127, 25, 127, 61), 'torch.tensor', 'torch.tensor', ({(127, 38, 127, 60): '[[0, 0, 1], [1, 1, 0]]'}, {}), '([[0, 0, 1], [1, 1, 0]])', False, 'import torch\n'), ((371, 16, 373, 17), 'tests.helpers.basic.assertTensorAlmostEqual', 'assertTensorAlmostEqual', (), '', False, 'from tests.helpers.basic import BaseTest, assertTensorAlmostEqual, assertTensorTuplesAlmostEqual\n')]