Example #1
0
def check_values(_board):
    """Look for values where only one is missing.  If there is only one missing, then there is
    only one cell where adding the value would not cause a duplicate in a row or column.  Fill
    in those cells if they exist."""
    if verbose:
        print("* check_values")
    board = get_filled_cells(_board)

    new_cells = Set()
    value_clans = partition.partition(board, partial(by_key, 'value'))
    for value_clan in _sort(value_clans, key=partial(by_clan_key, 'value')):
        # If there is only 1 missing value..fill in the cell
        if value_clan.cardinality == GRID_SIZE-1:
            # Get the set of rows and cols containing value
            occupied_rows = project(value_clan, 'row')
            occupied_cols = project(value_clan, 'col')
            # Get the entire set of rows and cols based on the occupied rows and cols
            occupied = clans.superstrict(_board, sets.union(occupied_rows, occupied_cols))
            # Remove all occupied rows to get the only candidate row_col left
            row_col = sets.minus(_board, occupied)
            value = project(value_clan, 'value')
            new_cells = sets.union(new_cells, clans.cross_union(row_col, value))
    if new_cells:
        return get_new_board(_board, new_cells)
    return _board
Example #2
0
def check_values(_board):
    """Look for values where only one is missing.  If there is only one missing, then there is
    only one cell where adding the value would not cause a duplicate in a row or column.  Fill
    in those cells if they exist."""
    if VERBOSE:
        print("* check_values")
    board = get_filled_cells(_board)

    new_cells = Set()
    value_clans = partition.partition(board, partial(by_key, 'value'))
    for value_clan in _SORT(value_clans, key=partial(by_clan_key, 'value')):
        # If there is only 1 missing value..fill in the cell
        if value_clan.cardinality == GRID_SIZE - 1:
            # Get the set of rows and cols containing value
            occupied_rows = project(value_clan, 'row')
            occupied_cols = project(value_clan, 'col')
            # Get the entire set of rows and cols based on the occupied rows and cols
            occupied = clans.superstrict(_board, sets.union(occupied_rows, occupied_cols))
            # Remove all occupied rows to get the only candidate row_col left
            row_col = sets.minus(_board, occupied)
            value = project(value_clan, 'value')
            new_cells = sets.union(new_cells, clans.cross_union(row_col, value))
    if new_cells:
        return get_new_board(_board, new_cells)
    return _board
    def test_lhs_cross_functional_union(self):
        """Test for functional_cross_union."""
        table_a = import_csv(self._get_table_a())
        table_b = import_csv(self._get_table_b())

        self.assertTrue(is_functional(table_a))
        self.assertTrue(is_functional(table_b))

        # Calculate left join.
        result = lhs_cross_functional_union(table_a, table_b)

        # Test result set properties
        self.assertEqual(result.cached_functional, CacheStatus.IS)
        self.assertFalse(result.is_empty)
        self.assertEqual(result.cardinality, 8)
        expected = import_csv(self._get_result_cross_functional_union())
        self.assertEqual(result, expected)

        import algebraixlib.algebras.sets as sets
        table_aa = sets.union(table_a, Set(Set(Couplet('PK', '-1'), Couplet('PK', '-2'))))
        self.assertFalse(is_functional(table_aa))
        result = lhs_cross_functional_union(table_aa, table_b)
        self.assertNotEqual(result.cached_functional, CacheStatus.IS)

        table_bb = sets.union(table_b, Set(Set(Couplet('PK', '-1'), Couplet('PK', '-2'))))
        self.assertFalse(is_functional(table_bb))
        result = lhs_cross_functional_union(table_a, table_bb)
        self.assertEqual(result.cached_functional, CacheStatus.IS)
    def test_lhs_cross_functional_union(self):
        """Test for functional_cross_union."""
        table_a = import_csv(self._get_table_a())
        table_b = import_csv(self._get_table_b())

        self.assertTrue(is_functional(table_a))
        self.assertTrue(is_functional(table_b))

        # Calculate left join.
        result = lhs_cross_functional_union(table_a, table_b)

        # Test result set properties
        self.assertEqual(result.cached_functional, CacheStatus.IS)
        self.assertFalse(result.is_empty)
        self.assertEqual(result.cardinality, 8)
        expected = import_csv(self._get_result_cross_functional_union())
        self.assertEqual(result, expected)

        if self.print_examples:
            print('Expected set: {0}'.format(expected))
            print('Result set: {0}'.format(result))

        import algebraixlib.algebras.sets as sets
        table_aa = sets.union(table_a, Set(Set(Couplet('PK', '-1'), Couplet('PK', '-2'))))
        self.assertFalse(is_functional(table_aa))
        result = lhs_cross_functional_union(table_aa, table_b)
        self.assertNotEqual(result.cached_functional, CacheStatus.IS)

        table_bb = sets.union(table_b, Set(Set(Couplet('PK', '-1'), Couplet('PK', '-2'))))
        self.assertFalse(is_functional(table_bb))
        result = lhs_cross_functional_union(table_a, table_bb)
        self.assertEqual(result.cached_functional, CacheStatus.IS)
 def test_union(self):
     rel1 = Set(Couplet('a', 1), Couplet('b', 2)).cache_is_relation(True)
     rel2 = Set(Couplet('c', 3))
     self.assertFalse(sets.union(rel1, rel2).cached_is_relation)
     self.assertFalse(sets.union(rel1, rel2).cached_is_not_relation)
     self.assertTrue(is_member(rel2))
     self.assertTrue(sets.union(rel1, rel2).cached_is_relation)
     self.assertFalse(sets.union(rel1, rel2).cached_is_not_relation)
    def test_functional_union(self):
        """Test relations.right_functional_union() which produces the union of two
        relations where the result must be functional."""
        self._check_wrong_argument_types_binary(functional_union)

        # Union of functional relations is the same as sets.union()
        result = functional_union(ar['rel1'], ar['rel2'])
        self.assertEqual(result, sets.union(ar['rel1'], ar['rel2']))

        # Union of non-functional relations is NOT the same as sets.union()
        result = functional_union(ar['rel1'], ar['reldiag'])
        self.assertIs(result, Undef())
        self.assertIsNot(result, sets.union(ar['rel1'], ar['reldiag']))
Example #7
0
def get_lefts(clan: 'PP(M x M)', _checked=True) -> 'P( M )':
    r"""Return the set of the left components of all couplets in all relations in ``clan``.

    :return: The :term:`union` of the :term:`left set`\s of all :term:`relation`\s in ``clan`` or
        `Undef()` if ``clan`` is not a :term:`clan`.
    """
    if _checked:
        if not is_member(clan):
            return _undef.make_or_raise_undef2(clan)
    else:
        assert is_member_or_undef(clan)
        if clan is _undef.Undef():
            return _undef.make_or_raise_undef(2)
    if clan.is_empty:
        # The left set of an empty set is the empty set
        return clan
    clan_itr = iter(clan)
    left_set = _relations.get_lefts(next(clan_itr), _checked=False)
    for rel in clan_itr:
        left_set = _sets.union(_relations.get_lefts(rel, _checked=False),
                               left_set,
                               _checked=False)
    if not left_set.is_empty:
        if clan.cached_is_absolute:
            left_set.cache_absolute(CacheStatus.IS)
    return left_set
Example #8
0
    def lhs_cross_functional_union(lhs: 'PP( MxM )', rhs: 'PP( MxM )',
                                   _checked=True) -> 'PP(M x M)':
        """Return the :term:`lhs-cross-functional union` ('left join') of ``lhs`` and ``rhs``.

        This operation results in a :term:`clan` that contains every :term:`relation` of a
        :term:`cross-functional union`, but also contains all relations in ``lhs`` that
        are not already part of one of the resulting relations.

        :param lhs: All relations in this clan are guaranteed to be represented in the result.
        :return: The resulting clan or `Undef()` if ``lhs`` or ``rhs`` are not clans.
        """
        if _checked:
            if not is_member(lhs):
                return _undef.make_or_raise_undef()
            if not is_member(rhs):
                return _undef.make_or_raise_undef()
        else:
            assert is_member(lhs)
            assert is_member(rhs)

        cfu = cross_functional_union(lhs, rhs, _checked=False)
        lhs_rest = _mo.Set(
            lhs_elem for lhs_elem in lhs
            if cross_functional_union(_mo.Set(lhs_elem, direct_load=True), rhs).is_empty)
        result = _sets.union(cfu, lhs_rest, _checked=False)
        if not result.is_empty:
            result.cache_clan(_mo.CacheStatus.IS)
            if lhs.cached_is_functional:
                result.cache_functional(_mo.CacheStatus.IS)
            if lhs.cached_is_not_right_functional or rhs.cached_is_not_right_functional:
                result.cache_right_functional(_mo.CacheStatus.IS_NOT)
            if not rhs.is_empty and not lhs_rest.is_empty:
                result.cache_regular(_mo.CacheStatus.IS_NOT)
        return result
