def execute_in_bicases_constraints(self, task):
        # adapt beam width to the result set size if desired
        if self.beamWidthAdaptive:
            self.beamWidth = task.resultSetSize[0]

        # check if beam size is to small for result set
        if (self.beamWidth < task.resultSetSize[0]):
            raise RuntimeError(
                'Beam width in the beam search algorithm is smaller than the result set size!'
            )

        # init
        beam = [(0, Subgroup(task.target, []), Subgroup(task.target, []))]
        beam2 = [(0, Subgroup(task.target, []))]

        last_beam = None

        depth1 = 0
        depth2 = 0

        while beam != last_beam and depth1 < task.depth:

            last_beam = beam.copy()
            last_candidates = [pattern[1] for pattern in last_beam]
            all_sg1_candidates = last_candidates

            # a list contains all sg1 without duplicates
            all_sg1_candidates = [
                i for n, i in enumerate(last_candidates)
                if i not in last_candidates[:n]
            ]

            # print(all_sg1_candidates)
            for last_sg1 in all_sg1_candidates:

                for sel1 in task.searchSpace:

                    new_selectors1 = list(
                        last_sg1.subgroupDescription.selectors)

                    if not sel1 in new_selectors1:
                        new_selectors1.append(sel1)
                        sg1 = Subgroup(task.target, new_selectors1)
                        # print('sg1')
                        # print(sg1)

                        if sg1.count(task.data) != 0:

                            beam2 = [(0, Subgroup(task.target, []))]
                            depth2 = 0
                            last_beam2 = None

                            searchSpace_sg2_1 = []
                            ignore_attrs = []

                            for sel in new_selectors1:
                                ignore_attr_name = sel.getAttributeName()
                                searchSpace_sg2_1.extend(createNominalSelectorsForAttribute(task.data, \
                                ignore_attr_name))
                                # searchSpace_sg2_1.extend(createNumericSelectorForAttribute(task.data, \
                                # ignore_attr_name))

                                ignore_attrs.append(ignore_attr_name)

                                searchSpace_sg2_1.remove(sel)

                            searchSpace_sg2_left = [
                                i for i in task.searchSpace
                                if i.getAttributeName() not in ignore_attrs
                            ]

                            map_searchSpace_sg2 = {}
                            map_searchSpace_sg2[0] = searchSpace_sg2_1

                            for i in range(1, task.depth):
                                map_searchSpace_sg2[i] = searchSpace_sg2_left

                            # print(map_searchSpace_sg2)

                            while beam2 != last_beam2 and depth2 < task.depth:
                                last_beam2 = beam2.copy()

                                for (_, last_sg2) in last_beam2:

                                    if depth2 == 0 or list(
                                            last_sg2.subgroupDescription.
                                            selectors) != []:
                                        # print(last_sg2)

                                        for sel2 in map_searchSpace_sg2[
                                                depth2]:

                                            new_selectors2 = list(
                                                last_sg2.subgroupDescription.
                                                selectors)
                                            new_selectors2.append(sel2)

                                            # if new_selectors2 != new_selectors1:
                                            sg2 = Subgroup(
                                                task.target, new_selectors2)
                                            quality = task.qf.evaluateFromDataset_BiCases(
                                                task.data, sg1, sg2)
                                            # print(quality)
                                            ut.addIfRequired(
                                                beam2,
                                                sg2,
                                                quality,
                                                task,
                                                check_for_duplicates=True)

                                # print(beam2)

                                depth2 += 1

                            beam2.sort(key=lambda x: x[0], reverse=True)
                            # print(beam2)

                            for i in range(len(beam2)):
                                ut.addIfRequired_Bicases(
                                    beam,
                                    sg1,
                                    beam2[i][-1],
                                    beam2[i][0],
                                    task,
                                    check_for_duplicates=True)

                            # print('here')
                            # for i in range(len(beam)):
                            #     print(beam[i][1])

            depth1 += 1

        result = beam[:]
        result.sort(key=lambda x: x[0], reverse=True)
        return result
    def execute_in_bicases(self, task):
        # adapt beam width to the result set size if desired
        if self.beamWidthAdaptive:
            self.beamWidth = task.resultSetSize[0]

        # check if beam size is to small for result set
        if (self.beamWidth < task.resultSetSize[0]):
            raise RuntimeError(
                'Beam width in the beam search algorithm is smaller than the result set size!'
            )

        # init
        beam = [(0, Subgroup(task.target, []), Subgroup(task.target, []))]
        beam2 = [(0, Subgroup(task.target, []))]

        last_beam = None

        depth1 = 0
        depth2 = 0

        while beam != last_beam and depth1 < task.depth:

            last_beam = beam.copy()
            last_candidates = [pattern[1] for pattern in last_beam]
            all_sg1_candidates = last_candidates

            # a list contains all sg1 without duplicates
            all_sg1_candidates = [
                i for n, i in enumerate(last_candidates)
                if i not in last_candidates[:n]
            ]

            # print(all_sg1_candidates)

            for last_sg1 in all_sg1_candidates:

                for sel1 in task.searchSpace:
                    new_selectors1 = list(
                        last_sg1.subgroupDescription.selectors)

                    if not sel1 in new_selectors1:
                        new_selectors1.append(sel1)
                        sg1 = Subgroup(task.target, new_selectors1)
                        # print('sg1')
                        # print(sg1)

                        if sg1.count(task.data) != 0:

                            beam2 = [(0, Subgroup(task.target, []))]
                            depth2 = 0
                            last_beam2 = None
                            searchSpace_sg2 = [
                                i for i in task.searchSpace
                                if i not in new_selectors1
                            ]

                            while beam2 != last_beam2 and depth2 < task.depth:
                                last_beam2 = beam2.copy()

                                for (_, last_sg2) in last_beam2:

                                    for sel2 in searchSpace_sg2:

                                        new_selectors2 = list(
                                            last_sg2.subgroupDescription.
                                            selectors)
                                        if not sel2 in new_selectors2:

                                            new_selectors2.append(sel2)

                                            # if new_selectors2 != new_selectors1:
                                            sg2 = Subgroup(
                                                task.target, new_selectors2)

                                            quality = task.qf.evaluateFromDataset_BiCases(
                                                task.data, sg1, sg2)
                                            ut.addIfRequired(
                                                beam2,
                                                sg2,
                                                quality,
                                                task,
                                                check_for_duplicates=True)

                                depth2 += 1

                            beam2.sort(key=lambda x: x[0], reverse=True)
                            # print(beam2)

                            for i in range(len(beam2)):
                                ut.addIfRequired_Bicases(
                                    beam,
                                    sg1,
                                    beam2[i][-1],
                                    beam2[i][0],
                                    task,
                                    check_for_duplicates=True)

                            # print('here')
                            # for i in range(len(beam)):
                            #     print(beam[i][1])

            depth1 += 1

        result = beam[:]
        result.sort(key=lambda x: x[0], reverse=True)
        return result