This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: Range causing unstable output(Windows64)
Type: behavior Stage: resolved
Components: Library (Lib) Versions: Python 3.5
process
Status: closed Resolution: not a bug
Dependencies: Superseder:
Assigned To: Nosy List: Sean Moss, mark.dickinson
Priority: normal Keywords:

Created on 2019-12-08 20:09 by Sean Moss, last changed 2022-04-11 14:59 by admin. This issue is now closed.

Files
File name Uploaded Description Edit
input7.txt Sean Moss, 2019-12-08 20:09 Input for IntCode
Messages (2)
msg358022 - (view) Author: Sean Moss (Sean Moss) Date: 2019-12-08 20:09
I was doing this year's Advent of Code and found that the following program produces unstable output when run using the given file as input:
"""
from itertools import permutations
import gc

def runProgram(amp_input, program, counter):
    while program[counter] != 99:
        # print('*' * 99)
        instruction = str(program[counter])
        opcode = instruction[-2:]
        value1 = program[counter + 1]
        # print('2:{}'.format(counter))
        try:
            if opcode in ['01', '02', '1', '2', '5', '05', '6', '06', '7', '07', '8', '08']:
                value1 = program[counter + 1]
                value2 = program[counter + 2]
                param_modes = instruction[::-1][2:]
                # print('{} {} {} {}'.format(instruction, value1, value2, value3))
                param_modes += '0' * (3 - len(param_modes))
                # print(param_modes)
                if param_modes[0] == '0':
                    value1 = program[value1]
                if param_modes[1] == '0':
                    value2 = program[value2]
                # print('{} {} {} {}'.format(instruction, value1, value2, value3))
                if opcode in ['01', '02', '1', '2', '7', '07', '8', '08']:
                    value3 = program[counter + 3]
                    if opcode.endswith('1'):
                        program[value3] = value1 + value2
                    elif opcode.endswith('2'):
                        program[value3] = value1 * value2
                    elif opcode in ['7', '07']:
                        program[value3] = 1 if value1 < value2 else 0
                    elif opcode in ['8', '08']:
                        program[value3] = 1 if value1 == value2 else 0
                    counter += 4
                elif opcode in ['5', '05']:
                    if value1 != 0:
                        counter = value2
                    else:
                        counter += 3
                elif opcode in ['6', '06']:
                    if value1 == 0:
                        counter = value2
                    else:
                        counter += 3
            elif opcode in ['03', '3']:
                program[value1] = amp_input.pop(0)
                counter += 2
            elif opcode in ['4', '04']:
                # print('{} {}'.format(instruction, value1))
                if instruction != '104':
                    value1 = program[value1]
                # print('Output value: {}'.format(value1))
                counter += 2
                return value1, counter
            else:
                print("Something broke at {}".format(counter))
                print("program state {}".format(program))
                print(instruction)
                return False
        except Exception as e:
            print("Out of bounds at {}".format(counter))
            print("program state {}".format(program))
            print(instruction)
            print(e)
            print(len(program))
            return
    return program, True

outputs = []
max_output = 0
# initial_program = list(map(int, open('input7.txt').read().split(',')))
amp_ids = ['A', 'B', 'C', 'D', 'E']
permutation = [5, 6, 7, 8, 9]
# for permutation in permutations([5, 6, 7, 8, 9]):
amp_programs = {amp_id: [list(map(int, open('input7.txt').read().split(',')))[:], 0] for amp_id in ['A', 'B', 'C', 'D', 'E']}
loops = 0
prev_output = 0
for x in range(0, 5):
    gc.collect()
    new_output, outer_counter = runProgram([permutation[x], prev_output], amp_programs[amp_ids[x]][0], amp_programs[amp_ids[x]][1])
    if outer_counter is not True:
        prev_output = new_output
    amp_programs[amp_ids[x]][1] = outer_counter
    # print(new_output)
while amp_programs['E'][1] is not True:
    gc.collect()
    for amp_id in amp_programs.keys():
        amp = amp_programs[amp_id]
        # print(prev_output)
        # print('1:{}'.format(amp[1]))
        new_output, outer_counter = runProgram([prev_output], amp[0], amp[1])
        if outer_counter is not True:
            prev_output = new_output
        amp[1] = outer_counter
        # print('{}, {}'.format(amp[1], outer_counter))
# outputs.append(prev_output)
# print(prev_output)
outputs.append(prev_output)
    # if prev_output > max_output:
    #     max_output = prev_output

print(max(outputs))
# print(outputs)
"""
However when this program is run on the same input it produces stable input:
"""
from itertools import permutations

def runProgram(amp_input, program, counter):
    while program[counter] != 99:
        # print('*' * 99)
        instruction = str(program[counter])
        opcode = instruction[-2:]
        value1 = program[counter + 1]
        # print('2:{}'.format(counter))
        try:
            if opcode in ['01', '02', '1', '2', '5', '05', '6', '06', '7', '07', '8', '08']:
                value1 = program[counter + 1]
                value2 = program[counter + 2]
                param_modes = instruction[::-1][2:]
                # print('{} {} {} {}'.format(instruction, value1, value2, value3))
                param_modes += '0' * (3 - len(param_modes))
                # print(param_modes)
                if param_modes[0] == '0':
                    value1 = program[value1]
                if param_modes[1] == '0':
                    value2 = program[value2]
                # print('{} {} {} {}'.format(instruction, value1, value2, value3))
                if opcode in ['01', '02', '1', '2', '7', '07', '8', '08']:
                    value3 = program[counter + 3]
                    if opcode.endswith('1'):
                        program[value3] = value1 + value2
                    elif opcode.endswith('2'):
                        program[value3] = value1 * value2
                    elif opcode in ['7', '07']:
                        program[value3] = 1 if value1 < value2 else 0
                    elif opcode in ['8', '08']:
                        program[value3] = 1 if value1 == value2 else 0
                    counter += 4
                elif opcode in ['5', '05']:
                    if value1 != 0:
                        counter = value2
                    else:
                        counter += 3
                elif opcode in ['6', '06']:
                    if value1 == 0:
                        counter = value2
                    else:
                        counter += 3
            elif opcode in ['03', '3']:
                program[value1] = amp_input.pop(0)
                counter += 2
            elif opcode in ['4', '04']:
                # print('{} {}'.format(instruction, value1))
                if instruction != '104':
                    value1 = program[value1]
                # print('Output value: {}'.format(value1))
                counter += 2
                return value1, counter
            else:
                print("Something broke at {}".format(counter))
                print("program state {}".format(program))
                print(instruction)
                return False
        except Exception as e:
            print("Out of bounds at {}".format(counter))
            print("program state {}".format(program))
            print(instruction)
            print(e)
            print(len(program))
            return
    return program, True

outputs = []
max_output = 0
# initial_program = list(map(int, open('input7.txt').read().split(',')))
amp_ids = ['A', 'B', 'C', 'D', 'E']
permutation = [5, 6, 7, 8, 9]
# for permutation in permutations([5, 6, 7, 8, 9]):
amp_programs = {amp_id: [list(map(int, open('input7.txt').read().split(',')))[:], 0] for amp_id in ['A', 'B', 'C', 'D', 'E']}
loops = 0
prev_output = 0
for amp, perm in zip(amp_programs.values(), permutation):
    new_output, outer_counter = runProgram([perm, prev_output], amp[0], amp[1])
    if outer_counter is not True:
        prev_output = new_output
    amp[1] = outer_counter
    # print(new_output)
while amp_programs['E'][1] is not True:
    for amp_id in amp_programs.keys():
        amp = amp_programs[amp_id]
        # print(prev_output)
        # print('1:{}'.format(amp[1]))
        new_output, outer_counter = runProgram([prev_output], amp[0], amp[1])
        if outer_counter is not True:
            prev_output = new_output
        amp[1] = outer_counter
        # print('{}, {}'.format(amp[1], outer_counter))
# outputs.append(prev_output)
# print(prev_output)
outputs.append(prev_output)
    # if prev_output > max_output:
    #     max_output = prev_output

print(max(outputs))
# print(outputs)
"""
The only difference is that the second program uses the zip function to iterate while the first uses the range function to iterate. Again this is not a case of divergent output, it's that the first program doesn't always have the same output, the second program always has the same output.
msg358027 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2019-12-08 20:54
This has nothing to do with range. The source of indeterminacy is this line in your code:

    for amp_id in amp_programs.keys():

In Python 3.5, the ordering of `amp_programs.keys()` could differ from run to run. (With Python 3.6 and later, that won't happen.)

If you want a deterministic result in Python 3.5, you could for example replace that line with:

    for amp_id in sorted(amp_programs):

Closing here; this isn't a Python bug.
History
Date User Action Args
2022-04-11 14:59:24adminsetgithub: 83181
2019-12-08 20:54:21mark.dickinsonsetstatus: open -> closed

nosy: + mark.dickinson
messages: + msg358027

resolution: not a bug
stage: resolved
2019-12-08 20:09:03Sean Mosscreate