Example #9
0
def get_rights(mclan: 'P(P(M x M) x N)', _checked=True) -> 'P( M )':
    r"""Return the set of the right components of all couplets in all relations in ``mclan``.

    :return: The :term:`union` of the :term:`right set`\s of all :term:`relation`\s in ``mclan`` or
        `Undef()` if ``mclan`` is not a :term:`multiclan`.
    """
    if _checked:
        if not is_member(mclan):
            return _undef.make_or_raise_undef2(mclan)
    else:
        assert is_member_or_undef(mclan)
        if mclan is _undef.Undef():
            return _undef.make_or_raise_undef(2)
    if mclan.is_empty:
        # The right set of an empty set is the empty set
        return mclan
    clan_itr = iter(mclan)
    right_set = _relations.get_rights(next(clan_itr), _checked=False)
    for rel in clan_itr:
        right_set = _sets.union(_relations.get_rights(rel, _checked=False),
                                right_set,
                                _checked=False)
    if not right_set.is_empty:
        if mclan.cached_is_absolute:
            right_set.cache_absolute(_mo.CacheStatus.IS)
    return right_set
Example #10
0
    def right_functional_union(rel1: 'P(M x M)',
                               rel2: 'P(M x M)',
                               _checked=True) -> 'P(M x M)':
        r"""Return the union of ``rel1`` and ``rel2`` if it is right-functional, otherwise
        `Undef()`.

        :return: The :term:`right-functional union` of the :term:`relation`\s ``rel1`` and
            ``rel2``; that is, the :term:`union` if the result is :term:`right-functional`,
            otherwise `Undef()`. Also return `Undef()` if ``rel1`` or ``rel2`` are not relations.
        """
        if _checked:
            if not is_member(rel1):
                return _undef.make_or_raise_undef2(rel1)
            if not is_member(rel2):
                return _undef.make_or_raise_undef2(rel2)
        else:
            assert is_member_or_undef(rel1)
            assert is_member_or_undef(rel2)
            if rel1 is _undef.Undef() or rel2 is _undef.Undef():
                return _undef.make_or_raise_undef(2)
        rel_union = _sets.union(rel1, rel2, _checked=False).cache_relation(
            _mo.CacheStatus.IS)
        if not is_right_functional(rel_union, _checked=False):
            return _undef.make_or_raise_undef(2)
        return rel_union
Example #11
0
def get_rights(mclan: 'P(P(M x M) x N)', _checked=True) -> 'P( M )':
    r"""Return the set of the right components of all couplets in all relations in ``mclan``.

    :return: The :term:`union` of the :term:`right set`\s of all :term:`relation`\s in ``mclan`` or
        `Undef()` if ``mclan`` is not a :term:`multiclan`.
    """
    if _checked:
        if not is_member(mclan):
            return _undef.make_or_raise_undef2(mclan)
    else:
        assert is_member_or_undef(mclan)
        if mclan is _undef.Undef():
            return _undef.make_or_raise_undef(2)
    if mclan.is_empty:
        # The right set of an empty set is the empty set
        return mclan
    clan_itr = iter(mclan)
    right_set = _relations.get_rights(next(clan_itr), _checked=False)
    for rel in clan_itr:
        right_set = _sets.union(
            _relations.get_rights(rel, _checked=False), right_set, _checked=False)
    if not right_set.is_empty:
        if mclan.cached_is_absolute:
            right_set.cache_absolute(_mo.CacheStatus.IS)
    return right_set
Example #12
0
def fill_lefts(rel: 'P(M x M)',
               renames: 'P(M x M)',
               _checked=True) -> 'P(M x M)':
    r"""Return the left components in ``rel`` that are missing in ``renames`` as a diagonal
    unioned with ``renames``.

    The purpose is to create a :term:`relation` that can be used with the :term:`composition`
    operation to change (rename) one or more :term:`left component`\s and leave the rest alone.

    :param rel: The :term:`relation` that provides the full :term:`left set`.
    :param renames: A relation where the :term:`right component`\s are meant to be
        :term:`composition` 'origins' and the :term:`left component`\s composition 'targets'.
    :return: A relation that contains all members of ``renames`` unioned with a :term:`diagonal`
        that consists of all left components in ``rel`` that are missing in ``renames``.
    """
    if _checked:
        if not is_member(rel):
            return _undef.make_or_raise_undef2(rel)
        if not is_member(renames):
            return _undef.make_or_raise_undef2(renames)
    else:
        assert is_member_or_undef(rel)
        assert is_member_or_undef(renames)
        if rel is _undef.Undef() or renames is _undef.Undef():
            return _undef.make_or_raise_undef(2)
    missing_lefts = _sets.minus(get_lefts(rel, _checked=False),
                                get_rights(renames, _checked=False),
                                _checked=False)
    diag_missing_lefts = diag(*missing_lefts, _checked=False)
    result = _sets.union(renames, diag_missing_lefts, _checked=False)
    assert result.cached_is_relation
    return result
Example #13
0
    def lhs_functional_cross_union(lhs: 'PP( MxM )', rhs: 'PP( MxM )', _checked=True):
        """This data manipulations preforms a left functional cross union, then unions the left hand
        side elements that were not cross unioned.

        :param lhs: The priority left hand clan for this operation
        :param rhs: The right hand clan to preform the cross union with
        :return: PP(MxM) resulting math object
        """
        if _checked:
            if not is_member(lhs):
                return _make_or_raise_undef()
            if not is_member(rhs):
                return _make_or_raise_undef()
        else:
            assert is_member(lhs)
            assert is_member(rhs)

        def _functional_cross_union(clan1: 'PP(M x M)', clan2: 'PP(M x M)') -> 'PP(M x M)':
            """Return the :term:`left-functional cross-union` of ``clan1`` and ``clan2``."""
            assert is_member(clan1)
            assert is_member(clan2)

            return _extension.binary_extend(
                clan1, clan2, partial(_relations.functional_union, _checked=False),
                _checked=False).cache_is_clan(True)

        return _sets.union(
            _functional_cross_union(lhs, rhs),
            _mo.Set(lhs_elem for lhs_elem in lhs if _functional_cross_union(
                _mo.Set(lhs_elem, direct_load=True), rhs).is_empty), _checked=False)
Example #14
0
def fill_lefts(rel: 'P(M x M)', renames: 'P(M x M)', _checked=True) -> 'P(M x M)':
    r"""Return the left components in ``rel`` that are missing in ``renames`` as a diagonal
    unioned with ``renames``.

    The purpose is to create a :term:`relation` that can be used with the :term:`composition`
    operation to change (rename) one or more :term:`left component`\s and leave the rest alone.

    :param rel: The :term:`relation` that provides the full :term:`left set`.
    :param renames: A relation where the :term:`right component`\s are meant to be
        :term:`composition` 'origins' and the :term:`left component`\s composition 'targets'.
    :return: A relation that contains all members of ``renames`` unioned with a :term:`diagonal`
        that consists of all left components in ``rel`` that are missing in ``renames``.
    """
    if _checked:
        if not is_member(rel):
            return _undef.make_or_raise_undef2(rel)
        if not is_member(renames):
            return _undef.make_or_raise_undef2(renames)
    else:
        assert is_member_or_undef(rel)
        assert is_member_or_undef(renames)
        if rel is _undef.Undef() or renames is _undef.Undef():
            return _undef.make_or_raise_undef(2)
    missing_lefts = _sets.minus(get_lefts(rel, _checked=False),
                                get_rights(renames, _checked=False), _checked=False)
    diag_missing_lefts = diag(*missing_lefts, _checked=False)
    result = _sets.union(renames, diag_missing_lefts, _checked=False)
    assert result.cached_is_relation
    return result
    def test_union(self):
        self._check_wrong_argument_types_binary(union)

        result = union(_set1, _set2)
        self.assertEqual(result, _set1u2)
        abc_ab_ac = Set([Set('a', 'b', 'c'), Set('a', 'b'), Set('a', 'c')])
        cu = _extension.binary_extend(_ab_c, _ac_a, union)
        self.assertEqual(cu, abc_ab_ac)
