• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

SpiNNakerManchester / SpiNNFrontEndCommon / 8143402062

04 Mar 2024 04:13PM UTC coverage: 47.969% (-0.02%) from 47.992%
8143402062

push

github

web-flow
Merge pull request #1157 from SpiNNakerManchester/pylint_default

Pylint default

2050 of 4800 branches covered (42.71%)

Branch coverage included in aggregate %.

63 of 110 new or added lines in 37 files covered. (57.27%)

26 existing lines in 18 files now uncovered.

5484 of 10906 relevant lines covered (50.28%)

0.5 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

19.34
/spinn_front_end_common/interface/interface_functions/host_bit_field_router_compressor.py
1
# Copyright (c) 2019 The University of Manchester
2
#
3
# Licensed under the Apache License, Version 2.0 (the "License");
4
# you may not use this file except in compliance with the License.
5
# You may obtain a copy of the License at
6
#
7
#     https://www.apache.org/licenses/LICENSE-2.0
8
#
9
# Unless required by applicable law or agreed to in writing, software
10
# distributed under the License is distributed on an "AS IS" BASIS,
11
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
# See the License for the specific language governing permissions and
13
# limitations under the License.
14

15
from collections import defaultdict
1✔
16
import functools
1✔
17
import math
1✔
18
import os
1✔
19
import struct
1✔
20
from typing import Dict, List, Optional, Tuple, cast
1✔
21
from spinn_utilities.config_holder import get_config_bool
1✔
22
from spinn_utilities.find_max_success import find_max_success
1✔
23
from spinn_utilities.progress_bar import ProgressBar
1✔
24
from spinn_utilities.ordered_set import OrderedSet
1✔
25
from spinn_machine import MulticastRoutingEntry, Chip
1✔
26
from pacman.exceptions import (
1✔
27
    PacmanAlgorithmFailedToGenerateOutputsException,
28
    PacmanElementAllocationException, MinimisationFailedError)
29
from pacman.model.routing_tables import (
1✔
30
    AbstractMulticastRoutingTable, MulticastRoutingTables,
31
    UnCompressedMulticastRoutingTable, CompressedMulticastRoutingTable)
32
from pacman.utilities.algorithm_utilities.routes_format import format_route
1✔
33
from pacman.operations.router_compressors import RTEntry
1✔
34
from pacman.operations.router_compressors.pair_compressor import (
1✔
35
    _PairCompressor)
36
from spinn_front_end_common.abstract_models import (
1✔
37
    AbstractSupportsBitFieldRoutingCompression)
38
from spinn_front_end_common.data import FecDataView
1✔
39
from spinn_front_end_common.utilities.helpful_functions import n_word_struct
1✔
40
from spinn_front_end_common.utilities.constants import (
1✔
41
    BYTES_PER_WORD, BYTES_PER_4_WORDS)
42
from spinn_front_end_common.utilities.report_functions.\
1✔
43
    bit_field_compressor_report import (
44
        generate_provenance_item)
45

46
_REPORT_FOLDER_NAME = "router_compressor_with_bitfield"
1✔
47

48

49
def host_based_bit_field_router_compressor() -> MulticastRoutingTables:
1✔
50
    """
51
    Entry point when using the PACMANAlgorithmExecutor.
52

53
    :return: compressed routing table entries
54
    :rtype: ~pacman.model.routing_tables.MulticastRoutingTables
55
    """
56
    routing_tables = FecDataView.get_uncompressed().routing_tables
×
57
    # create progress bar
58
    progress = ProgressBar(
×
59
        len(routing_tables) * 2,
60
        "Compressing routing Tables with bitfields in host")
61

62
    # create report
63
    if get_config_bool(
×
64
            "Reports", "write_router_compression_with_bitfield_report"):
65
        report_folder_path = generate_report_path()
×
66
    else:
67
        report_folder_path = None
×
68

69
    # compressed router table
70
    compressed_pacman_router_tables = MulticastRoutingTables()
