def test_shapefactor(backend):
    mc = MockConfig(
        par_map={
            'shapefac1': {
                'paramset':
                unconstrained(
                    name='shapefac1',
                    is_scalar=False,
                    n_parameters=1,
                    inits=[0],
                    bounds=[[0, 10]],
                    fixed=False,
                ),
                'slice':
                slice(0, 1),
            },
            'shapefac2': {
                'paramset':
                unconstrained(
                    name='shapefac2',
                    is_scalar=False,
                    n_parameters=2,
                    inits=[0, 0],
                    bounds=[[0, 10], [0, 10]],
                    fixed=False,
                ),
                'slice':
                slice(1, 3),
            },
        },
        par_order=['shapefac1', 'shapefac2'],
        samples=['signal', 'background'],
        channels=['chan_one', 'chan_two'],
        channel_nbins={
            'chan_one': 1,
            'chan_two': 2
        },
    )

    mega_mods = {
        'shapefactor/shapefac1': {
            'signal': {
                'type': 'shapefactor',
                'name': 'shapefac1',
                'data': {
                    'mask': [True, False, False]
                },
            },
            'background': {
                'type': 'shapefactor',
                'name': 'shapefac1',
                'data': {
                    'mask': [True, False, False]
                },
            },
        },
        'shapefactor/shapefac2': {
            'signal': {
                'type': 'shapefactor',
                'name': 'shapefac2',
                'data': {
                    'mask': [False, True, True]
                },
            },
            'background': {
                'type': 'normfactor',
                'name': 'shapefac2',
                'data': {
                    'mask': [False, True, True]
                },
            },
        },
    }

    hsc = shapefactor_combined([('shapefac1', 'shapefactor'),
                                ('shapefac2', 'shapefactor')], mc, mega_mods)

    mod = hsc.apply(pyhf.tensorlib.astensor([2.0, 3.0, 4.0]))
    shape = pyhf.tensorlib.shape(mod)
    assert shape == (2, 2, 1, 3)

    mod = np.asarray(pyhf.tensorlib.tolist(mod))
    assert np.allclose(mod[0, 0, 0], [2.0, 1.0, 1.0])
    assert np.allclose(mod[1, 0, 0], [1.0, 3.0, 4.0])

    hsc = shapefactor_combined(
        [('shapefac1', 'shapefactor'), ('shapefac2', 'shapefactor')],
        mc,
        mega_mods,
        batch_size=4,
    )
    mod = hsc.apply(
        pyhf.tensorlib.astensor([[2.0, 3.0, 4.0], [5.0, 6.0, 7.0],
                                 [8.0, 9.0, 10.0], [11.0, 12.0, 13.0]]))
    shape = pyhf.tensorlib.shape(mod)
    assert shape == (2, 2, 4, 3)

    mod = np.asarray(pyhf.tensorlib.tolist(mod))
    assert np.allclose(mod[0, 0, 0], [2.0, 1.0, 1.0])
    assert np.allclose(mod[0, 0, 1], [5.0, 1.0, 1.0])
    assert np.allclose(mod[0, 0, 2], [8.0, 1.0, 1.0])
    assert np.allclose(mod[0, 0, 3], [11.0, 1.0, 1.0])

    assert np.allclose(mod[1, 0, 0], [1.0, 3.0, 4.0])
    assert np.allclose(mod[1, 0, 1], [1.0, 6.0, 7.0])
    assert np.allclose(mod[1, 0, 2], [1.0, 9.0, 10.0])
    assert np.allclose(mod[1, 0, 3], [1.0, 12.0, 13.0])
def test_normfactor(backend):
    mc = MockConfig(
        par_map={
            'mu1': {
                'paramset':
                unconstrained(
                    name='mu1',
                    is_scalar=True,
                    n_parameters=1,
                    inits=[0],
                    bounds=[[0, 10]],
                    fixed=False,
                ),
                'slice':
                slice(0, 1),
            },
            'mu2': {
                'paramset':
                unconstrained(
                    name='mu2',
                    is_scalar=True,
                    n_parameters=1,
                    inits=[0],
                    bounds=[[0, 10]],
                    fixed=False,
                ),
                'slice':
                slice(1, 2),
            },
        },
        par_order=['mu1', 'mu2'],
        samples=['signal', 'background'],
    )

    mega_mods = {
        'normfactor/mu1': {
            'signal': {
                'type': 'normfactor',
                'name': 'mu1',
                'data': {
                    'mask': [True, False, False]
                },
            },
            'background': {
                'type': 'normfactor',
                'name': 'mu1',
                'data': {
                    'mask': [True, False, False]
                },
            },
        },
        'normfactor/mu2': {
            'signal': {
                'type': 'normfactor',
                'name': 'mu2',
                'data': {
                    'mask': [False, True, True]
                },
            },
            'background': {
                'type': 'normfactor',
                'name': 'mu2',
                'data': {
                    'mask': [False, True, True]
                },
            },
        },
    }
    hsc = normfactor_combined([('mu1', 'normfactor'), ('mu2', 'normfactor')],
                              mc, mega_mods)

    mod = hsc.apply(pyhf.tensorlib.astensor([2.0, 3.0]))
    shape = pyhf.tensorlib.shape(mod)
    assert shape == (2, 2, 1, 3)

    mod = np.asarray(pyhf.tensorlib.tolist(mod))
    assert np.allclose(mod[0, 0, 0], [2.0, 1.0, 1.0])
    assert np.allclose(mod[1, 0, 0], [1.0, 3.0, 3.0])

    hsc = normfactor_combined([('mu1', 'normfactor'), ('mu2', 'normfactor')],
                              mc,
                              mega_mods,
                              batch_size=4)

    mod = hsc.apply(
        pyhf.tensorlib.astensor([[1.0, 5.0], [2.0, 6.0], [3.0, 7.0],
                                 [4.0, 8.0]]))
    shape = pyhf.tensorlib.shape(mod)
    assert shape == (2, 2, 4, 3)

    mod = np.asarray(pyhf.tensorlib.tolist(mod))
    assert np.allclose(mod[0, 0, 0], [1.0, 1.0, 1.0])
    assert np.allclose(mod[0, 0, 1], [2.0, 1.0, 1.0])
    assert np.allclose(mod[0, 0, 2], [3.0, 1.0, 1.0])
    assert np.allclose(mod[0, 0, 3], [4.0, 1.0, 1.0])

    assert np.allclose(mod[1, 0, 0], [1.0, 5.0, 5.0])
    assert np.allclose(mod[1, 0, 1], [1.0, 6.0, 6.0])
    assert np.allclose(mod[1, 0, 2], [1.0, 7.0, 7.0])
    assert np.allclose(mod[1, 0, 3], [1.0, 8.0, 8.0])