Example #16
0
    def test_union(self):
        self._check_wrong_argument_types_binary(union)

        result = union(_set1, _set2)
        self.assertEqual(result, _set1u2)
        abc_ab_ac = Set(Set('a', 'b', 'c'), Set('a', 'b'), Set('a', 'c'))
        cu = _extension.binary_extend(_ab_c, _ac_a, union)
        self.assertEqual(cu, abc_ab_ac)
Example #17
0
    def test_functional_union(self):
        """Test relations.right_functional_union() which produces the union of two
        relations where the result must be functional."""
        self._check_wrong_argument_types_binary(functional_union)

        rel1 = ar['rel1']
        # Union of functional relations is the same as sets.union()
        result = functional_union(rel1, ar['rel2'])
        self.assertEqual(result, sets.union(rel1, ar['rel2']))

        # Union of non-functional relations is NOT the same as sets.union()
        result = functional_union(rel1, ar['reldiag'])
        self.assertIs(result, Undef())
        self.assertIsNot(result, sets.union(rel1, ar['reldiag']))

        self.assertIs(functional_union(rel1, Undef()), Undef())
        self.assertIs(functional_union(Undef(), rel1), Undef())
        self.assertIs(functional_union(rel1, Undef(), _checked=False), Undef())
        self.assertIs(functional_union(Undef(), rel1, _checked=False), Undef())
    def test_right_functional_union(self):
        """Test relations.right_functional_union() which produces the union of two
        relations where the result must be right functional."""
        self._check_wrong_argument_types_binary(right_functional_union)

        rel1 = ar['rel1']
        # Union of right functional relations is the same as sets.union()
        result = right_functional_union(rel1, ar['rel2'])
        self.assertEqual(result, sets.union(rel1, ar['rel2']))

        # Union of non-right functional relations is NOT the same as sets.union()
        result = right_functional_union(rel1, ar['reldiag'])
        self.assertIs(result, Undef())
        self.assertIsNot(result, sets.union(rel1, ar['reldiag']))

        self.assertIs(right_functional_union(rel1, Undef()), Undef())
        self.assertIs(right_functional_union(Undef(), rel1), Undef())
        self.assertIs(right_functional_union(rel1, Undef(), _checked=False), Undef())
        self.assertIs(right_functional_union(Undef(), rel1, _checked=False), Undef())
Example #19
0
def check_blocks(_board):
    """Check each block.  If there is only one value missing..."""
    if VERBOSE:
        print("* check_blocks")

    board = get_filled_cells(_board)
    blocks = partition.partition(board, partial(by_keys, 'band', 'stack'))
    for block_clan in _SORT(blocks, key=partial(by_clan_keys, 'band', 'stack')):
        new_possible, conflict = get_block_candidates(block_clan, board)

        if new_possible.is_empty:
            continue
        if new_possible.cardinality == 1:
            _board = get_new_board(_board, new_possible)
            continue

        if block_clan.cardinality == GRID_SIZE - 2:
            # Knowing that the value in conflict can't be placed in the conflict cell
            # ..it must go in the other...
            first_choice = clans.superstrict(new_possible, project(conflict, 'value'))
            if first_choice.cardinality == 2:
                # place both values
                _board = get_new_board(_board, first_choice)
                continue

            # Remove the first choice for all_possible
            remaining_possible = sets.minus(new_possible, first_choice)

            # Knowing that first_choice goes in a row/col, remove other value from that cell
            first_rowcol = project(first_choice, 'row', 'col')

            # The remaining cell is the second choice
            second_choice = sets.minus(remaining_possible,
                                       clans.superstrict(remaining_possible, first_rowcol))

            new_cells = sets.union(first_choice, second_choice)
            _board = get_new_board(_board, new_cells)
            continue

        # Partition by value
        candidates = partition.partition(new_possible, partial(by_key, 'value'))
        for candidate in _SORT(candidates, key=partial(by_clan_key, 'value')):
            # If any value fits in only 1 cell, place it
            if candidate.cardinality == 1:
                # Remove band/stack
                new_cell = project(candidate, 'row', 'col', 'value')
                _board = get_new_board(_board, new_cell)

    return _board
Example #20
0
def check_blocks(_board):
    """Check each block.  If there is only one value missing..."""
    if VERBOSE:
        print("* check_blocks")

    board = get_filled_cells(_board)
    blocks = partition.partition(board, partial(by_keys, 'band', 'stack'))
    for block_clan in _SORT(blocks, key=partial(by_clan_keys, 'band', 'stack')):
        new_possible, conflict = get_block_candidates(block_clan, board)

        if new_possible.is_empty:
            continue
        if new_possible.cardinality == 1:
            _board = get_new_board(_board, new_possible)
            continue

        if block_clan.cardinality == GRID_SIZE - 2:
            # Knowing that the value in conflict can't be placed in the conflict cell
            # ..it must go in the other...
            first_choice = clans.superstrict(new_possible, project(conflict, 'value'))
            if first_choice.cardinality == 2:
                # place both values
                _board = get_new_board(_board, first_choice)
                continue

            # Remove the first choice for all_possible
            remaining_possible = sets.minus(new_possible, first_choice)

            # Knowing that first_choice goes in a row/col, remove other value from that cell
            first_rowcol = project(first_choice, 'row', 'col')

            # The remaining cell is the second choice
            second_choice = sets.minus(remaining_possible,
                                       clans.superstrict(remaining_possible, first_rowcol))

            new_cells = sets.union(first_choice, second_choice)
            _board = get_new_board(_board, new_cells)
            continue

        # Partition by value
        candidates = partition.partition(new_possible, partial(by_key, 'value'))
        for candidate in _SORT(candidates, key=partial(by_clan_key, 'value')):
            # If any value fits in only 1 cell, place it
            if candidate.cardinality == 1:
                # Remove band/stack
                new_cell = project(candidate, 'row', 'col', 'value')
                _board = get_new_board(_board, new_cell)

    return _board
Example #21
0
def get_block_candidates(block_clan, board):
    # Get the set of missing values...see if any can be placed due to row/col information
    values_clan = get_missing_values(block_clan)

    # Get the set of missing values...see if any can be placed due to row/col information
    target_rowcols = get_missing_rowcols(block_clan)

    if block_clan.cardinality == GRID_SIZE - 1:
        new_cells = clans.cross_union(target_rowcols, values_clan)
        return new_cells, Set()

    # Need cross union values with rows
    rows_clan = project(target_rowcols, 'row')
    cols_clan = project(target_rowcols, 'col')
    possible_rows_values = clans.cross_union(values_clan, rows_clan)
    possible_cols_values = clans.cross_union(values_clan, cols_clan)

    possible_rows_cols_values = sets.union(possible_rows_values, possible_cols_values)

    # The occupied_clan is the row/col/value set that is a conflict for values
    occupied_clan = clans.superstrict(board, possible_rows_cols_values)

    # If there are no conflicts then no cells can be placed
    if occupied_clan.is_empty:
        return Set(), Set()

    all_possible = clans.cross_union(values_clan, target_rowcols).cache_functional(CacheStatus.IS)

    # Get the set of conflicts...conflicting row/value + col/value
    conflict = sets.union(
        clans.superstrict(all_possible, project(occupied_clan, 'value', 'col')),
        clans.superstrict(all_possible, project(occupied_clan, 'value', 'row')))

    # Remove the conflicts from all_possible
    new_possible = sets.minus(all_possible, conflict)
    return new_possible, conflict