×
71

72
    key_atom_map = generate_key_to_atom_map()
×
73

74
    most_costly_cores: Dict[Chip, Dict[int, int]] = defaultdict(
×
75
        lambda: defaultdict(int))
76
    for partition in FecDataView.iterate_partitions():
×
77
        for edge in partition.edges:
×
78
            splitter = edge.post_vertex.splitter
×
79
            for vertex, _ in splitter.get_source_specific_in_coming_vertices(
×
80
                    partition.pre_vertex, partition.identifier):
81
                place = FecDataView.get_placement_of_vertex(vertex)
×
82
                most_costly_cores[place.chip][place.p] += 1
×
83

84
    # start the routing table choice conversion
85
    compressor = HostBasedBitFieldRouterCompressor(
×
86
        key_atom_map, report_folder_path, most_costly_cores)
87
    for router_table in progress.over(routing_tables):
×
88
        compressor.compress_bitfields(
×
89
            router_table, compressed_pacman_router_tables)
90

91
    # return compressed tables
92
    return compressed_pacman_router_tables
×
93

94

95
def generate_report_path() -> str:
1✔
96
    """
97
    :rtype: str
98
    """
99
    report_folder_path = os.path.join(
×
100
        FecDataView.get_run_dir_path(), _REPORT_FOLDER_NAME)
101
    if not os.path.exists(report_folder_path):
×
102
        os.mkdir(report_folder_path)
×
103
    return report_folder_path
×
104

105

106
def generate_key_to_atom_map() -> Dict[int, int]:
1✔
107
    """
108
    *THIS IS NEEDED* due to the link from key to edge being lost.
109

110
    :return: key to atom map based of key to n atoms
111
    :rtype: dict(int,int)
112
    """
113
    # build key to n atoms map
114
    routing_infos = FecDataView.get_routing_infos()
×
115
    key_to_n_atoms_map: Dict[int, int] = dict()
×
116
    for partition in FecDataView.iterate_partitions():
×
117
        for vertex in partition.pre_vertex.splitter.get_out_going_vertices(
×
118
                partition.identifier):
119
            key = routing_infos.get_first_key_from_pre_vertex(
×
120
                vertex, partition.identifier)
121
            if key is not None:
×
122
                key_to_n_atoms_map[key] = vertex.vertex_slice.n_atoms
×
123
    return key_to_n_atoms_map
×
124

125

126
class _BitFieldData(object):
1✔
127
    __slots__ = (
1✔
128
        # bit_field data
129
        "bit_field",
130
        # Key this applies to
131
        "master_pop_key",
132
        # address of n_atoms word to wrote back merged flags
133
        "n_atoms_address",
134
        # Word that holds merged: 1; all_ones: 1; n_atoms: 30;
135
        "n_atoms_word",
136
        # P coordinate of processor this applies to
137
        "processor_id",
138
        # index of this entry in the "sorted_list"
139
        "sort_index",
140
        # The shift to get the core id
141
        "core_shift",
142
        # The number of atoms per core
143
        "n_atoms_per_core")
144

145
    def __init__(self, processor_id: int, bit_field: Tuple[int, ...],
1✔
146
                 master_pop_key: int, n_atoms_address: int, n_atoms_word: int,
147
                 core_shift: int, n_atoms_per_core: int):
148
        self.processor_id = processor_id
1✔
149
        self.bit_field = bit_field
1✔
150
        self.master_pop_key = master_pop_key
1✔
151
        self.n_atoms_address = n_atoms_address
1✔
152
        self.n_atoms_word = n_atoms_word
1✔
153
        self.core_shift = core_shift
1✔
154
        self.n_atoms_per_core = n_atoms_per_core
1✔
155
        self.sort_index = 0
1✔
156

157
    def __str__(self) -> str:
1✔
158
        return f"{self.processor_id} {self.master_pop_key} {self.bit_field}"
×
159

160
    def bit_field_as_bit_array(self) -> List[int]:
1✔
161
        """
162
        Convert the bitfield into an array of bits like bit_field.c
163
        """
164
        return [(word >> i) & 1
1!
165
                for word in self.bit_field
166
                for i in range(32)]
167

168

169
class HostBasedBitFieldRouterCompressor(object):
1✔
170
    """
171
    Host-based fancy router compressor using the bitfield filters of the
172
    cores. Compresses bitfields and router table entries together as
173
    much as feasible.
174
    """
175

176
    __slots__ = (
1✔
177
        # List of the entries from the highest successful midpoint
178
        "_best_routing_entries",
179
        # The highest successful midpoint
180
        "_best_midpoint",
181
        # Dict of lists of the original bitfields by key
182
        "_bit_fields_by_key",
183
        # Record of which midpoints where tired and with what result
184
        "_compression_attempts",
185
        # Number of bitfields for this core
186
        "_n_bitfields",
187
        # Mapping from base keys to size of vertex that they were issued for
188
        "__key_atom_map",
189
        # Mapping from chip to processor ID to count of incoming on processor
190
        "__core_cost_map",
191
        # Where, if anywhere, reports on compression will be written
192
        "__report_folder")
193

194
    # max entries that can be used by the application code
195
    _MAX_SUPPORTED_LENGTH = 1023
1✔
196

197
    # the amount of time each attempt at router compression can be allowed to
198
    #  take (in seconds)
199
    # _DEFAULT_TIME_PER_ITERATION = 5 * 60
200
    _DEFAULT_TIME_PER_ITERATION = 10
1✔
201

202
    # report name
203
    _REPORT_NAME = "router_{}_{}.rpt"
1✔
204

205
    # key id for the initial entry
206
    _ORIGINAL_ENTRY = 0
1✔
207

208
    # key id for the bitfield entries
209
    _ENTRIES = 1
1✔
210

211
    # size of a filter info in bytes (key, n words, pointer)
212
    _SIZE_OF_FILTER_INFO_IN_BYTES = 12
1✔
213

214
    # bit to mask a bit
215
    _BIT_MASK = 1
1✔
216

217
    # mask for neuron level
218
    _NEURON_LEVEL_MASK = 0xFFFFFFFF
1✔
219

220
    # to remove the first   merged: 1; and all_ones: 1;
221
    N_ATOMS_MASK = 0x3FFFFFFF
1✔
222
    MERGED_SETTER = 0x80000000
1✔
223

224
    # struct for performance requirements.
225
    _FOUR_WORDS = struct.Struct("<IIII")
1✔
226

227
    # for router report
228
    _LOWER_16_BITS = 0xFFFF
1✔
229

230
    # threshold starting point at 1ms time step
231
    _N_PACKETS_PER_SECOND = 100000
1✔
232

233
    # convert between milliseconds and second
234
    _MS_TO_SEC = 1000
1✔
235

236
    # fraction effect for threshold
237
    _THRESHOLD_FRACTION_EFFECT = 2
1✔
238

239
    # Number of bits per word as an int
240
    _BITS_PER_WORD = 32
1✔
241

242
    def __init__(self, key_to_n_atom_map: Dict[int, int],
1✔
243
                 report_folder_path: Optional[str],
244
                 most_costly_cores: Dict[Chip, Dict[int, int]]):
245
        """
246
        :param dict(int,int) key_to_n_atom_map: key to atoms map
247
        :param report_folder_path: the report folder base address
248
        :type report_folder_path: str or None
249
        :param dict(Chip,dict(int,int)) most_costly_cores:
250
            Map of chip to processors to count of incoming on processor
251
        """
252
        self._best_routing_entries: List[RTEntry] = []
×
253
        self._best_midpoint = -1
×
254
        self._bit_fields_by_key: Dict[int, List[_BitFieldData]] = {}
×
255
        self._compression_attempts: Dict[int, str] = dict()
×
256
        self._n_bitfields: int = 0
