Skip to content
Snippets Groups Projects
test_schedule.py 31.9 KiB
Newer Older
  • Learn to ignore specific revisions
  • B-ASIC test suite for the schedule module and Schedule class.
    
    Hugo Winbladh's avatar
    Hugo Winbladh committed
    import matplotlib.testing.decorators
    
    import pytest
    
    from b_asic.core_operations import Addition, Butterfly, ConstantMultiplication
    
    from b_asic.process import OperatorProcess
    
    from b_asic.schedule import Schedule
    
    from b_asic.scheduler import ALAPScheduler, ASAPScheduler
    
    from b_asic.sfg_generators import direct_form_1_iir, direct_form_fir
    
    from b_asic.signal_flow_graph import SFG
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
    from b_asic.special_operations import Delay, Input, Output
    
    
    
    class TestInit:
        def test_simple_filter_normal_latency(self, sfg_simple_filter):
            sfg_simple_filter.set_latency_of_type(Addition.type_name(), 5)
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
            sfg_simple_filter.set_latency_of_type(ConstantMultiplication.type_name(), 4)
    
            schedule = Schedule(sfg_simple_filter, scheduler=ASAPScheduler())
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
            assert schedule._start_times == {
    
                "in0": 0,
                "add0": 4,
                "cmul0": 0,
                "out0": 0,
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
            }
    
            assert schedule.schedule_time == 9
    
            with pytest.raises(
                ValueError, match="No operation with graph_id 'foo' in schedule"
            ):
                schedule.start_time_of_operation("foo")
    
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
        def test_complicated_single_outputs_normal_latency(self, precedence_sfg_delays):
    
            precedence_sfg_delays.set_latency_of_type(Addition.type_name(), 4)
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
            precedence_sfg_delays.set_latency_of_type(ConstantMultiplication.type_name(), 3)
    
            schedule = Schedule(precedence_sfg_delays, scheduler=ASAPScheduler())
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
            start_times_names = {}
    
            for op_id, start_time in schedule._start_times.items():
    
                op_name = precedence_sfg_delays.find_by_id(op_id).name
                start_times_names[op_name] = start_time
    
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
            assert start_times_names == {
                "IN1": 0,
                "C0": 0,
                "B1": 0,
                "B2": 0,
                "ADD2": 3,
                "ADD1": 7,
                "Q1": 11,
                "A0": 14,
                "A1": 0,
                "A2": 0,
                "ADD3": 3,
                "ADD4": 17,
                "OUT1": 21,
            }
    
            assert schedule.schedule_time == 21
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
    
    
        def test_complicated_single_outputs_normal_latency_alap(
            self, precedence_sfg_delays
        ):
            precedence_sfg_delays.set_latency_of_type(Addition.type_name(), 4)
            precedence_sfg_delays.set_latency_of_type(ConstantMultiplication.type_name(), 3)
    
    
            schedule = Schedule(precedence_sfg_delays, scheduler=ALAPScheduler())
    
            for op_id in schedule.start_times:
    
                op_name = precedence_sfg_delays.find_by_id(op_id).name
    
                start_times_names[op_name] = schedule.start_time_of_operation(op_id)
    
    
            assert start_times_names == {
                "IN1": 4,
                "C0": 4,
                "B1": 0,
                "B2": 0,
                "ADD2": 3,
                "ADD1": 7,
                "Q1": 11,
                "A0": 14,
                "A1": 10,
                "A2": 10,
                "ADD3": 13,
                "ADD4": 17,
                "OUT1": 21,
            }
            assert schedule.schedule_time == 21
    
    
        def test_complicated_single_outputs_normal_latency_alap_with_schedule_time(
            self, precedence_sfg_delays
        ):
            precedence_sfg_delays.set_latency_of_type(Addition.type_name(), 4)
            precedence_sfg_delays.set_latency_of_type(ConstantMultiplication.type_name(), 3)
    
    
            schedule = Schedule(
                precedence_sfg_delays, schedule_time=25, scheduler=ALAPScheduler()
            )
    
    
            start_times_names = {}
            for op_id in schedule.start_times:
                op_name = precedence_sfg_delays.find_by_id(op_id).name
                start_times_names[op_name] = schedule.start_time_of_operation(op_id)
    
            assert start_times_names == {
    
    Simon Bjurek's avatar
    Simon Bjurek committed
                "IN1": 8 - 4,
                "C0": 8 - 4,
                "B1": 4 - 4,
                "B2": 4 - 4,
                "ADD2": 7 - 4,
                "ADD1": 11 - 4,
                "Q1": 15 - 4,
                "A0": 18 - 4,
                "A1": 14 - 4,
                "A2": 14 - 4,
                "ADD3": 17 - 4,
                "ADD4": 21 - 4,
                "OUT1": 25 - 4,
    
    Simon Bjurek's avatar
    Simon Bjurek committed
            assert schedule.schedule_time == 25 - 4
    
    
        def test_complicated_single_outputs_normal_latency_alap_too_short_schedule_time(
            self, precedence_sfg_delays
        ):
            precedence_sfg_delays.set_latency_of_type(Addition.type_name(), 4)
            precedence_sfg_delays.set_latency_of_type(ConstantMultiplication.type_name(), 3)
            with pytest.raises(ValueError, match="Too short schedule time. Minimum is 21."):
    
                Schedule(precedence_sfg_delays, schedule_time=19, scheduler=ALAPScheduler())
    
        def test_complicated_single_outputs_normal_latency_from_fixture(
            self, secondorder_iir_schedule
        ):
            start_times_names = {
                secondorder_iir_schedule.sfg.find_by_id(op_id).name: start_time
                for op_id, start_time in secondorder_iir_schedule._start_times.items()
            }
    
            assert start_times_names == {
                "IN1": 0,
                "C0": 0,
                "B1": 0,
                "B2": 0,
                "ADD2": 3,
                "ADD1": 7,
                "Q1": 11,
                "A0": 14,
                "A1": 0,
                "A2": 0,
                "ADD3": 3,
                "ADD4": 17,
                "OUT1": 21,
            }
            assert secondorder_iir_schedule.schedule_time == 21
    
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
        def test_complicated_single_outputs_complex_latencies(self, precedence_sfg_delays):
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
            precedence_sfg_delays.set_latency_offsets_of_type(
                ConstantMultiplication.type_name(), {"in0": 3, "out0": 5}
            )
    
            precedence_sfg_delays.find_by_name("B1")[0].set_latency_offsets(
                {"in0": 4, "out0": 7}
            )
            precedence_sfg_delays.find_by_name("B2")[0].set_latency_offsets(
                {"in0": 1, "out0": 4}
            )
            precedence_sfg_delays.find_by_name("ADD2")[0].set_latency_offsets(
                {"in0": 4, "in1": 2, "out0": 4}
            )
            precedence_sfg_delays.find_by_name("ADD1")[0].set_latency_offsets(
                {"in0": 1, "in1": 2, "out0": 4}
            )
            precedence_sfg_delays.find_by_name("Q1")[0].set_latency_offsets(
                {"in0": 3, "out0": 6}
            )
            precedence_sfg_delays.find_by_name("A0")[0].set_latency_offsets(
                {"in0": 0, "out0": 2}
            )
    
            precedence_sfg_delays.find_by_name("A1")[0].set_latency_offsets(
                {"in0": 0, "out0": 5}
            )
            precedence_sfg_delays.find_by_name("A2")[0].set_latency_offsets(
                {"in0": 2, "out0": 3}
            )
            precedence_sfg_delays.find_by_name("ADD3")[0].set_latency_offsets(
                {"in0": 2, "in1": 1, "out0": 4}
            )
            precedence_sfg_delays.find_by_name("ADD4")[0].set_latency_offsets(
                {"in0": 6, "in1": 7, "out0": 9}
            )
    
            schedule = Schedule(precedence_sfg_delays, scheduler=ASAPScheduler())
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
            start_times_names = {}
    
            for op_id, start_time in schedule._start_times.items():
    
                op_name = precedence_sfg_delays.find_by_id(op_id).name
                start_times_names[op_name] = start_time
    
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
            assert start_times_names == {
                "IN1": 0,
                "C0": 0,
                "B1": 0,
                "B2": 0,
                "ADD2": 3,
                "ADD1": 5,
                "Q1": 6,
                "A0": 12,
                "A1": 0,
                "A2": 0,
                "ADD3": 3,
                "ADD4": 8,
                "OUT1": 17,
            }
    
    
            assert schedule.schedule_time == 17
    
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
        def test_independent_sfg(self, sfg_two_inputs_two_outputs_independent_with_cmul):
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
            schedule = Schedule(
                sfg_two_inputs_two_outputs_independent_with_cmul,
    
                scheduler=ASAPScheduler(),
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
            )
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
            start_times_names = {}
    
            for op_id, start_time in schedule._start_times.items():
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
                op_name = sfg_two_inputs_two_outputs_independent_with_cmul.find_by_id(
                    op_id
                ).name
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
            assert start_times_names == {
                "C1": 0,
                "IN1": 0,
                "IN2": 0,
                "CMUL1": 0,
                "CMUL2": 5,
                "ADD1": 0,
                "CMUL3": 7,
                "OUT1": 9,
                "OUT2": 10,
            }
    
            assert schedule.schedule_time == 10
    
        def test_provided_schedule(self):
            sfg = direct_form_1_iir([1, 2, 3], [1, 2, 3])
    
            sfg.set_latency_of_type(Addition.type_name(), 1)
            sfg.set_latency_of_type(ConstantMultiplication.type_name(), 3)
            sfg.set_execution_time_of_type(Addition.type_name(), 1)
            sfg.set_execution_time_of_type(ConstantMultiplication.type_name(), 1)
    
            start_times = {
                "in0": 1,
                "cmul0": 1,
                "cmul1": 0,
                "cmul2": 0,
                "cmul3": 0,
                "cmul4": 0,
                "add3": 3,
                "add1": 3,
                "add0": 4,
                "add2": 5,
                "out0": 6,
            }
            laps = {
                's8': 1,
                's10': 2,
                's15': 1,
                's17': 2,
                's0': 0,
                's3': 0,
                's12': 0,
                's11': 0,
                's14': 0,
                's13': 0,
                's6': 0,
                's4': 0,
                's5': 0,
                's2': 0,
            }
    
            schedule = Schedule(sfg, start_times=start_times, laps=laps)
    
            assert schedule.start_times == start_times
            assert schedule.laps == laps
            assert schedule.schedule_time == 6
    
    
    
    class TestSlacks:
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
        def test_forward_backward_slack_normal_latency(self, precedence_sfg_delays):
    
            precedence_sfg_delays.set_latency_of_type(Addition.type_name(), 1)
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
            precedence_sfg_delays.set_latency_of_type(ConstantMultiplication.type_name(), 3)
    
            schedule = Schedule(precedence_sfg_delays, scheduler=ASAPScheduler())
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
            assert (
                schedule.forward_slack(
                    precedence_sfg_delays.find_by_name("ADD3")[0].graph_id
                )
                == 7
            )
            assert (
                schedule.backward_slack(
                    precedence_sfg_delays.find_by_name("ADD3")[0].graph_id
                )
                == 0
            )
    
            assert (
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
                schedule.forward_slack(precedence_sfg_delays.find_by_name("A2")[0].graph_id)
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
                == 0
            )
            assert (
                schedule.backward_slack(
                    precedence_sfg_delays.find_by_name("A2")[0].graph_id
                )
                == 16
            )
    
    
        def test_slacks_normal_latency(self, precedence_sfg_delays):
            precedence_sfg_delays.set_latency_of_type(Addition.type_name(), 1)
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
            precedence_sfg_delays.set_latency_of_type(ConstantMultiplication.type_name(), 3)
    
            schedule = Schedule(precedence_sfg_delays, scheduler=ASAPScheduler())
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
            assert schedule.slacks(
                precedence_sfg_delays.find_by_name("ADD3")[0].graph_id
            ) == (0, 7)
            assert schedule.slacks(
                precedence_sfg_delays.find_by_name("A2")[0].graph_id
            ) == (16, 0)
    
        def test_print_slacks(self, capsys, precedence_sfg_delays):
            precedence_sfg_delays.set_latency_of_type(Addition.type_name(), 1)
            precedence_sfg_delays.set_latency_of_type(ConstantMultiplication.type_name(), 3)
    
    
            schedule = Schedule(precedence_sfg_delays, scheduler=ASAPScheduler())
    
            schedule.print_slacks()
            captured = capsys.readouterr()
    
            assert captured.out == (
                "Graph ID | Backward |  Forward\n"
                "---------|----------|---------\n"
                "add0     |        0 |        0\n"
                "add1     |        0 |        0\n"
                "add2     |        0 |        0\n"
                "add3     |        0 |        7\n"
                "cmul0    |        0 |        1\n"
                "cmul1    |        0 |        0\n"
                "cmul2    |        0 |        0\n"
                "cmul3    |        4 |        0\n"
                "cmul4    |       16 |        0\n"
                "cmul5    |       16 |        0\n"
                "cmul6    |        4 |        0\n"
                "in0      |       oo |        0\n"
                "out0     |        0 |       oo\n"
    
            assert captured.err == ""
    
        def test_print_slacks_sorting(self, capsys, precedence_sfg_delays):
            precedence_sfg_delays.set_latency_of_type(Addition.type_name(), 1)
            precedence_sfg_delays.set_latency_of_type(ConstantMultiplication.type_name(), 3)
    
    
            schedule = Schedule(precedence_sfg_delays, scheduler=ASAPScheduler())
    
            schedule.print_slacks(1)
            captured = capsys.readouterr()
    
            assert captured.out == (
                "Graph ID | Backward |  Forward\n"
                "---------|----------|---------\n"
                "cmul0    |        0 |        1\n"
                "add0     |        0 |        0\n"
                "add1     |        0 |        0\n"
                "cmul1    |        0 |        0\n"
                "cmul2    |        0 |        0\n"
                "add3     |        0 |        7\n"
                "add2     |        0 |        0\n"
                "out0     |        0 |       oo\n"
                "cmul3    |        4 |        0\n"
                "cmul6    |        4 |        0\n"
                "cmul4    |       16 |        0\n"
                "cmul5    |       16 |        0\n"
                "in0      |       oo |        0\n"
    
            assert captured.err == ""
    
        def test_slacks_errors(self, precedence_sfg_delays):
            precedence_sfg_delays.set_latency_of_type(Addition.type_name(), 1)
            precedence_sfg_delays.set_latency_of_type(ConstantMultiplication.type_name(), 3)
    
    
            schedule = Schedule(precedence_sfg_delays, scheduler=ASAPScheduler())
    
            with pytest.raises(
                ValueError, match="No operation with graph_id 'foo' in schedule"
            ):
                schedule.forward_slack("foo")
            with pytest.raises(
                ValueError, match="No operation with graph_id 'foo' in schedule"
            ):
                schedule.backward_slack("foo")
            with pytest.raises(
                ValueError, match="No operation with graph_id 'foo' in schedule"
            ):
                schedule.slacks("foo")
    
    
    
    class TestRescheduling:
        def test_move_operation(self, precedence_sfg_delays):
            precedence_sfg_delays.set_latency_of_type(Addition.type_name(), 4)
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
            precedence_sfg_delays.set_latency_of_type(ConstantMultiplication.type_name(), 3)
    
            schedule = Schedule(precedence_sfg_delays, scheduler=ASAPScheduler())
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
            schedule.move_operation(
                precedence_sfg_delays.find_by_name("ADD3")[0].graph_id, 4
            )
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
            schedule.move_operation(precedence_sfg_delays.find_by_name("A2")[0].graph_id, 2)
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
            start_times_names = {}
    
            for op_id, start_time in schedule._start_times.items():
    
                op_name = precedence_sfg_delays.find_by_id(op_id).name
                start_times_names[op_name] = start_time
    
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
            assert start_times_names == {
                "IN1": 0,
                "C0": 0,
                "B1": 0,
                "B2": 0,
                "ADD2": 3,
                "ADD1": 7,
                "Q1": 11,
                "A0": 14,
                "A1": 0,
                "A2": 2,
                "ADD3": 7,
                "ADD4": 17,
                "OUT1": 21,
            }
    
    
            with pytest.raises(
                ValueError, match="No operation with graph_id 'foo' in schedule"
            ):
                schedule.move_operation("foo", 0)
    
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
        def test_move_operation_slack_after_rescheduling(self, precedence_sfg_delays):
    
            precedence_sfg_delays.set_latency_of_type(Addition.type_name(), 1)
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
            precedence_sfg_delays.set_latency_of_type(ConstantMultiplication.type_name(), 3)
    
            schedule = Schedule(precedence_sfg_delays, scheduler=ASAPScheduler())
    
            add3_id = precedence_sfg_delays.find_by_name("ADD3")[0].graph_id
    
            schedule.move_operation(add3_id, 4)
            assert schedule.forward_slack(add3_id) == 3
            assert schedule.backward_slack(add3_id) == 4
    
    
            a2_id = precedence_sfg_delays.find_by_name("A2")[0].graph_id
    
            assert schedule.forward_slack(a2_id) == 4
            assert schedule.backward_slack(a2_id) == 16
    
            schedule.move_operation(a2_id, 2)
    
            assert schedule.forward_slack(add3_id) == 3
            assert schedule.backward_slack(add3_id) == 2
    
            assert schedule.forward_slack(a2_id) == 2
            assert schedule.backward_slack(a2_id) == 18
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
        def test_move_operation_incorrect_move_backward(self, precedence_sfg_delays):
    
            precedence_sfg_delays.set_latency_of_type(Addition.type_name(), 1)
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
            precedence_sfg_delays.set_latency_of_type(ConstantMultiplication.type_name(), 3)
    
            schedule = Schedule(precedence_sfg_delays, scheduler=ASAPScheduler())
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
            with pytest.raises(
                ValueError,
    
                match="Operation 'add3' got incorrect move: -4. Must be between 0 and 7.",
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
            ):
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
                schedule.move_operation(
                    precedence_sfg_delays.find_by_name("ADD3")[0].graph_id, -4
                )
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
        def test_move_operation_incorrect_move_forward(self, precedence_sfg_delays):
    
            precedence_sfg_delays.set_latency_of_type(Addition.type_name(), 1)
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
            precedence_sfg_delays.set_latency_of_type(ConstantMultiplication.type_name(), 3)
    
            schedule = Schedule(precedence_sfg_delays, scheduler=ASAPScheduler())
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
            with pytest.raises(
                ValueError,
    
                match="Operation 'add3' got incorrect move: 10. Must be between 0 and 7.",
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
            ):
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
                schedule.move_operation(
                    precedence_sfg_delays.find_by_name("ADD3")[0].graph_id, 10
                )
    
        def test_move_operation_asap(self, precedence_sfg_delays):
            precedence_sfg_delays.set_latency_of_type(Addition.type_name(), 1)
            precedence_sfg_delays.set_latency_of_type(ConstantMultiplication.type_name(), 3)
    
    
            schedule = Schedule(precedence_sfg_delays, scheduler=ASAPScheduler())
    
            assert schedule.backward_slack('cmul5') == 16
            assert schedule.forward_slack('cmul5') == 0
            schedule.move_operation_asap('cmul5')
            assert schedule.start_time_of_operation('in0') == 0
            assert schedule.laps['cmul5'] == 0
            assert schedule.backward_slack('cmul5') == 0
            assert schedule.forward_slack('cmul5') == 16
    
    
        def test_move_input_asap_does_not_mess_up_laps(self, precedence_sfg_delays):
            precedence_sfg_delays.set_latency_of_type(Addition.type_name(), 1)
            precedence_sfg_delays.set_latency_of_type(ConstantMultiplication.type_name(), 3)
    
    
            schedule = Schedule(precedence_sfg_delays, scheduler=ASAPScheduler())
    
            old_laps = schedule.laps['in0']
            schedule.move_operation_asap('in0')
            assert schedule.start_time_of_operation('in0') == 0
            assert schedule.laps['in0'] == old_laps
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
        def test_move_operation_acc(self):
            in0 = Input()
            d = Delay()
            a = d + in0
            out0 = Output(a)
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
            sfg = SFG([in0], [out0])
            sfg.set_latency_of_type(Addition.type_name(), 1)
    
            schedule = Schedule(sfg, scheduler=ASAPScheduler(), cyclic=True)
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
    
            # Check initial conditions
    
            assert schedule.laps[sfg.find_by_id("add0").input(0).signals[0].graph_id] == 1
            assert schedule.laps[sfg.find_by_id("add0").input(1).signals[0].graph_id] == 0
            assert schedule._start_times["add0"] == 0
            assert schedule.laps[sfg.find_by_id("out0").input(0).signals[0].graph_id] == 0
            assert schedule._start_times["out0"] == 1
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
    
            # Move and scheduling algorithm behaves differently
    
            schedule.move_operation("out0", 0)
            assert schedule.laps[sfg.find_by_id("out0").input(0).signals[0].graph_id] == 0
            assert schedule.laps[sfg.find_by_id("add0").input(0).signals[0].graph_id] == 1
            assert schedule.laps[sfg.find_by_id("add0").input(1).signals[0].graph_id] == 0
            assert schedule._start_times["out0"] == 1
            assert schedule._start_times["add0"] == 0
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
    
            # Increase schedule time
            schedule.set_schedule_time(2)
    
            assert schedule.laps[sfg.find_by_id("out0").input(0).signals[0].graph_id] == 0
            assert schedule.laps[sfg.find_by_id("add0").input(0).signals[0].graph_id] == 1
            assert schedule.laps[sfg.find_by_id("add0").input(1).signals[0].graph_id] == 0
            assert schedule._start_times["out0"] == 1
            assert schedule._start_times["add0"] == 0
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
    
            # Move out one time unit
    
            schedule.move_operation("out0", 1)
            assert schedule.laps[sfg.find_by_id("out0").input(0).signals[0].graph_id] == 0
            assert schedule.laps[sfg.find_by_id("add0").input(0).signals[0].graph_id] == 1
            assert schedule.laps[sfg.find_by_id("add0").input(1).signals[0].graph_id] == 0
            assert schedule._start_times["out0"] == 2
            assert schedule._start_times["add0"] == 0
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
    
            # Move add one time unit
    
            schedule.move_operation("add0", 1)
            assert schedule.laps[sfg.find_by_id("add0").input(0).signals[0].graph_id] == 1
            assert schedule.laps[sfg.find_by_id("add0").input(1).signals[0].graph_id] == 0
            assert schedule.laps[sfg.find_by_id("out0").input(0).signals[0].graph_id] == 0
            assert schedule._start_times["add0"] == 1
            assert schedule._start_times["out0"] == 2
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
    
            # Move add back one time unit
    
            schedule.move_operation("add0", -1)
            assert schedule.laps[sfg.find_by_id("add0").input(0).signals[0].graph_id] == 1
            assert schedule.laps[sfg.find_by_id("add0").input(1).signals[0].graph_id] == 0
            assert schedule.laps[sfg.find_by_id("out0").input(0).signals[0].graph_id] == 0
            assert schedule._start_times["add0"] == 0
            assert schedule._start_times["out0"] == 2
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
    
    
        def test_reintroduce_delays(
            self, precedence_sfg_delays, sfg_direct_form_iir_lp_filter
        ):
    
            precedence_sfg_delays.set_latency_of_type(Addition.type_name(), 1)
            precedence_sfg_delays.set_latency_of_type(ConstantMultiplication.type_name(), 3)
            sfg_direct_form_iir_lp_filter.set_latency_of_type(Addition.type_name(), 1)
    
            sfg_direct_form_iir_lp_filter.set_latency_of_type(
                ConstantMultiplication.type_name(), 3
            )
    
            schedule = Schedule(precedence_sfg_delays, scheduler=ASAPScheduler())
    
            sfg = schedule.sfg
            assert precedence_sfg_delays.evaluate(5) == sfg.evaluate(5)
    
    
            schedule = Schedule(sfg_direct_form_iir_lp_filter, scheduler=ASAPScheduler())
    
            sfg = schedule.sfg
            assert sfg_direct_form_iir_lp_filter.evaluate(5) == sfg.evaluate(5)
    
    
                list(range(1, 10)),
                mult_properties={'latency': 2, 'execution_time': 1},
                add_properties={'latency': 2, 'execution_time': 1},
            )
    
            schedule = Schedule(fir_sfg, scheduler=ASAPScheduler())
    
            sfg = schedule.sfg
            assert fir_sfg.evaluate(5) == sfg.evaluate(5)
    
    class TestTimeResolution:
        def test_increase_time_resolution(
            self, sfg_two_inputs_two_outputs_independent_with_cmul
        ):
            schedule = Schedule(
                sfg_two_inputs_two_outputs_independent_with_cmul,
    
                scheduler=ASAPScheduler(),
    
            )
            old_schedule_time = schedule.schedule_time
    
            assert schedule.get_possible_time_resolution_decrements() == [1]
    
    
            schedule.increase_time_resolution(2)
    
            start_times_names = {}
            for op_id, start_time in schedule._start_times.items():
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
                op_name = sfg_two_inputs_two_outputs_independent_with_cmul.find_by_id(
                    op_id
                ).name
    
                start_times_names[op_name] = start_time
    
            assert start_times_names == {
                "C1": 0,
                "IN1": 0,
                "IN2": 0,
                "CMUL1": 0,
                "CMUL2": 10,
                "ADD1": 0,
                "CMUL3": 14,
                "OUT1": 18,
                "OUT2": 20,
            }
    
            assert 2 * old_schedule_time == schedule.schedule_time
    
            assert schedule.get_possible_time_resolution_decrements() == [1, 2]
    
    
        def test_increase_time_resolution_twice(
            self, sfg_two_inputs_two_outputs_independent_with_cmul
        ):
            schedule = Schedule(
                sfg_two_inputs_two_outputs_independent_with_cmul,
    
                scheduler=ASAPScheduler(),
    
            )
            old_schedule_time = schedule.schedule_time
    
            schedule.increase_time_resolution(2)
            schedule.increase_time_resolution(3)
    
            start_times_names = {}
            for op_id, start_time in schedule._start_times.items():
    
                op = sfg_two_inputs_two_outputs_independent_with_cmul.find_by_id(op_id)
                start_times_names[op.name] = (start_time, op.latency, op.execution_time)
    
    
            assert start_times_names == {
    
                "C1": (0, 0, None),
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
                "IN1": (0, 0, 0),
                "IN2": (0, 0, 0),
    
                "CMUL1": (0, 30, 18),
                "CMUL2": (30, 24, 6),
                "ADD1": (0, 42, 12),
                "CMUL3": (42, 18, 6),
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
                "OUT1": (54, 0, 0),
                "OUT2": (60, 0, 0),
    
            }
    
            assert 6 * old_schedule_time == schedule.schedule_time
    
            assert schedule.get_possible_time_resolution_decrements() == [
                1,
                2,
                3,
                6,
            ]
    
        def test_increase_decrease_time_resolution(
            self, sfg_two_inputs_two_outputs_independent_with_cmul
        ):
            schedule = Schedule(
                sfg_two_inputs_two_outputs_independent_with_cmul,
    
                scheduler=ASAPScheduler(),
    
            )
            old_schedule_time = schedule.schedule_time
            assert schedule.get_possible_time_resolution_decrements() == [1]
    
            schedule.increase_time_resolution(6)
    
            start_times_names = {}
            for op_id, start_time in schedule._start_times.items():
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
                op_name = sfg_two_inputs_two_outputs_independent_with_cmul.find_by_id(
                    op_id
                ).name
    
                start_times_names[op_name] = start_time
    
            assert start_times_names == {
                "C1": 0,
                "IN1": 0,
                "IN2": 0,
                "CMUL1": 0,
                "CMUL2": 30,
                "ADD1": 0,
                "CMUL3": 42,
                "OUT1": 54,
                "OUT2": 60,
            }
    
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
            with pytest.raises(ValueError, match="Not possible to decrease resolution"):
    
                schedule.decrease_time_resolution(4)
    
            schedule.decrease_time_resolution(3)
            start_times_names = {}
            for op_id, start_time in schedule._start_times.items():
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
                op_name = sfg_two_inputs_two_outputs_independent_with_cmul.find_by_id(
                    op_id
                ).name
    
                start_times_names[op_name] = start_time
    
            assert start_times_names == {
                "C1": 0,
                "IN1": 0,
                "IN2": 0,
                "CMUL1": 0,
                "CMUL2": 10,
                "ADD1": 0,
                "CMUL3": 14,
                "OUT1": 18,
                "OUT2": 20,
            }
    
            assert 2 * old_schedule_time == schedule.schedule_time
            assert schedule.get_possible_time_resolution_decrements() == [1, 2]
    
    
    
    class TestProcesses:
        def test__get_memory_variables_list(self, secondorder_iir_schedule):
            mvl = secondorder_iir_schedule._get_memory_variables_list()
            assert len(mvl) == 12
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
            pc = secondorder_iir_schedule.get_memory_variables()
            assert len(pc) == 12
    
        def test_get_operations(self, secondorder_iir_schedule_with_execution_times):
            pc = secondorder_iir_schedule_with_execution_times.get_operations()
            assert len(pc) == 13
            assert all(isinstance(operand, OperatorProcess) for operand in pc.collection)
    
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
    
    class TestFigureGeneration:
    
        @matplotlib.testing.decorators.image_comparison(
            ['test__get_figure_no_execution_times.png'], remove_text=True
        )
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
        def test__get_figure_no_execution_times(self, secondorder_iir_schedule):
            return secondorder_iir_schedule._get_figure()
    
    
    
    class TestErrors:
        def test_no_latency(self, sfg_simple_filter):
            with pytest.raises(
                ValueError,
    
                match="Input port 0 of operation add0 has no latency-offset.",
    
                Schedule(sfg_simple_filter, scheduler=ASAPScheduler())
    
    
        def test_no_output_latency(self):
            in1 = Input()
            in2 = Input()
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
            bfly = Butterfly(in1, in2, latency_offsets={"in0": 4, "in1": 2, "out0": 10})
    
            out1 = Output(bfly.output(0))
            out2 = Output(bfly.output(1))
            sfg = SFG([in1, in2], [out1, out2])
            with pytest.raises(
                ValueError,
    
                match="Output port 1 of operation bfly0 has no latency-offset.",
    
                Schedule(sfg, scheduler=ASAPScheduler())
    
            in1 = Input()
            in2 = Input()
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
            bfly1 = Butterfly(in1, in2, latency_offsets={"in0": 4, "in1": 2, "out1": 10})
    
            bfly2 = Butterfly(
                bfly1.output(0),
                bfly1.output(1),
                latency_offsets={"in0": 4, "in1": 2, "out0": 10, "out1": 8},
            )
            out1 = Output(bfly2.output(0))
            out2 = Output(bfly2.output(1))
            sfg = SFG([in1, in2], [out1, out2])
            with pytest.raises(
                ValueError,
    
                match="Output port 0 of operation bfly0 has no latency-offset.",
    
                Schedule(sfg, scheduler=ASAPScheduler())
    
    
        def test_too_short_schedule_time(self, sfg_simple_filter):
            sfg_simple_filter.set_latency_of_type(Addition.type_name(), 5)
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
            sfg_simple_filter.set_latency_of_type(ConstantMultiplication.type_name(), 4)
            with pytest.raises(ValueError, match="Too short schedule time. Minimum is 9."):
    
                Schedule(sfg_simple_filter, scheduler=ASAPScheduler(), schedule_time=3)
    
            schedule = Schedule(sfg_simple_filter, scheduler=ASAPScheduler())
    
            with pytest.raises(
                ValueError,
                match=re.escape("New schedule time (3) too short, minimum: 9."),
            ):
                schedule.set_schedule_time(3)
    
    
        # def test_incorrect_scheduling_algorithm(self, sfg_simple_filter):
        #     sfg_simple_filter.set_latency_of_type(Addition.type_name(), 1)
        #     sfg_simple_filter.set_latency_of_type(ConstantMultiplication.type_name(), 2)
        #     with pytest.raises(
        #         NotImplementedError, match="No algorithm with name: foo defined."
        #     ):
        #         Schedule(sfg_simple_filter, algorithm="foo")
    
        def test_no_sfg(self):
            with pytest.raises(TypeError, match="An SFG must be provided"):
                Schedule(1)
    
        def test_provided_no_start_times(self, sfg_simple_filter):
            sfg_simple_filter.set_latency_of_type(Addition.type_name(), 1)
            sfg_simple_filter.set_latency_of_type(ConstantMultiplication.type_name(), 2)
            with pytest.raises(
                ValueError, match="Must provide start_times when using 'provided'"
            ):
    
                Schedule(sfg_simple_filter)
    
    
        def test_provided_no_laps(self, sfg_simple_filter):
            sfg_simple_filter.set_latency_of_type(Addition.type_name(), 1)
            sfg_simple_filter.set_latency_of_type(ConstantMultiplication.type_name(), 2)
            with pytest.raises(ValueError, match="Must provide laps when using 'provided'"):
    
                Schedule(sfg_simple_filter, start_times={'in0': 0})
    
    
    class TestGetUsedTypeNames:
        def test_secondorder_iir_schedule(self, secondorder_iir_schedule):
            assert secondorder_iir_schedule.get_used_type_names() == [
                'add',
                'cmul',
                'in',
                'out',
            ]
    
    
    
    class TestYLocations:
        def test_provided_no_laps(self, sfg_simple_filter):
            sfg_simple_filter.set_latency_of_type(Addition.type_name(), 1)
            sfg_simple_filter.set_latency_of_type(ConstantMultiplication.type_name(), 2)
    
            schedule = Schedule(sfg_simple_filter, ASAPScheduler())
    
            assert schedule._y_locations == {"in0": 0, "cmul0": 1, "add0": 2, "out0": 3}
    
            schedule.move_y_location("add0", 1, insert=True)
            assert schedule._y_locations == {"in0": 0, "cmul0": 2, "add0": 1, "out0": 3}
            schedule.move_y_location("out0", 1)
            assert schedule._y_locations == {"in0": 0, "cmul0": 2, "add0": 1, "out0": 1}
    
        def test_reset(self, sfg_simple_filter):
            sfg_simple_filter.set_latency_of_type(Addition.type_name(), 1)
            sfg_simple_filter.set_latency_of_type(ConstantMultiplication.type_name(), 2)
            schedule = Schedule(sfg_simple_filter, ASAPScheduler())
    
    
            assert schedule._y_locations == {"in0": 0, "cmul0": 1, "add0": 2, "out0": 3}
    
            schedule.reset_y_locations()
            assert schedule._y_locations["in0"] is None
            assert schedule._y_locations["cmul0"] is None
            assert schedule._y_locations["add0"] is None
            assert schedule._y_locations["add0"] is None
            assert schedule._y_locations["out0"] is None
            assert schedule._y_locations["foo"] is None