I'm doing a few algorithm questions and ran into a strange issue in one of my test cases. The get_num_groups function returns an integer that the assert statements verify. 
The first assert statement seems to pass intermittently after I introduced the line list(set(words)) in the function. 
Code:
def are_similar(w1, w2):
    '''
        returns true if swapping the positions of exactly 2 characters
        in a word makes the given words equal
    '''
    if len(w1) != len(w2):
        return False
    mis_matches = 0
    locs = []
    n = len(w1)
    for i in range(n):
        if w1[i] != w2[i]:
            mis_matches += 1
            locs.append(i)
    if mis_matches != 2:
        return False
    # check if the 2 letter swap will result in equal words
    return w1[locs[0]] == w2[locs[1]] and w1[locs[1]] == w2[locs[0]]
def get_group(graph, n, word_i):
    # for a word at the word_i'th position, get all words connected to it
    group = set()
    stack = [word_i]
    while stack:
        i = stack.pop()
        # view all nodes connected directly/indirectly to i'th node
        for neigh in range(i, n):
            if graph[i][neigh] != 0:
                group.add(neigh)
                stack.append(neigh)
                graph[neigh][i] = 0
                graph[i][neigh] = 0
    return group
def get_num_groups(words):
    '''
        - creates a graph of words that are connected if similar.
        - does a dfs/bfs traversal to collect count of the disconnected sub graphs
    '''
    words = list(set(words)) # causes a strange intermittent bug. in first testcase
    n = len(words)
    # n X n adj matrix
    graph = [[0] * n for _ in range(n)]
    # create graph
    for i in range(n):
        graph[i][i] = 1 # word is connected to itself
        wi = words[i]
        for j in range(i+1, n):
            wj = words[j]
            s = are_similar(wi, wj)
            if s:
                graph[i][j] = 1
                graph[j][i] = 1
    num_groups = 0
    for i in range(n):
        g = get_group(graph, n, i)
        if g:
            num_groups += 1
    return num_groups
if __name__ == '__main__':
    # === Test ===
    wds = ["tars", "rats", "star", "arts"]
    num_ways = get_num_groups(wds)
    inp = (wds)
    res = (num_ways)
    expected = (2)
    assert res == expected, '[Fail] {} -> {}\nexpected:{}'.format(inp, res, expected)
    print('[+]')
    # === Test ===
    wds = []
    num_ways = get_num_groups(wds)
    inp = (wds)
    res = (num_ways)
    expected = (0)
    assert res == expected, '[Fail] {} -> {}\nexpected:{}'.format(inp, res, expected)
    print('[+]')
    # === Test ===
    wds = ["ss", "ss"]
    num_ways = get_num_groups(wds)
    inp = (wds)
    res = (num_ways)
    expected = (1)
    assert res == expected, '[Fail] {} -> {}\nexpected:{}'.format(inp, res, expected)
    print('[+]')
    # === Test ===
    wds = ["ssa", "ssb"]
    num_ways = get_num_groups(wds)
    inp = (wds)
    res = (num_ways)
    expected = (2)
    assert res == expected, '[Fail] {} -> {}\nexpected:{}'.format(inp, res, expected)
    print('[+]')
Unexpected output when run with command while true; do python3 file.py; done:
+]
[+]
[+]
[+]
Traceback (most recent call last):
  File "p.py", line 110, in <module>
    assert res == expected, '[Fail] {} -> {}\nexpected:{}'.format(inp, res, expected)
AssertionError: [Fail] ['tars', 'rats', 'star', 'arts'] -> 3
expected:2
Traceback (most recent call last):
  File "p.py", line 110, in <module>
    assert res == expected, '[Fail] {} -> {}\nexpected:{}'.format(inp, res, expected)
AssertionError: [Fail] ['tars', 'rats', 'star', 'arts'] -> 3
expected:2
[+]
[+]
[+]
[+]
[+]
[+]
[+]
[+]
[+]
[+]
[+]
[+]
[+]
[+]
[+]
[+]
[+]
[+]
[+]
[+]
[+]
[+]
[+]
It is extremely unlikely, but, could there be a bug in python with the way list(set(<some array>)) is done?