Example #22
0
def get_block_candidates(block_clan, board):
    # Get the set of missing values...see if any can be placed due to row/col information
    values_clan = get_missing_values(block_clan)

    # Get the set of missing values...see if any can be placed due to row/col information
    target_rowcols = get_missing_rowcols(block_clan)

    if block_clan.cardinality == GRID_SIZE - 1:
        new_cells = clans.cross_union(target_rowcols, values_clan)
        return new_cells, Set()

    # Need cross union values with rows
    rows_clan = project(target_rowcols, 'row')
    cols_clan = project(target_rowcols, 'col')
    possible_rows_values = clans.cross_union(values_clan, rows_clan)
    possible_cols_values = clans.cross_union(values_clan, cols_clan)

    possible_rows_cols_values = sets.union(possible_rows_values, possible_cols_values)

    # The occupied_clan is the row/col/value set that is a conflict for values
    occupied_clan = clans.superstrict(board, possible_rows_cols_values)

    # If there are no conflicts then no cells can be placed
    if occupied_clan.is_empty:
        return Set(), Set()

    all_possible = clans.cross_union(values_clan, target_rowcols).cache_functional(CacheStatus.IS)

    # Get the set of conflicts...conflicting row/value + col/value
    conflict = sets.union(
        clans.superstrict(all_possible, project(occupied_clan, 'value', 'col')),
        clans.superstrict(all_possible, project(occupied_clan, 'value', 'row')))

    # Remove the conflicts from all_possible
    new_possible = sets.minus(all_possible, conflict)
    return new_possible, conflict
    def test_union_flags(self):
        s1 = Set(1)
        s2 = Set(2)
        s3 = Set(1, 2)
        self.assertTrue(is_absolute_member(s1))
        self.assertTrue(is_absolute_member(s2))
        self.assertTrue(is_absolute_member(s3))
        result = union(s1, s2)
        self.assertEqual(s3, result)
        self.assertEqual(result.cached_is_absolute, CacheStatus.IS)

        from algebraixlib.algebras import relations as _rels

        rel1 = Set(Couplet('a', 1))
        rel2 = Set(Couplet('b', 2))
        rel3 = Set(Couplet('a', 1), Couplet('b', 2))
        self.assertTrue(_rels.is_absolute_member(rel1))
        self.assertTrue(_rels.is_absolute_member(rel2))
        result = union(rel1, rel2)
        self.assertEqual(rel3, result)
        self.assertEqual(result.cached_is_absolute, CacheStatus.IS)

        from algebraixlib.algebras import clans as _clans

        clan1 = Set(rel1)
        clan2 = Set(rel2)
        clan3 = Set(rel1, rel2)
        self.assertTrue(_clans.is_absolute_member(clan1))
        self.assertTrue(_clans.is_absolute_member(clan2))
        self.assertTrue(_clans.is_functional(clan1))
        self.assertTrue(_clans.is_functional(clan2))
        self.assertTrue(_clans.is_right_functional(clan1))
        self.assertTrue(_clans.is_right_functional(clan2))
        result = union(clan1, clan2)
        self.assertEqual(clan3, result)
        self.assertEqual(result.cached_absolute, CacheStatus.IS)
        self.assertEqual(result.cached_functional, CacheStatus.IS)
        self.assertEqual(result.cached_right_functional, CacheStatus.IS)

        clan4 = Set(Set(Couplet(s1, 1), Couplet(s1, 2)))
        clan5 = Set(rel1, Set(Couplet(s1, 1), Couplet(s1, 2)))
        self.assertFalse(_clans.is_absolute_member(clan4))
        self.assertFalse(_clans.is_functional(clan4))
        result = union(clan1, clan4)
        self.assertEqual(clan5, result)
        self.assertEqual(result.cached_absolute, CacheStatus.IS_NOT)
        self.assertEqual(result.cached_functional, CacheStatus.IS_NOT)

        rel1 = Set(Couplet('a', 1), Couplet('b',
                                            2)).cache_relation(CacheStatus.IS)
        rel2 = Set(Couplet('c', 3))
        self.assertEqual(
            union(rel1, rel2).cached_relation, CacheStatus.UNKNOWN)
        self.assertTrue(_rels.is_member(rel2))
        self.assertEqual(union(rel1, rel2).cached_relation, CacheStatus.IS)
Example #24
0
def functional_add(rel: 'P(M x M)', element: 'M x M') -> 'P(M x M)':
    """Add ``element`` to ``rel`` and return the new :term:`relation`.

    :param rel: The source data. Must be a :term:`relation`. It must not contain a :term:`couplet`
        with the same :term:`left component` as ``element``.
    :param element: The element to be added to ``rel``. Must be a :class:`~.Couplet` and its
        :term:`left component` must not be a left component in ``rel``.
    :return: The new relation, composed of ``rel`` and ``element``.
    """
    if not is_member(rel):
        return _make_or_raise_undef()
    if not isinstance(element, _mo.Couplet):
        return _make_or_raise_undef()
    if _sets.is_subset_of(_mo.Set(element.left), get_lefts(rel)):
        return _make_or_raise_undef(2)
    result_relation = _sets.union(rel, _mo.Set(element))
    return result_relation
    def test_union_flags(self):
        s1 = Set(1)
        s2 = Set(2)
        s3 = Set(1, 2)
        self.assertTrue(is_absolute_member(s1))
        self.assertTrue(is_absolute_member(s2))
        self.assertTrue(is_absolute_member(s3))
        result = union(s1, s2)
        self.assertEqual(s3, result)
        self.assertEqual(result.cached_is_absolute, CacheStatus.IS)

        from algebraixlib.algebras import relations as _rels

        rel1 = Set(Couplet('a', 1))
        rel2 = Set(Couplet('b', 2))
        rel3 = Set(Couplet('a', 1), Couplet('b', 2))
        self.assertTrue(_rels.is_absolute_member(rel1))
        self.assertTrue(_rels.is_absolute_member(rel2))
        result = union(rel1, rel2)
        self.assertEqual(rel3, result)
        self.assertEqual(result.cached_is_absolute, CacheStatus.IS)

        from algebraixlib.algebras import clans as _clans

        clan1 = Set(rel1)
        clan2 = Set(rel2)
        clan3 = Set(rel1, rel2)
        self.assertTrue(_clans.is_absolute_member(clan1))
        self.assertTrue(_clans.is_absolute_member(clan2))
        self.assertTrue(_clans.is_functional(clan1))
        self.assertTrue(_clans.is_functional(clan2))
        self.assertTrue(_clans.is_right_functional(clan1))
        self.assertTrue(_clans.is_right_functional(clan2))
        result = union(clan1, clan2)
        self.assertEqual(clan3, result)
        self.assertEqual(result.cached_absolute, CacheStatus.IS)
        self.assertEqual(result.cached_functional, CacheStatus.IS)
        self.assertEqual(result.cached_right_functional, CacheStatus.IS)

        clan4 = Set(Set(Couplet(s1, 1), Couplet(s1, 2)))
        clan5 = Set(rel1, Set(Couplet(s1, 1), Couplet(s1, 2)))
        self.assertFalse(_clans.is_absolute_member(clan4))
        self.assertFalse(_clans.is_functional(clan4))
        result = union(clan1, clan4)
        self.assertEqual(clan5, result)
        self.assertEqual(result.cached_absolute, CacheStatus.IS_NOT)
        self.assertEqual(result.cached_functional, CacheStatus.IS_NOT)

        rel1 = Set(Couplet('a', 1), Couplet('b', 2)).cache_relation(CacheStatus.IS)
        rel2 = Set(Couplet('c', 3))
        self.assertEqual(union(rel1, rel2).cached_relation, CacheStatus.UNKNOWN)
        self.assertTrue(_rels.is_member(rel2))
        self.assertEqual(union(rel1, rel2).cached_relation, CacheStatus.IS)
Example #26
0
def swap(rel: 'P(M x M)', swaps: 'P(M x M)', _checked=True) -> 'P(M x M)':
    """Return a :term:`relation` where  components in ``rel`` are swapped according to ``swaps``.

    :param rel: The relation with the left components to swap.
    :param swaps: A relation where both right components and left components are current left
        components in ``rel``.  These left components are swapped.
    :return: A version of ``rel`` where the left components of the member couplets are swapped.
    """
    if _checked:
        if not is_member(rel):
            return _make_or_raise_undef()
        if not is_member(swaps):
            return _make_or_raise_undef()
    else:
        assert is_member(rel)
        assert is_member(swaps)
    renames = _sets.union(swaps, transpose(swaps, _checked=False), _checked=False)
    return rename(rel, renames, _checked=False)