×
257
        self.__key_atom_map = key_to_n_atom_map
×
258
        self.__core_cost_map = most_costly_cores
×
259
        self.__report_folder = report_folder_path
×
260

261
    def get_bit_field_sdram_base_addresses(
1✔
262
            self, chip_x: int, chip_y: int) -> Dict[int, int]:
263
        """
264
        :param int chip_x:
265
        :param int chip_y:
266
        """
267
        # locate the bitfields in a chip level scope
268
        base_addresses: Dict[int, int] = dict()
×
269
        for placement in FecDataView.iterate_placements_by_xy_and_type(
×
270
                chip_x, chip_y, AbstractSupportsBitFieldRoutingCompression):
271
            vertex = cast(
×
272
                AbstractSupportsBitFieldRoutingCompression, placement.vertex)
273
            base_addresses[placement.p] = vertex.bit_field_base_address(
×
274
                FecDataView.get_placement_of_vertex(placement.vertex))
275
        return base_addresses
×
276

277
    def compress_bitfields(
1✔
278
            self, router_table: AbstractMulticastRoutingTable,
279
            compressed_pacman_router_tables: MulticastRoutingTables):
280
        """
281
        Entrance method for doing on host compression. Can be used as a
282
        public method for other compressors.
283

284
        :param router_table: the routing table in question to compress
285
        :type router_table:
286
            ~pacman.model.routing_tables.UnCompressedMulticastRoutingTable
287
        :param compressed_pacman_router_tables:
288
            a data holder for compressed tables
289
        :type compressed_pacman_router_tables:
290
            ~pacman.model.routing_tables.MulticastRoutingTables
291
        """
292
        # Init the per-attempt data
293
        self._best_routing_entries = []
×
294
        self._best_midpoint = -1
×
295
        self._bit_fields_by_key = {}
×
296
        self._compression_attempts = {}
×
297
        self._n_bitfields = 0
×
298
        # Find the processors that have bitfield data and where it is
299
        bit_field_chip_base_addresses = (
×
300
            self.get_bit_field_sdram_base_addresses(
301
                router_table.x, router_table.y))
302

303
        # read in bitfields.
304
        self._read_in_bit_fields(
×
305
            router_table.x, router_table.y, bit_field_chip_base_addresses)
306

307
        # execute binary search
308
        self._start_binary_search(router_table)
×
309

310
        # add final to compressed tables:
311
        # self._best_routing_table is a list of entries
312
        best_router_table = CompressedMulticastRoutingTable(
×
313
            router_table.x, router_table.y)
314

315
        if self._best_routing_entries:
×
316
            for entry in self._best_routing_entries:
×
317
                best_router_table.add_multicast_routing_entry(
×
318
                    entry.to_multicast_routing_entry())
319

320
        compressed_pacman_router_tables.add_routing_table(best_router_table)
×
321

322
        # remove bitfields from cores that have been merged into the
323
        # router table
324
        self._remove_merged_bitfields_from_cores(
×
325
            router_table.x, router_table.y)
326

327
        # create report file if required
328
        if self.__report_folder:
×
329
            report_file_path = os.path.join(
×
330
                self.__report_folder,
331
                self._REPORT_NAME.format(router_table.x, router_table.y))
332
            with open(report_file_path, "w", encoding="utf-8") as report_out:
×
333
                self._create_table_report(router_table, report_out)
×
334

335
        generate_provenance_item(
×
336
            router_table.x, router_table.y, self._best_midpoint)
337

338
    def _convert_bitfields_into_router_table(
1✔
339
            self, router_table: AbstractMulticastRoutingTable,
340
            mid_point: int) -> UnCompressedMulticastRoutingTable:
341
        """
342
        Converts the bitfield into router table entries for compression,
343
        based off the entry located in the original router table.
344

345
        :param router_table: the original routing table
346
        :type router_table:
347
            ~pacman.model.routing_tables.UnCompressedMulticastRoutingTable
348
        :param int mid_point: cut-off for bitfields to use
349
        :return: routing tables.
350
        :rtype: ~pacman.model.routing_tables.AbstractMulticastRoutingTable
351
        """
