Skip to content

Commit

Permalink
virtual_machine_by
Browse files Browse the repository at this point in the history
  • Loading branch information
Christian-B committed Apr 9, 2024
1 parent b9b6c77 commit 7384e71
Show file tree
Hide file tree
Showing 6 changed files with 151 additions and 74 deletions.
6 changes: 4 additions & 2 deletions spinn_machine/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,10 +85,12 @@
from .multicast_routing_entry import MulticastRoutingEntry
from .router import Router
from .spinnaker_triad_geometry import SpiNNakerTriadGeometry
from .virtual_machine import virtual_machine
from .virtual_machine import (
virtual_machine, virtual_machine_by_boards, virtual_machine_by_cores)
from .fixed_route_entry import FixedRouteEntry


__all__ = ["Chip", "CoreSubset", "CoreSubsets", "FixedRouteEntry",
"FrozenCoreSubsets", "Link", "Machine", "MulticastRoutingEntry",
"Router", "SpiNNakerTriadGeometry", "virtual_machine"]
"Router", "SpiNNakerTriadGeometry", "virtual_machine",
"virtual_machine_by_boards", "virtual_machine_by_cores"]
5 changes: 2 additions & 3 deletions spinn_machine/data/machine_data_writer.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
from spinn_utilities.data.utils_data_writer import UtilsDataWriter
from spinn_utilities.overrides import overrides
from spinn_utilities.log import FormatAdapter
from spinn_machine import Machine, virtual_machine
from spinn_machine import Machine, virtual_machine_by_boards
from .machine_data_view import MachineDataView, _MachineDataModel
logger = FormatAdapter(logging.getLogger(__name__))
__temp_dir = None
Expand Down Expand Up @@ -46,8 +46,7 @@ def _mock_machine(self) -> None:
"""
Method to create a virtual machine in mock mode.
"""
width, height = self.get_machine_version().board_shape
self.set_machine(virtual_machine(width=width, height=height))
self.set_machine(virtual_machine_by_boards(1))