Example #27
0
    def functional_union(rel1: 'P(M x M)', rel2: 'P(M x M)', _checked=True) -> 'P(M x M)':
        """Return the union of ``rel1`` and ``rel2`` if it is a function, otherwise `Undef()`.

        :return: The :term:`left-functional union` of the :term:`relation`\s ``rel1`` and ``rel2``;
            that is, the :term:`union` if the result is :term:`left-functional`, otherwise
            `Undef()`. Also return `Undef()` if ``rel1`` or ``rel2`` are not relations.
        """
        if _checked:
            if not is_member(rel1):
                return _make_or_raise_undef()
            if not is_member(rel2):
                return _make_or_raise_undef()
        else:
            assert is_member(rel1)
            assert is_member(rel2)
        rel_union = _sets.union(rel1, rel2, _checked=False).cache_is_relation(True)
        if not is_left_functional(rel_union, _checked=False):
            return _make_or_raise_undef(2)
        return rel_union
Example #28
0
def functional_add(func: 'P(M x M)', element: 'M x M') -> 'P(M x M)':
    """Add ``element`` to ``func`` and return the new functional relation.

    :param func: The source data. Must be a :term:`function`. It must not contain a :term:`couplet`
        with the same :term:`left component` as ``element``.
    :param element: The element to be added to ``func``. Must be a :class:`~.Couplet` and its
        :term:`left component` must not be a left component in ``func``.
    :return: The new relation, composed of ``func`` and ``element``.
    """
    if not is_member(func) or not is_functional(func):
        return _undef.make_or_raise_undef2(func)
    if not _couplets.is_member(element):
        return _undef.make_or_raise_undef2(element)
    if _sets.is_subset_of(_mo.Set(element.left), get_lefts(func)):
        return _undef.make_or_raise_undef(2)
    element_func = _mo.Set(element).cache_relation(_mo.CacheStatus.IS)
    result = _sets.union(func, element_func)
    assert result.cached_is_relation and is_functional(result)
    result.cache_functional(_mo.CacheStatus.IS)
    return result
Example #29
0
def get_new_board(board, new_cells):
    if verbose:
        for cell in new_cells:
            row = cell('row').value
            col = cell('col').value
            value = cell('value').value
            print("*** value %d goes in Row %d, Col %d" % (value, row, col))

    cell_filter = project(new_cells, 'row', 'col')
    old_cells = clans.superstrict(board, cell_filter)
    new_board = sets.minus(board, old_cells)

    bands_stacks = clans.superstrict(BANDS_STACKS, cell_filter)
    new_cells = clans.functional_cross_union(new_cells, bands_stacks)

    new_board = sets.union(new_board, new_cells)
    # if verbose:
    #     print(get_string(new_board))
    assert len(new_board) == GRID_SIZE*GRID_SIZE
    return new_board
Example #30
0
def functional_add(func: 'P(M x M)', element: 'M x M') -> 'P(M x M)':
    """Add ``element`` to ``func`` and return the new functional relation.

    :param func: The source data. Must be a :term:`function`. It must not contain a :term:`couplet`
        with the same :term:`left component` as ``element``.
    :param element: The element to be added to ``func``. Must be a :class:`~.Couplet` and its
        :term:`left component` must not be a left component in ``func``.
    :return: The new relation, composed of ``func`` and ``element``.
    """
    if not is_member(func) or not is_functional(func):
        return _undef.make_or_raise_undef2(func)
    if not _couplets.is_member(element):
        return _undef.make_or_raise_undef2(element)
    if _sets.is_subset_of(_mo.Set(element.left), get_lefts(func)):
        return _undef.make_or_raise_undef(2)
    element_func = _mo.Set(element).cache_relation(_mo.CacheStatus.IS)
    result = _sets.union(func, element_func)
    assert result.cached_is_relation and is_functional(result)
    result.cache_functional(_mo.CacheStatus.IS)
    return result
Example #31
0
def swap(rel: 'P(M x M)', swaps: 'P(M x M)', _checked=True) -> 'P(M x M)':
    r"""Return a relation where  components in ``rel`` are swapped according to ``swaps``.

    :param rel: The :term:`relation` with the :term:`left component`\s to swap.
    :param swaps: A relation where both :term:`right component`\s and left components are current
        left components in ``rel``.  These left components are swapped.
    :return: A version of ``rel`` where some left components of the member :term:`couplet`\s are
        swapped, according to ``swaps``.
    """
    if _checked:
        if not is_member(rel):
            return _undef.make_or_raise_undef2(rel)
        if not is_member(swaps):
            return _undef.make_or_raise_undef2(swaps)
    else:
        assert is_member_or_undef(rel)
        assert is_member_or_undef(swaps)
        if rel is _undef.Undef() or swaps is _undef.Undef():
            return _undef.make_or_raise_undef(2)
    renames = _sets.union(swaps, transpose(swaps, _checked=False), _checked=False)
    return rename(rel, renames, _checked=False)
Example #32
0
def fill_lefts(rel: 'P(M x M)', renames: 'P(M x M)', _checked=True) -> 'P(M x M)':
    """Return the left components in ``rel`` that are missing in ``renames`` as a diagonal
    unioned with ``renames``.

    :param rel: The :term:`relation` that provides the full :term:`left set`.
    :param renames: A relation where the :term:`right component`\s are meant to be
        :term:`composition` 'origins' and the :term:`left component`\s composition 'targets'.
    :return: A relation that contains all members of ``lefts`` unioned with a :term:`diagonal`
        that consists of all left components in ``rel`` that are missing in ``lefts``.
    """
    if _checked:
        if not is_member(rel):
            return _make_or_raise_undef()
        if not is_member(renames):
            return _make_or_raise_undef()
    else:
        assert is_member(rel)
        assert is_member(renames)
    missing_lefts = _sets.minus(get_lefts(rel, _checked=False),
                                get_rights(renames, _checked=False), _checked=False)
    diag_missing_lefts = diag(*missing_lefts)
    return _sets.union(renames, diag_missing_lefts, _checked=False).cache_is_relation(True)
Example #33
0
def get_rights(clan: 'PP(M x M)', _checked=True) -> "P( M )":
    r"""Return the :term:`right set` of this :term:`clan`.

    :return: The :term:`union` of the :term:`right set`\s of all :term:`relation`\s in the
        :term:`clan` or `Undef()` if ``clan`` is not a clan.
    """
    if _checked:
        if not is_member(clan):
            return _make_or_raise_undef()
    else:
        assert is_member(clan)
    if clan.is_empty:
        # The right set of an empty set is the empty set
        return clan
    clan_itr = iter(clan)
    right_set = _relations.get_rights(next(clan_itr), _checked=False)
    for rel in clan_itr:
        right_set = _sets.union(
            _relations.get_rights(rel, _checked=False), right_set, _checked=False)
    if not right_set.is_empty:
        right_set.cache_is_relation(False).cache_is_clan(False)
    return right_set
Example #34
0
    def right_functional_union(rel1: 'P(M x M)', rel2: 'P(M x M)', _checked=True) -> 'P(M x M)':
        r"""Return the union of ``rel1`` and ``rel2`` if it is right-functional, otherwise
        `Undef()`.

        :return: The :term:`right-functional union` of the :term:`relation`\s ``rel1`` and
            ``rel2``; that is, the :term:`union` if the result is :term:`right-functional`,
            otherwise `Undef()`. Also return `Undef()` if ``rel1`` or ``rel2`` are not relations.
        """
        if _checked:
            if not is_member(rel1):
                return _undef.make_or_raise_undef2(rel1)
            if not is_member(rel2):
                return _undef.make_or_raise_undef2(rel2)
        else:
            assert is_member_or_undef(rel1)
            assert is_member_or_undef(rel2)
            if rel1 is _undef.Undef() or rel2 is _undef.Undef():
                return _undef.make_or_raise_undef(2)
        rel_union = _sets.union(rel1, rel2, _checked=False).cache_relation(_mo.CacheStatus.IS)
        if not is_right_functional(rel_union, _checked=False):
            return _undef.make_or_raise_undef(2)
        return rel_union