352
        new_table = UnCompressedMulticastRoutingTable(
×
353
            router_table.x, router_table.y)
354

355
        # go through the routing tables and convert when needed
356
        for original_entry in router_table.multicast_routing_entries:
×
357
            base_key = original_entry.routing_entry_key
×
358
            if base_key not in self._bit_fields_by_key:
×
359
                continue
×
360
            n_neurons = self.__key_atom_map[base_key]
×
361
            entry_links = original_entry.link_ids
×
362
            # Assume all neurons for each processor to be kept
363
            all_mask = [1] * n_neurons
×
364
            core_map = {
×
365
                processor_id: all_mask
366
                for processor_id in original_entry.processor_ids}
367
            # For those below the midpoint use the bitfield data
368
            for bf_data in self._bit_fields_by_key[base_key]:
×
369
                if bf_data.sort_index < mid_point:
×
370
                    core_map[bf_data.processor_id] = (
×
371
                        bf_data.bit_field_as_bit_array())
372
            # Add an Entry for each neuron
373
            for neuron in range(n_neurons):
×
374
                # build new entry for this neuron and add to table
375
                new_table.add_multicast_routing_entry(MulticastRoutingEntry(
×
376
                    routing_entry_key=base_key + neuron,
377
                    mask=self._NEURON_LEVEL_MASK, link_ids=entry_links,
378
                    defaultable=False, processor_ids=(
379
                        processor_id
380
                        for processor_id in original_entry.processor_ids
381
                        if core_map[processor_id][neuron])))
382

383
        # return the bitfield tables and the reduced original table
384
        return new_table
×
385

386
    def _bit_for_neuron_id(self, bit_field: List[int], neuron_id: int) -> int:
1✔
387
        """
388
        Locate the bit for the neuron in the bitfield.
389

390
        :param list(int) bit_field:
391
            the block of words which represent the bitfield
392
        :param int neuron_id: the neuron id to find the bit in the bitfield
393
        :return: the bit
394
        """
395
        word_id, bit_in_word = divmod(neuron_id, self._BITS_PER_WORD)
×
396
        return (bit_field[word_id] >> bit_in_word) & self._BIT_MASK
×
397

398
    def _read_in_bit_fields(
1✔
399
            self, chip_x: int, chip_y: int,
400
            chip_base_addresses: Dict[int, int]):
401
        """
402
        Read in the bitfields from the cores.
403

404
        :param int chip_x: chip x coordinate
405
        :param int chip_y: chip y coordinate
406
        :param dict(int,int) chip_base_addresses:
407
            maps core id to base address of its bitfields
408
        """
409
        self._bit_fields_by_key = defaultdict(list)
×
410
        bit_fields_by_coverage = defaultdict(list)
×
411
        processor_coverage_by_bitfield = defaultdict(list)
×
412

413
        # read in for each app vertex that would have a bitfield
414
        for processor_id, base_address in chip_base_addresses.items():
×
415
            # from filter_region_t read how many bitfields there are
416
            # n_filters then array of filters
417
            n_filters = FecDataView.get_transceiver().read_word(
×
418
                chip_x, chip_y, base_address)
419
            reading_address = base_address + BYTES_PER_WORD
×
420

421
            # read in each bitfield
422
            for _ in range(n_filters):
×
423
                master_pop_key, n_atoms_word, n_per_core_word, read_pointer = \
×
424
                    self._FOUR_WORDS.unpack(FecDataView.read_memory(
425
                        chip_x, chip_y, reading_address, BYTES_PER_4_WORDS))
426
                n_atoms_address = reading_address + BYTES_PER_WORD
×
427
                reading_address += BYTES_PER_4_WORDS
×
428

429
                # merged: 1; all_ones: 1; n_atoms: 30;
430
                atoms = n_atoms_word & self.N_ATOMS_MASK