@overrides(UtilsDataWriter._setup)
def _setup(self) -> None:
Expand Down
2 changes: 1 addition & 1 deletion spinn_machine/version/abstract_version.py
Original file line number Diff line number Diff line change
Expand Up @@ -357,7 +357,7 @@ def size_from_n_cores(self, n_cores: int) -> Tuple[int, int]:
"""
Returns the size needed to support this many cores.
Takes into consideration monitor cores.
Takes into consideration scamp and monitor cores.
Designed for use with virtual boards.
Does not include a safety factor for blacklisted boards.
Expand Down
118 changes: 76 additions & 42 deletions spinn_machine/virtual_machine.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import math
from collections import defaultdict
import logging
from typing import Dict, List, Optional, overload, Set, Tuple
Expand All @@ -28,40 +28,92 @@
logger = FormatAdapter(logging.getLogger(__name__))


@overload
def virtual_machine(
width: int, height: int,
validate: bool = True, n_cores: None = None):
...
def virtual_machine(width: int, height: int, validate: bool = True):
"""
Create a virtual SpiNNaker machine, used for planning execution.
:param int width: the width of the virtual machine in chips
:param int height: the height of the virtual machine in chips
:param bool validate: if True will call the machine validate function
@overload
def virtual_machine(
width: None = None, height: None = None,
validate: bool = True, n_cores: int = 0):
...
:returns: a virtual machine (that cannot execute code)
:rtype: ~spinn_machine.Machine
"""
factory = _VirtualMachine(width, height, validate)
return factory.machine


def virtual_machine(
width: Optional[int] = None, height: Optional[int] = None,
validate: bool = True, n_cores: Optional[int] = None):
def virtual_machine_by_min_size(width: int, height: int, validate: bool = True):
"""
Create a virtual SpiNNaker machine, used for planning execution.
:param width: the width of the virtual machine in chips
:type width: int or None
:param height: the height of the virtual machine in chips
:type height: int or None
:param int width: the minimum width of the virtual machine in chips
:param int height: the minimum height of the virtual machine in chips
:param bool validate: if True will call the machine validate function
:param n_cores:
If provided will be used to calculate the size of Machine needed.
In which case width and height are ignored!
:returns: a virtual machine (that cannot execute code)
:rtype: ~spinn_machine.Machine
"""
factory = _VirtualMachine(width, height, validate, n_cores)
return factory.machine
version = MachineDataView.get_machine_version()
w_board, h_board = version.board_shape
# check for edge case
if width <= w_board and height > h_board:
width = w_board * 2
if height <= h_board and width > w_board:
height = h_board * 2
width = w_board * math.ceil(width / w_board)
height = h_board * math.ceil(height / h_board)
return virtual_machine(width, height, validate)


def virtual_machine_by_cores(n_cores: int, validate: bool = True):
"""
Create a virtual SpiNNaker machine, used for planning execution.
Semantic sugar for
MachineDataView.get_machine_version()
width, height = version.size_from_n_cores(n_cores)
return virtual_machine(width, height, validate)
:param n_cores: Minimum number of user cores
:param bool validate: if True will call the machine validate function
:returns: a virtual machine (that cannot execute code)
:rtype: ~spinn_machine.Machine
:raises SpinnMachineException:
If multiple boards are needed but not supported
"""
version = MachineDataView.get_machine_version()
width, height = version.size_from_n_cores(n_cores)
return virtual_machine(width, height, validate)


def virtual_machine_by_boards(n_boards: int, validate: bool = True):
"""
Create a virtual SpiNNaker machine, used for planning execution.
semantic sugar for:
version = MachineDataView.get_machine_version()
width, height = version.size_from_n_boards(n_boards)
return virtual_machine(width, height, validate)
:param n_boards: Minimum number of boards
:param bool validate: if True will call the machine validate function
:returns: a virtual machine (that cannot execute code)
:rtype: ~spinn_machine.Machine
:raises SpinnMachineException:
If multiple boards are needed but not supported
"""
version = MachineDataView.get_machine_version()
width, height = version.size_from_n_boards(n_boards)
return virtual_machine(width, height, validate)


class _VirtualMachine(object):
Expand All @@ -84,27 +136,9 @@ class _VirtualMachine(object):

ORIGIN = "Virtual"

@overload
def __init__(
self, width: int, height: int,
validate: bool = True, n_cores: None = None):
...

@overload
def __init__(
self, width: None = None, height: None = None,
validate: bool = True, n_cores: int = 0):
...

def __init__(
self, width: Optional[int] = None, height: Optional[int] = None,
validate: bool = True, n_cores: Optional[int] = None):
def __init__(self, width: int, height: int, validate: bool = True):
version = MachineDataView.get_machine_version()
if n_cores:
width, height = version.size_from_n_cores(n_cores)
version.verify_size(width, height)
assert width is not None
assert height is not None
max_cores = version.max_cores_per_chip
self._n_router_entries = version.n_router_entries
self._machine = version.create_machine(
Expand Down
25 changes: 13 additions & 12 deletions unittests/test_json_machine.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
from tempfile import mktemp
import unittest
from spinn_utilities.config_holder import set_config
from spinn_machine import virtual_machine
from spinn_machine.virtual_machine import (
virtual_machine_by_boards, virtual_machine_by_min_size)
from spinn_machine.data.machine_data_writer import MachineDataWriter
from spinn_machine.config_setup import unittest_setup
from spinn_machine.json_machine import (machine_from_json, to_json_path)
Expand All @@ -29,7 +30,7 @@ def setUp(self):

def test_json_version_5_hole(self):
set_config("Machine", "down_chips", "3,3")
vm = virtual_machine(width=8, height=8)
vm = virtual_machine_by_min_size(width=8, height=8)
MachineDataWriter.mock().set_machine(vm)
jpath = mktemp("json")
to_json_path(jpath)
Expand All @@ -41,7 +42,7 @@ def test_json_version_5_hole(self):
self.assertEqual(str(vchip), str(jchip))

def test_exceptions(self):
vm = virtual_machine(width=8, height=8)
vm = virtual_machine_by_min_size(width=8, height=8)
MachineDataWriter.mock().set_machine(vm)
chip22 = vm[2, 2]
router22 = chip22.router
Expand Down Expand Up @@ -73,21 +74,21 @@ def test_monitor_exceptions(self):
machine_from_json(jpath)

def test_ethernet_exceptions(self):
vm = virtual_machine(width=16, height=16)
vm = virtual_machine_by_boards(2)
MachineDataWriter.mock().set_machine(vm)
chip48 = vm[4, 8]
router48 = chip48.router
router48._n_available_multicast_entries = \
router48._n_available_multicast_entries - 20
chip48._sdram = 50000000
chip48._tag_ids = [2, 3]
eth2 = vm.ethernet_connected_chips[1]
router2 = eth2.router
router2._n_available_multicast_entries = \
router2._n_available_multicast_entries - 20
eth2._sdram = 50000000
eth2._tag_ids = [2, 3]
jpath = mktemp("json")
to_json_path(jpath)
jm = machine_from_json(jpath)
for vchip, jchip in zip(vm, jm):
self.assertEqual(str(vchip), str(jchip))
vchip48 = jm[4, 8]
self.assertEqual(vchip48.tag_ids, chip48.tag_ids)
jeth2 = jm.ethernet_connected_chips[1]
self.assertEqual(jeth2.tag_ids, eth2.tag_ids)


if __name__ == '__main__':
Expand Down
69 changes: 55 additions & 14 deletions unittests/test_virtual_machine.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@
import unittest
from spinn_utilities.config_holder import set_config
from spinn_machine.config_setup import unittest_setup
from spinn_machine import Chip, Link, Router, virtual_machine
from spinn_machine import (Chip, Link, Router, virtual_machine,
virtual_machine_by_boards, virtual_machine_by_cores)
from spinn_machine.virtual_machine import virtual_machine_by_min_size
from spinn_machine.data import MachineDataView
from spinn_machine.exceptions import (
SpinnMachineException, SpinnMachineAlreadyExistsException)
Expand Down Expand Up @@ -1089,26 +1091,34 @@ def test_n_cores_2_2(self):

def test_2_2_by_cores(self):
set_config("Machine", "version", 2)
machine = virtual_machine(n_cores=40)
n_cores = 40
machine = virtual_machine_by_cores(n_cores)
self.assertEqual(4, machine.n_chips)
self.assertEqual(2, machine.width)
self.assertEqual(2, machine.height)
self.assertGreaterEqual(machine.total_available_user_cores, n_cores)
machine2 = virtual_machine_by_boards(1)
self.assertEqual(4, machine2.n_chips)
self.assertEqual(2, machine2.width)
self.assertEqual(2, machine2.height)

def test_2_2_by_cores_too_many(self):
set_config("Machine", "version", 2)
version = MachineDataView.get_machine_version()
n_cores = sum(version.chip_core_map.values())
n_cores -= version.n_chips_per_board
with self.assertRaises(SpinnMachineException):
machine = virtual_machine(n_cores=100)
self.assertEqual(2, machine.width)
self.assertEqual(2, machine.height)
self.assertEqual(n_cores, machine.total_available_user_cores)
virtual_machine_by_cores(100)
with self.assertRaises(SpinnMachineException):
virtual_machine_by_boards(2)

def test_8_8_by_cores_1_board(self):
set_config("Machine", "version", 5)
version = MachineDataView.get_machine_version()
n_cores = sum(version.chip_core_map.values())
n_cores -= version.n_chips_per_board
machine = virtual_machine(n_cores=n_cores)
machine = virtual_machine_by_cores(n_cores)
self.assertEqual(8, machine.width)
self.assertEqual(8, machine.height)
self.assertEqual(n_cores, machine.total_available_user_cores)
machine = virtual_machine_by_boards(1)
self.assertEqual(8, machine.width)
self.assertEqual(8, machine.height)
self.assertEqual(n_cores, machine.total_available_user_cores)
Expand All @@ -1118,7 +1128,12 @@ def test_8_8_by_cores_3_boards(self):
version = MachineDataView.get_machine_version()
n_cores = sum(version.chip_core_map.values())
n_cores -= version.n_chips_per_board
machine = virtual_machine(n_cores=n_cores * 2)
machine = virtual_machine_by_cores(n_cores * 2)
# despite asking for two boards you get a triad
self.assertEqual(16, machine.width)
self.assertEqual(16, machine.height)
self.assertEqual(n_cores*3, machine.total_available_user_cores)
machine = virtual_machine_by_boards(2)
# despite asking for two boards you get a triad
self.assertEqual(16, machine.width)
self.assertEqual(16, machine.height)
Expand All @@ -1129,7 +1144,11 @@ def test_8_8_by_cores_6_boards(self):
version = MachineDataView.get_machine_version()
n_cores = sum(version.chip_core_map.values())
n_cores -= version.n_chips_per_board
machine = virtual_machine(n_cores=n_cores * 5)
machine = virtual_machine_by_cores(n_cores * 5)
self.assertEqual(28, machine.width)
self.assertEqual(16, machine.height)
self.assertEqual(n_cores * 6, machine.total_available_user_cores)
machine = virtual_machine_by_boards(4)
self.assertEqual(28, machine.width)
self.assertEqual(16, machine.height)
self.assertEqual(n_cores * 6, machine.total_available_user_cores)
Expand All @@ -1139,7 +1158,11 @@ def test_8_8_by_cores_12_boards(self):
version = MachineDataView.get_machine_version()
n_cores = sum(version.chip_core_map.values())
n_cores -= version.n_chips_per_board
machine = virtual_machine(n_cores=n_cores * 9)
machine = virtual_machine_by_cores(n_cores * 9)
self.assertEqual(28, machine.width)
self.assertEqual(28, machine.height)
self.assertEqual(n_cores * 12, machine.total_available_user_cores)
machine = virtual_machine_by_boards(10)
self.assertEqual(28, machine.width)
self.assertEqual(28, machine.height)
self.assertEqual(n_cores * 12, machine.total_available_user_cores)
Expand All @@ -1149,11 +1172,29 @@ def test_8_8_by_cores_18_boards(self):
version = MachineDataView.get_machine_version()
n_cores = sum(version.chip_core_map.values())
n_cores -= version.n_chips_per_board
machine = virtual_machine(n_cores=n_cores * 12 + 1)
machine = virtual_machine_by_cores(n_cores * 12 + 1)
self.assertEqual(40, machine.width)
self.assertEqual(28, machine.height)
self.assertEqual(n_cores * 18, machine.total_available_user_cores)
machine = virtual_machine_by_boards(15)
self.assertEqual(40, machine.width)
self.assertEqual(28, machine.height)
self.assertEqual(n_cores * 18, machine.total_available_user_cores)

def test_by_min_size(self):
set_config("Machine", "version", 5)
machine = virtual_machine_by_min_size(15, 21)
self.assertGreaterEqual(machine.width, 15)
self.assertGreaterEqual(machine.height, 21)

def test_by_min_size_edge_case(self):
set_config("Machine", "version", 5)
version = MachineDataView.get_machine_version()
width, height = version.board_shape
machine = virtual_machine_by_min_size(width, height + 1)
self.assertGreaterEqual(machine.width, width)
self.assertGreaterEqual(machine.height, height + 1)


if __name__ == '__main__':
unittest.main()

0 comments on commit 7384e71

Please sign in to comment.