Example #35
0
def swap(rel: 'P(M x M)', swaps: 'P(M x M)', _checked=True) -> 'P(M x M)':
    r"""Return a relation where  components in ``rel`` are swapped according to ``swaps``.

    :param rel: The :term:`relation` with the :term:`left component`\s to swap.
    :param swaps: A relation where both :term:`right component`\s and left components are current
        left components in ``rel``.  These left components are swapped.
    :return: A version of ``rel`` where some left components of the member :term:`couplet`\s are
        swapped, according to ``swaps``.
    """
    if _checked:
        if not is_member(rel):
            return _undef.make_or_raise_undef2(rel)
        if not is_member(swaps):
            return _undef.make_or_raise_undef2(swaps)
    else:
        assert is_member_or_undef(rel)
        assert is_member_or_undef(swaps)
        if rel is _undef.Undef() or swaps is _undef.Undef():
            return _undef.make_or_raise_undef(2)
    renames = _sets.union(swaps,
                          transpose(swaps, _checked=False),
                          _checked=False)
    return rename(rel, renames, _checked=False)
Example #36
0
def get_new_board(board, new_cells):
    if VERBOSE:
        for cell in new_cells:
            row = cell('row').value
            col = cell('col').value
            value = cell('value').value
            print("*** value %d goes in Row %d, Col %d" % (value, row, col))

    cell_filter = project(new_cells, 'row', 'col')
    old_cells = clans.superstrict(board, cell_filter)
    new_board = sets.minus(board, old_cells)

    band = new_cells['band']
    if not band:
        # print("missing band")
        bands_stacks = clans.superstrict(BANDS_STACKS, cell_filter)
        new_cells = clans.cross_functional_union(new_cells, bands_stacks)

    new_board = sets.union(new_board, new_cells)
    # if VERBOSE:
    #     print(get_string(new_board))
    assert len(new_board) == GRID_SIZE * GRID_SIZE
    return new_board
Example #37
0
def get_lefts(clan: 'PP(M x M)', _checked=True) -> 'P( M )':
    r"""Return the set of the left components of all couplets in all relations in ``clan``.

    :return: The :term:`union` of the :term:`left set`\s of all :term:`relation`\s in ``clan`` or
        `Undef()` if ``clan`` is not a :term:`clan`.
    """
    if _checked:
        if not is_member(clan):
            return _undef.make_or_raise_undef()
    else:
        assert is_member(clan)
    if clan.is_empty:
        # The left set of an empty set is the empty set
        return clan
    clan_itr = iter(clan)
    left_set = _relations.get_lefts(next(clan_itr), _checked=False)
    for rel in clan_itr:
        left_set = _sets.union(
            _relations.get_lefts(rel, _checked=False), left_set, _checked=False)
    if not left_set.is_empty:
        if clan.cached_is_absolute:
            left_set.cache_absolute(_mo.CacheStatus.IS)
    return left_set
Example #38
0
    def lhs_cross_functional_union(lhs: 'PP( MxM )',
                                   rhs: 'PP( MxM )',
                                   _checked=True) -> 'PP(M x M)':
        """Return the :term:`lhs-cross-functional union` ('left join') of ``lhs`` and ``rhs``.

        This operation results in a :term:`clan` that contains every :term:`relation` of a
        :term:`cross-functional union`, but also contains all relations in ``lhs`` that
        are not already part of one of the resulting relations.

        :param lhs: All relations in this clan are guaranteed to be represented in the result.
        :return: The resulting clan or `Undef()` if ``lhs`` or ``rhs`` are not clans.
        """
        if _checked:
            if not is_member(lhs):
                return _undef.make_or_raise_undef2(lhs)
            if not is_member(rhs):
                return _undef.make_or_raise_undef2(rhs)
        else:
            assert is_member_or_undef(lhs)
            assert is_member_or_undef(rhs)
            if lhs is _undef.Undef() or rhs is _undef.Undef():
                return _undef.make_or_raise_undef(2)
        cfu = cross_functional_union(lhs, rhs, _checked=False)
        lhs_rest = _mo.Set(
            lhs_elem for lhs_elem in lhs if cross_functional_union(
                _mo.Set(lhs_elem, direct_load=True), rhs).is_empty)
        result = _sets.union(cfu, lhs_rest, _checked=False)
        if not result.is_empty:
            result.cache_clan(_mo.CacheStatus.IS)
            if lhs.cached_is_functional:
                result.cache_functional(_mo.CacheStatus.IS)
            if lhs.cached_is_not_right_functional or rhs.cached_is_not_right_functional:
                result.cache_right_functional(_mo.CacheStatus.IS_NOT)
            if not rhs.is_empty and not lhs_rest.is_empty:
                result.cache_regular(_mo.CacheStatus.IS_NOT)
        return result
Example #39
0
def check_rows(_board):
    """Look for rows where there is only one missing value.  If any are found fill in the missing
    value.  Look for rows where there are two missing values.  If either missing value is blocked
    by the same value in the candidate row, col, or block then the other value can be placed in
    the blocked cell.  The other value can be placed in the other cell.  Look for rows with more
    than two missing values.  Check each empty cell to see only one of the missing values can be
    placed in it.  Check each value to see if there is only one cell where it can be placed."""
    if verbose:
        print("* check_rows")
    board = get_filled_cells(_board)

    all_rows_clans = partition.partition(board, partial(by_key, 'row'))
    for row_clan in _sort(all_rows_clans, key=partial(by_clan_key, 'row')):
        row = project(row_clan, 'row')
        board_row = clans.superstrict(_board, row)
        values_clan = get_missing_values(row_clan)

        if row_clan.cardinality == GRID_SIZE-1:
            # Row is missing only 1 value, remove row_clan from the board leaving target row_col
            row_col = sets.minus(board_row, row_clan)
            new_cells = clans.cross_union(row_col, values_clan)
            _board = get_new_board(_board, new_cells)
            continue

        # Get the set of candidate col/value pairs
        row_possible = clans.cross_union(values_clan,
                                         project(sets.minus(board_row, row_clan), 'col'))

        if row_clan.cardinality == GRID_SIZE-2:

            # The occupied_clan is the col/value pair that is a conflict for each col/value
            occupied_clan = project(clans.superstrict(board, row_possible), 'col', 'value')

            # If there are no conflicts neither value can be placed without checking entire board
            if not occupied_clan.is_empty:
                # ..remove occupied_clan col/value pairs from all possible
                new_possible = sets.minus(row_possible, occupied_clan)

                if new_possible.cardinality == 2:
                    # Of the 4 possibilities (2 values * 2 cols), 2 were removed, place remaining
                    new_cells = clans.cross_union(row, new_possible)
                    _board = get_new_board(_board, new_cells)
                    continue

                # 3 of the possibilities remain...
                occupied_col = project(occupied_clan, 'col')

                # Remove the occupied_col choices to get the first col/value pair
                col_value1 = clans.superstrict(new_possible, occupied_col)

                occupied_val = project(col_value1, 'value')

                # Remove the occupied_val choices to get the second col/value pair
                col_value2 = sets.minus(new_possible, clans.superstrict(new_possible, occupied_val))

                new_cells = clans.cross_union(row, col_value1)
                new_cells = sets.union(new_cells, clans.cross_union(row, col_value2))
                _board = get_new_board(_board, new_cells)
                continue

        # The occupied_clan is the row/col/value set that could be a conflict for values
        occupied_clan = clans.superstrict(board, values_clan)

        # If there are no conflicts then no cells can be placed
        if occupied_clan.is_empty:
            continue

        # Add row to row_possible for remaining checks
        all_possible = clans.cross_union(row_possible, row)

        # Get the set of conflicts...conflicting row/value + col/value
        conflict = sets.union(
            clans.superstrict(all_possible,
                              project(occupied_clan, 'value', 'col')),
            clans.superstrict(all_possible,
                              project(occupied_clan, 'value', 'row')))

        # Remove the conflicts from all_possible
        new_possible = sets.minus(all_possible, conflict)

        if new_possible.is_empty:
            continue  # All possible may have been excluded due to row/col conflicts

        # Otherwise...need to check for block (band+stack) conflicts too!!
        # ...if value exists in same block as element of all_possible

        # Add band/stack
        new_targets = clans.superstrict(BANDS_STACKS, project(new_possible, 'row', 'col'))
        new_possible3 = clans.functional_cross_union(new_targets, new_possible)
        occupied_clan2 = occupied_clan

        # Remove block (band+stack) conflicts
        new_possible4a = sets.minus(project(new_possible3, 'value', 'band', 'stack'),
                                    project(occupied_clan2, 'value', 'band', 'stack'))
        new_possible4 = clans.superstrict(new_possible3, new_possible4a)

        # Partition by row/col
        placed = 0
        candidates = partition.partition(new_possible4, partial(by_keys, 'row', 'col'))
        for candidate in _sort(candidates, key=partial(by_clan_key, 'col')):
            # If any row/col has only 1 candidate, place it
            if candidate.cardinality == 1:
                # Remove band/stack
                cell = project(candidate, 'row', 'col', 'value')
                _board = get_new_board(_board, cell)
                placed += 1

        if placed:
            continue

        # Partition by value
        candidates = partition.partition(new_possible4, partial(by_key, 'value'))
        for candidate in _sort(candidates, key=partial(by_clan_key, 'value')):
            # If any value fits in only 1 cell, place it
            if candidate.cardinality == 1:
                # Remove band/stack
                cell = project(candidate, 'row', 'col', 'value')
                _board = get_new_board(_board, cell)
    return _board