×
431
                # get bitfield words
432
                n_words_to_read = math.ceil(atoms / self._BITS_PER_WORD)
×
433

434
                bit_field = n_word_struct(n_words_to_read).unpack(
×
435
                    FecDataView.read_memory(
436
                        chip_x, chip_y, read_pointer,
437
                        n_words_to_read * BYTES_PER_WORD))
438

439
                # sorted by best coverage of redundant packets
440
                data = _BitFieldData(
×
441
                    processor_id, bit_field, master_pop_key, n_atoms_address,
442
                    n_atoms_word, n_per_core_word & 0x1F, n_per_core_word >> 5)
443
                as_array = data.bit_field_as_bit_array()
×
444
                # Number of fields in the array that are zero instead of one
445
                n_redundant_packets = len(as_array) - sum(as_array)
×
446

447
                bit_fields_by_coverage[n_redundant_packets].append(data)
×
448
                processor_coverage_by_bitfield[processor_id].append(
×
449
                    n_redundant_packets)
450

451
                # add to the bitfields tracker
452
                self._bit_fields_by_key[master_pop_key].append(data)
×
453

454
        # use the ordered process to find the best ones to do first
455
        self._order_bit_fields(
×
456
            bit_fields_by_coverage,
457
            FecDataView.get_chip_at(chip_x, chip_y),
458
            processor_coverage_by_bitfield)
459

460
    def _order_bit_fields(
1✔
461
            self, bit_fields_by_coverage: Dict[int, List[_BitFieldData]],
462
            chip: Chip, processor_coverage_by_bitfield: Dict[int, List[int]]):
463
        """
464
        Order the bit fields by redundancy setting the sorted index.
465

466
        Also counts the bitfields setting _n_bitfields.
467

468
        :param dict(int,list(_BitFieldData)) bit_fields_by_coverage:
469
        :param Chip chip:
470
        :param dict(int,list(int)) processor_coverage_by_bitfield:
471
        """
472
        sort_index = 0
×
473

474
        # get cores that are the most likely to have the worst time and order
475
        #  bitfields accordingly
476
        cores = sorted(
×
477
            self.__core_cost_map[chip].keys(),
478
            key=lambda k: self.__core_cost_map[chip][k],
479
            reverse=True)
480

481
        # only add bit fields from the worst affected cores, which will
482
        # build as more and more are taken to make the worst a collection
483
        # instead of a individual
484
        cores_to_add_for: OrderedSet[int] = OrderedSet()
×
485
        for worst_core_idx in range(len(cores) - 1):
×
486
            worst_core = cores[worst_core_idx]
×
487
            # determine how many of the worst to add before it balances with
488
            # next one
489
            cores_to_add_for.add(worst_core)
×
490
            diff = (
×
491
                self.__core_cost_map[chip][worst_core] -
492
                self.__core_cost_map[chip][cores[worst_core_idx + 1]])
493

494
            # order over most effective bitfields
495
            coverage = processor_coverage_by_bitfield[worst_core]
×
496
            coverage.sort(reverse=True)
×
497

498
            # cycle till at least the diff is covered
499
            covered = 0
×
500
            for redundant_count in coverage:
×
501
                to_delete: List[_BitFieldData] = list()
×
502
                for bit_field_data in bit_fields_by_coverage[redundant_count]:
×
503
                    if bit_field_data.processor_id in cores_to_add_for:
×
504
                        if covered < diff:
×
505
                            bit_field_data.sort_index = sort_index
×
506
                            sort_index += 1
×
507
                            to_delete.append(bit_field_data)
×
508
                            covered += 1
×
509
                for item in to_delete:
×
510
                    bit_fields_by_coverage[redundant_count].remove(item)
×
511

512
        # take left overs
513
        for coverage_level in sorted(
×
514
                bit_fields_by_coverage.keys(), reverse=True):
515
            for bit_field_data in bit_fields_by_coverage[coverage_level]:
×
516
                bit_field_data.sort_index = sort_index
×
517
                sort_index += 1
×
518

519
        self._n_bitfields = sort_index
×
520

521
    def _start_binary_search(
1✔
522
            self, router_table: AbstractMulticastRoutingTable):
523
        """
524
        Start binary search of the merging of bitfield to router table.
525

526
        :param ~.UnCompressedMulticastRoutingTable router_table:
527
            uncompressed router table
528
        """
529
        # try first just uncompressed. so see if its possible
530
        try:
×
531
            self._best_routing_entries = self._run_algorithm(router_table)
×
532
            self._best_midpoint = 0
×
533
            self._compression_attempts[0] = "success"
×
534
        except MinimisationFailedError as e:
×
535
            raise PacmanAlgorithmFailedToGenerateOutputsException(
×
536
                "host bitfield router compressor can't compress the "
537
                "uncompressed routing tables, regardless of bitfield merging. "
538
                "System is fundamentally flawed here") from e
539

540
        find_max_success(self._n_bitfields, functools.partial(
×
541
            self._binary_search_check, routing_table=router_table))
542

543
    def _binary_search_check(
1✔
544
            self, mid_point: int,
545
            routing_table: AbstractMulticastRoutingTable) -> bool:
546
        """
547
        Check function for fix max success.
548

549
        :param int mid_point: the point if the list to stop at
550
        :param ~.UnCompressedMulticastRoutingTable routing_table:
551
            the basic routing table
552
        :return: true if it compresses
553
        :rtype: bool
554
        """
555
        # convert bitfields into router tables
556
        bit_field_router_table = self._convert_bitfields_into_router_table(
×
557
            routing_table, mid_point)
558

559
        # try to compress
560
        try:
×
561
            self._best_routing_entries = self._run_algorithm(
×
562
                bit_field_router_table)
563
            self._best_midpoint = mid_point
×
564
            self._compression_attempts[mid_point] = "success"
×
565
            return True
×
566
        except MinimisationFailedError:
×
567
            self._compression_attempts[mid_point] = "fail"
×
568
            return False
×
569
        except PacmanElementAllocationException:
×
570
            self._compression_attempts[mid_point] = "Exception"
×
571
            return False
×
572

573
    def _run_algorithm(
1✔
574
            self, router_table: AbstractMulticastRoutingTable
575
            ) -> List[RTEntry]:
576
        """
577
        Attempts to covert the mega router tables into 1 router table.
578

579
        :param list(~.AbstractMulticastRoutingTable) router_table:
580
            the set of router tables that together need to
581
            be merged into 1 router table
582
        :return: compressor router table
583
        :rtype: list(~.RoutingTableEntry)
584
        :throws MinimisationFailedError: if it fails to
585
            compress to the correct length.
586
        """
587
        compressor = _PairCompressor(ordered=True)
×
588
        compressed_entries = compressor.compress_table(router_table)
×
589
        chip = FecDataView.get_chip_at(router_table.x, router_table.y)
×
590
        if len(compressed_entries) > chip.n_user_processors:
×
591
            raise MinimisationFailedError(
×
592
                f"Compression failed as {len(compressed_entries)} "
593
                f"entries found")
594
        return compressed_entries
×
595

596
    def _remove_merged_bitfields_from_cores(self, chip_x: int, chip_y: int):
1✔
597
        """
598
        Goes to SDRAM and removes said merged entries from the cores'
599
        bitfield region.
600

601
        :param int chip_x: the chip x coordinate from which this happened
602
        :param int chip_y: the chip y coordinate from which this happened
603
        """
604
        assert self._bit_fields_by_key is not None
×
605
        for entries in self._bit_fields_by_key.values():
×
606
            for entry in entries:
×
607
                if entry.sort_index < self._best_midpoint:
×
608
                    # set merged
609
                    n_atoms_word = entry.n_atoms_word | self.MERGED_SETTER