Example #40
0
def check_rows(_board, try_harder=0):
    """Look for rows where there is only one missing value.  If any are found fill in the missing
    value.  Look for rows where there are two missing values.  If either missing value is blocked
    by the same value in the candidate row, col, or block then the other value can be placed in
    the blocked cell.  The other value can be placed in the other cell.  Look for rows with more
    than two missing values.  Check each empty cell to see only one of the missing values can be
    placed in it.  Check each value to see if there is only one cell where it can be placed."""
    if VERBOSE:
        print("* check_rows")
    board = get_filled_cells(_board)

    all_rows_clans = partition.partition(board, partial(by_key, 'row'))
    for row_clan in _SORT(all_rows_clans, key=partial(by_clan_key, 'row')):
        row = project(row_clan, 'row')
        board_row = clans.superstrict(_board, row)
        values_clan = get_missing_values(row_clan)

        if row_clan.cardinality == GRID_SIZE - 1:
            # Row is missing only 1 value, remove row_clan from the board leaving target row_col
            row_col = sets.minus(board_row, row_clan)
            new_cells = clans.cross_union(row_col, values_clan)
            _board = get_new_board(_board, new_cells)
            try_harder = 0
            continue

        # Get the set of candidate col/value pairs
        row_possible = clans.cross_union(values_clan,
                                         project(sets.minus(board_row, row_clan), 'col'))

        if row_clan.cardinality == GRID_SIZE - 2:

            # The occupied_clan is the col/value pair that is a conflict for each col/value
            occupied_clan = project(clans.superstrict(board, row_possible), 'col', 'value')

            # If there are no conflicts neither value can be placed without checking entire board
            if not occupied_clan.is_empty:
                # ..remove occupied_clan col/value pairs from all possible
                new_possible = sets.minus(row_possible, occupied_clan)

                if new_possible.cardinality == 2:
                    # Of the 4 possibilities (2 values * 2 cols), 2 were removed, place remaining
                    new_cells = clans.cross_union(row, new_possible)
                    _board = get_new_board(_board, new_cells)
                    try_harder = 0
                    continue

                # 3 of the possibilities remain...
                occupied_col = project(occupied_clan, 'col')

                # Remove the occupied_col choices to get the first col/value pair
                col_value1 = clans.superstrict(new_possible, occupied_col)

                occupied_val = project(col_value1, 'value')

                # Remove the occupied_val choices to get the second col/value pair
                col_value2 = sets.minus(new_possible, clans.superstrict(new_possible, occupied_val))

                new_cells = clans.cross_union(row, col_value1)
                new_cells = sets.union(new_cells, clans.cross_union(row, col_value2))
                _board = get_new_board(_board, new_cells)
                try_harder = 0
                continue

        # The occupied_clan is the row/col/value set that could be a conflict for values
        occupied_clan = clans.superstrict(board, values_clan)

        # If there are no conflicts then no cells can be placed
        if occupied_clan.is_empty:
            continue

        # Add row to row_possible for remaining checks
        all_possible = clans.cross_union(row_possible, row)

        # Get the set of conflicts...conflicting row/value + col/value
        conflict = sets.union(
            clans.superstrict(all_possible,
                              project(occupied_clan, 'value', 'col')),
            clans.superstrict(all_possible,
                              project(occupied_clan, 'value', 'row')))

        # Remove the conflicts from all_possible
        new_possible = sets.minus(all_possible, conflict)

        if new_possible.is_empty:
            continue  # All possible may have been excluded due to row/col conflicts

        # Otherwise...need to check for block (band+stack) conflicts too!!
        # ...if value exists in same block as element of all_possible

        # Add band/stack
        new_targets = clans.superstrict(BANDS_STACKS, project(new_possible, 'row', 'col'))
        new_possible3 = clans.cross_functional_union(new_targets, new_possible)
        occupied_clan2 = occupied_clan

        # Remove block (band+stack) conflicts
        new_possible4a = sets.minus(project(new_possible3, 'value', 'band', 'stack'),
                                    project(occupied_clan2, 'value', 'band', 'stack'))
        new_possible4 = clans.superstrict(new_possible3, new_possible4a)

        while True:
            candidates_updated = False
            # Partition by row/col
            placed = 0
            candidates = partition.partition(new_possible4, partial(by_keys, 'row', 'col'))
            for candidate in _SORT(candidates, key=partial(by_clan_key, 'col')):
                # If any row/col has only 1 candidate, place it
                if candidate.cardinality == 1:
                    # Remove band/stack
                    _board = get_new_board(_board, candidate)
                    try_harder = 0
                    placed += 1
            if placed:
                break

            # Partition by value
            candidates = partition.partition(new_possible4, partial(by_key, 'value'))
            for candidate in _SORT(candidates, key=partial(by_clan_key, 'value')):
                # If any value fits in only 1 cell, place it
                if candidate.cardinality == 1:
                    # Remove band/stack
                    _board = get_new_board(_board, candidate)
                    try_harder = 0
                else:  # If any value must be placed elsewhere, remove as candidate for this cell
                    if try_harder:
                        value = project(candidate, 'value')
                        # If this row of a sibling block must contain this value...
                        blocks = partition.partition(candidate, partial(by_keys, 'band', 'stack'))
                        if blocks.cardinality > 1:
                            for block_clan in _SORT(blocks,
                                                    key=partial(by_clan_keys, 'band', 'stack')):
                                block = project(block_clan, 'band', 'stack')
                                board_block = clans.superstrict(board, block)
                                if board_block.is_empty:
                                    continue

                                new_possible, conflict = get_block_candidates(board_block, board)
                                new_possible_value = clans.superstrict(new_possible, value)

                                if new_possible_value['row'].cardinality == 1:
                                    # Value must be placed in this block
                                    # ...other block candidates can be removed
                                    remove = sets.minus(candidate, block_clan)
                                    new_possible4 = sets.minus(new_possible4, remove)
                                    candidates_updated = True
            if not candidates_updated or not try_harder:
                break
    return _board
Example #41
0
region_key_nation_name_pairs_accumulator = Set()
for region in regions:
    region_key = project(Set(region), 'regionkey')
    print('region_key:\n' + mo_to_str(region_key))

    region_nations = region['nation']
    print('region_nations:\n' + mo_to_str(region_nations))

    region_nation_names = project(region_nations, 'name')
    print('region_nation_names:\n' + mo_to_str(region_nation_names))

    region_key_nation_name_pairs = cross_union(region_key, region_nation_names)
    print('region_key_nation_name_pairs:\n' +
          mo_to_str(region_key_nation_name_pairs))

    region_key_nation_name_pairs_accumulator = union(
        region_key_nation_name_pairs_accumulator, region_key_nation_name_pairs)
    print('region_key_nation_name_pairs_accumulator:\n' +
          mo_to_str(region_key_nation_name_pairs_accumulator))

print('region_key_nation_name_pairs_accumulator:\n' +
      mo_to_str(region_key_nation_name_pairs_accumulator))

# Get all regions with a nation named "UNITED STATES"
# for $x in doc("regions.xml")/regions/region/nation[name="UNITED STATES"] return <name>$x/../name</name>
from algebraixlib.mathobjects.couplet import Couplet
us_nation_name = from_dict({'name': 'UNITED STATES'})
print('us_nation_name:\n' + mo_to_str(us_nation_name))

us_region_key_nation_name_pair = superstrict(
    region_key_nation_name_pairs_accumulator, us_nation_name)
print('us_region_key_nation_name_pair:\n' +
Example #42
0
from algebraixlib.algebras.sets import union
region_key_nation_name_pairs_accumulator = Set()
for region in regions:
    region_key = project(Set(region), 'regionkey')
    print('region_key:\n' + mo_to_str(region_key))

    region_nations = region['nation']
    print('region_nations:\n' + mo_to_str(region_nations))

    region_nation_names = project(region_nations, 'name')
    print('region_nation_names:\n' + mo_to_str(region_nation_names))

    region_key_nation_name_pairs = cross_union(region_key, region_nation_names)
    print('region_key_nation_name_pairs:\n' + mo_to_str(region_key_nation_name_pairs))

    region_key_nation_name_pairs_accumulator = union(region_key_nation_name_pairs_accumulator, region_key_nation_name_pairs)
    print('region_key_nation_name_pairs_accumulator:\n' + mo_to_str(region_key_nation_name_pairs_accumulator))

print('region_key_nation_name_pairs_accumulator:\n' + mo_to_str(region_key_nation_name_pairs_accumulator))

# Get all regions with a nation named "UNITED STATES"
# for $x in doc("regions.xml")/regions/region/nation[name="UNITED STATES"] return <name>$x/../name</name>
from algebraixlib.mathobjects.couplet import Couplet
us_nation_name = from_dict({'name': 'UNITED STATES'})
print('us_nation_name:\n' + mo_to_str(us_nation_name))

us_region_key_nation_name_pair = superstrict(region_key_nation_name_pairs_accumulator, us_nation_name)
print('us_region_key_nation_name_pair:\n' + mo_to_str(us_region_key_nation_name_pair))

us_region_key = project(us_region_key_nation_name_pair, 'regionkey')
print('us_region_key:\n' + mo_to_str(us_region_key))
 def test_union(self):
     rel1 = Set(Couplet('a', 1), Couplet('b', 2)).cache_relation(CacheStatus.IS)
     rel2 = Set(Couplet('c', 3))
     self.assertEqual(sets.union(rel1, rel2).cached_relation, CacheStatus.UNKNOWN)
     self.assertTrue(is_member(rel2))
     self.assertEqual(sets.union(rel1, rel2).cached_relation, CacheStatus.IS)
Example #44
0
# Sets support for...in syntax for iteration and in and not in syntax for membership tests. Because
# sets are unordered, they do not support random access (no bracket operator).
nums = Set(1, 2, 3, 4, 5)
for elem in nums:
    print(elem, " ")
print(1 in nums)
print(7 in nums)

# Sets can be unioned, intersected, set-minused. Relations such as is_subset and is_superset are
# defined.
a = Set(1, 2)
b = Set(2, 3)

import algebraixlib.algebras.sets as sets

print("union(a, b) = {}".format(sets.union(a, b)))
print("intersect(a, b) = {}".format(sets.intersect(a, b)))
print("minus(a, b) = {}".format(sets.minus(a, b)))
print("is_subset(a, b) = {}".format(sets.is_subset_of(a, b)))
print("is_superset(a, {{1}}) = {}".format(sets.is_superset_of(a, Set(1))))

# We can use a Couplet to model a single truth, such as 'blue'^'sky' or 'jeff'^'name'. By collecting
# multiple Couplets together in a set, we form a mathematical model of a data record. This data
# structure, called a binary relation (abbreviated from hereon as simply 'relation'), is the
# fundamental data type in a Data Algebra program.
record_relation = Set(Couplet('id', 123), Couplet('name', 'jeff'), Couplet('loves', 'math'),
                      Couplet('loves', 'code'))
print(record_relation)

# Some relations, specify a function from left to right. This is the case when every left
# value maps to exactly one right value. Such a relation is called "left functional".
Example #45
0
# Sets support for...in syntax for iteration and in and not in syntax for membership tests. Because
# sets are unordered, they do not support random access (no bracket operator).
nums = Set(1, 2, 3, 4, 5)
for elem in nums:
    print(elem, " ")
print(1 in nums)
print(7 in nums)

# Sets can be unioned, intersected, set-minused. Relations such as is_subset and is_superset are
# defined.
a = Set(1, 2)
b = Set(2, 3)

import algebraixlib.algebras.sets as sets

print("union(a, b) = {}".format(sets.union(a, b)))
print("intersect(a, b) = {}".format(sets.intersect(a, b)))
print("minus(a, b) = {}".format(sets.minus(a, b)))
print("is_subset(a, b) = {}".format(sets.is_subset_of(a, b)))
print("is_superset(a, {{1}}) = {}".format(sets.is_superset_of(a, Set(1))))

# We can use a Couplet to model a single truth, such as 'sky'->'blue' or 'name'->'jeff'. By
# collecting multiple Couplets together in a set, we form a mathematical model of a data record.
# This data structure, called a binary relation (abbreviated from hereon as simply 'relation'), is
# the fundamental data type in a Data Algebra program.
record_relation = Set(Couplet('id', 123), Couplet('name', 'jeff'),
                      Couplet('loves', 'math'), Couplet('loves', 'code'))
print(record_relation)

# Some relations, specify a function from left to right. This is the case when every left
# value maps to exactly one right value. Such a relation is called "left functional".
Example #46
0
def check_blocks(_board):
    """Check each block.  If there is only one value missing..."""
    if verbose:
        print("* check_blocks")

    board = get_filled_cells(_board)
    blocks = partition.partition(board, partial(by_keys, 'band', 'stack'))
    for block_clan in _sort(blocks, key=partial(by_clan_keys, 'band', 'stack')):
        # Get the set of missing values...see if any can be placed due to row/col information
        values_clan = get_missing_values(block_clan)

        # Get the set of missing values...see if any can be placed due to row/col information
        target_rowcols = get_missing_rowcols(block_clan)

        if block_clan.cardinality == GRID_SIZE-1:
            new_cells = clans.cross_union(target_rowcols, values_clan)
            _board = get_new_board(_board, new_cells)
            continue

        # Need cross union values with rows
        rows_clan = project(target_rowcols, 'row')
        cols_clan = project(target_rowcols, 'col')
        possible_rows_values = clans.cross_union(values_clan, rows_clan)
        possible_cols_values = clans.cross_union(values_clan, cols_clan)

        possible_rows_cols_values = sets.union(possible_rows_values, possible_cols_values)

        # The occupied_clan is the row/col/value set that is a conflict for values
        occupied_clan = project(clans.superstrict(board, possible_rows_cols_values),
                                'value', 'row', 'col')

        # If there are no conflicts then no cells can be placed
        if occupied_clan.is_empty:
            continue

        all_possible = clans.cross_union(values_clan, target_rowcols).cache_is_left_functional(True)
        for rel in all_possible:
            rel.cache_is_left_functional(True)

        # Get the set of conflicts...conflicting row/value + col/value
        conflict = sets.union(
            clans.superstrict(all_possible, project(occupied_clan, 'value', 'col')),
            clans.superstrict(all_possible, project(occupied_clan, 'value', 'row')))

        # Remove the conflicts from all_possible
        new_possible = sets.minus(all_possible, conflict)

        if block_clan.cardinality == GRID_SIZE-2:
            # Knowing that the value in conflict can't be placed in the conflict cell
            # ..it must go in the other...
            first_choice = clans.superstrict(new_possible, project(conflict, 'value'))
            if first_choice.cardinality == 2:
                # place both values
                _board = get_new_board(_board, first_choice)
                continue

            # Remove the first choice for all_possible
            remaining_possible = sets.minus(new_possible, first_choice)

            # Knowing that first_choice goes in a row/col, remove other value from that cell
            first_rowcol = project(first_choice, 'row', 'col')

            # The remaining cell is the second choice
            second_choice = sets.minus(remaining_possible,
                                       clans.superstrict(remaining_possible, first_rowcol))

            new_cells = sets.union(first_choice, second_choice)
            _board = get_new_board(_board, new_cells)
            continue

        # Partition by value
        candidates = partition.partition(new_possible, partial(by_key, 'value'))
        for candidate in _sort(candidates, key=partial(by_clan_key, 'value')):
            # If any value fits in only 1 cell, place it
            if candidate.cardinality == 1:
                # Remove band/stack
                new_cell = project(candidate, 'row', 'col', 'value')
                _board = get_new_board(_board, new_cell)
    return _board