×
610
                    FecDataView.write_memory(
×
611
                        chip_x, chip_y, entry.n_atoms_address, n_atoms_word)
612

613
    def _create_table_report(
1✔
614
            self, router_table: AbstractMulticastRoutingTable, report_out):
615
        """
616
        Create the report entry.
617

618
        :param ~.AbstractMulticastRoutingTable router_table:
619
            the uncompressed router table to process
620
        :param ~io.TextIOBase report_out: the report writer
621
        """
622
        n_bit_fields_merged = 0
×
623
        n_packets_filtered = 0
×
624
        n_possible_bit_fields = 0
×
625
        merged_by_core = defaultdict(list)
×
626

627
        for key in self._bit_fields_by_key:
×
628
            for bf_data in self._bit_fields_by_key[key]:
×
629
                n_possible_bit_fields += 1
×
630
                if bf_data.sort_index >= self._best_midpoint:
×
631
                    continue
×
632
                n_bit_fields_merged += 1
×
633
                as_array = bf_data.bit_field_as_bit_array()
×
634
                n_packets_filtered += sum(as_array)
×
635
                merged_by_core[bf_data.processor_id].append(bf_data)
×
636

637
        percentage_done = 100.0
×
638
        if n_possible_bit_fields != 0:
×
639
            percentage_done = (
×
640
                (100.0 / n_possible_bit_fields) * n_bit_fields_merged)
641

642
        report_out.write(
×
643
            f"\nTable {router_table.x}:{router_table.y} has integrated "
644
            f"{n_bit_fields_merged} out of {n_possible_bit_fields} available "
645
            "chip level bitfields into the routing table, thereby producing a "
646
            f"compression of {percentage_done}%.\n\n")
647

648
        report_out.write(
×
649
            "The uncompressed routing table had "
650
            f"{router_table.number_of_entries} entries, the compressed "
651
            f"one with {n_bit_fields_merged} integrated bitfields has "
652
            f"{len(self._best_routing_entries)} entries.\n\n")
653

654
        report_out.write(
×
655
            f"The integration of {n_bit_fields_merged} bitfields removes up "
656
            f"to {n_packets_filtered} MC packets that otherwise would be "
657
            "being processed by the cores on the chip, just to be dropped as "
658
            "they do not target anything.\n\n")
659

660
        report_out.write("The compression attempts are as follows:\n\n")
×
661
        for mid_point, result in self._compression_attempts.items():
×
662
            report_out.write(f"Midpoint {mid_point}: {result}\n")
×
663

664
        report_out.write("\nThe bit_fields merged are as follows:\n\n")
×
665

666
        for core in merged_by_core:
×
667
            for bf_data in merged_by_core[core]:
×
668
                report_out.write(
×
669
                    f"bitfield on core {core} for "
670
                    f"key {bf_data.master_pop_key}\n")
671

672
        report_out.write("\n\n\n")
×
673
        report_out.write("The final routing table entries are as follows:\n\n")
×
674

675
        report_out.write(
×
676
            f'{"Index": <5s} {"Key": <10s} {"Mask": <10s} {"Route": <10s} '
677
            f'#{"Default": <7s} [Cores][Links]\n')
UNCOV
678
        report_out.write(
×
679
            f"{'':-<5s} {'':-<10s} {'':-<10s} {'':-<10s} "
680
            f"{'':-<7s} {'':-<14s}\n")
681

682
        entry_count = 0
×
683
        n_defaultable = 0
×
684
        # Note: _best_routing_table is a list(), router_table is not
685
        for entry in self._best_routing_entries:
×
686
            index = entry_count & self._LOWER_16_BITS
×
NEW
687
            entry_str = format_route(entry.to_multicast_routing_entry())
×
688
            entry_count += 1
×
689
            if entry.defaultable:
×
690
                n_defaultable += 1
×
691
            report_out.write(f"{index:>5d} {entry_str}\n")
×
692
        report_out.write(f"{n_defaultable} Defaultable entries\n")
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc