From 12174f6c3efd61736c14ccc4e57377caeb3f98db Mon Sep 17 00:00:00 2001 From: monjnask Date: Thu, 30 Apr 2026 14:38:16 +0530 Subject: [PATCH 1/5] added de bruijn blueprints --- .../fabrics/hybrid_debruijn_fabric.py | 207 ++++++++++++++++++ .../fabrics/multi_host_debruijn_fabric.py | 159 ++++++++++++++ .../test_hybrid_debruijn_fabric.py | 37 ++++ .../test_multi_host_debruijn_fabric.py | 35 +++ 4 files changed, 438 insertions(+) create mode 100644 src/infragraph/blueprints/fabrics/hybrid_debruijn_fabric.py create mode 100644 src/infragraph/blueprints/fabrics/multi_host_debruijn_fabric.py create mode 100644 src/tests/test_blueprints/test_hybrid_debruijn_fabric.py create mode 100644 src/tests/test_blueprints/test_multi_host_debruijn_fabric.py diff --git a/src/infragraph/blueprints/fabrics/hybrid_debruijn_fabric.py b/src/infragraph/blueprints/fabrics/hybrid_debruijn_fabric.py new file mode 100644 index 0000000..2f07806 --- /dev/null +++ b/src/infragraph/blueprints/fabrics/hybrid_debruijn_fabric.py @@ -0,0 +1,207 @@ +from infragraph import * +from infragraph.infragraph_service import InfraGraphService +import itertools + +from infragraph.blueprints.devices.generic.server import Server +from infragraph.blueprints.devices.generic.generic_switch import Switch + + +class DeBruijnFabricWithAccessLayer(Infrastructure): + """ + De Bruijn Fabric with Access Layer + + Inputs: + switch : fabric switch device + server : host device + order : order of DeBruijn graph + + Derived: + sp = switch radix + degree = sp / 8 + + Fabric Switch Ports + ------------------- + 0..degree-1 primary outgoing + degree..2degree-1 redundant outgoing + 2degree..3degree-1 primary incoming + 3degree..4degree-1 redundant incoming + 4degree..sp-1 access switch uplinks + + Access Switch Ports + ------------------- + 0..(sp/2 -1) hosts + remaining fabric switch connection + unused + """ + + def __init__(self, switch: Switch, server: Server, order: int): + + super().__init__( + name="debruijn-fabric-access", + description=f"DeBruijn Fabric with Access Layer (order={order})" + ) + + self.order = order + + # devices + self.devices.append(switch) + self.devices.append(server) + + # components + switch_port = InfraGraphService.get_component(switch, Component.PORT) + host_nic = InfraGraphService.get_component(server, Component.NIC) + + sp = switch_port.count + hn = host_nic.count + + if sp % 8 != 0: + raise ValueError("Switch radix must be divisible by 8") + + degree = sp // 8 + self.degree = degree + + # host capacity + host_ports = sp // 2 + + if host_ports % hn != 0: + raise ValueError("Host NIC count must divide available host ports") + + hosts_per_access = host_ports // hn + + # alphabet + alphabet = [str(i) for i in range(degree)] + + # DeBruijn nodes + nodes = [''.join(p) for p in itertools.product(alphabet, repeat=order)] + self.nodes = nodes + + num_switches = len(nodes) + + # Fabric switches + fabric_switches = self.instances.add( + name="fabric_switch", + device=switch.name, + count=num_switches + ) + + # Access switch device + access_switch = Switch(port_count=sp) + self.devices.append(access_switch) + + access_port = InfraGraphService.get_component(access_switch, Component.PORT) + + # Access switches + access_switches = self.instances.add( + name="access_switch", + device=access_switch.name, + count=num_switches + ) + + # Hosts + total_hosts = num_switches * hosts_per_access + + hosts = self.instances.add( + name="host", + device=server.name, + count=total_hosts + ) + + node_index = {node: i for i, node in enumerate(nodes)} + + # links + fabric_link = self.links.add( + name="fabric-link", + description="DeBruijn fabric connectivity" + ) + fabric_link.physical.bandwidth.gigabits_per_second = 400 + + access_link = self.links.add( + name="access-uplink", + description="Access switch to fabric switch" + ) + access_link.physical.bandwidth.gigabits_per_second = 200 + + host_link = self.links.add( + name="host-link", + description="Host to access switch" + ) + host_link.physical.bandwidth.gigabits_per_second = 100 + + + # Fabric edges (with redundancy) + + for node in nodes: + + src_idx = node_index[node] + + for i, digit in enumerate(alphabet): + + next_node = node[1:] + digit + dst_idx = node_index[next_node] + + # primary fabric link + edge = self.edges.add( + scheme=InfrastructureEdge.ONE2ONE, + link=fabric_link.name + ) + + edge.ep1.instance = f"{fabric_switches.name}[{src_idx}]" + edge.ep1.component = f"{switch_port.name}[{i}]" + + edge.ep2.instance = f"{fabric_switches.name}[{dst_idx}]" + edge.ep2.component = f"{switch_port.name}[{i + 2*degree}]" + + # redundant fabric link + edge = self.edges.add( + scheme=InfrastructureEdge.ONE2ONE, + link=fabric_link.name + ) + + edge.ep1.instance = f"{fabric_switches.name}[{src_idx}]" + edge.ep1.component = f"{switch_port.name}[{i + degree}]" + + edge.ep2.instance = f"{fabric_switches.name}[{dst_idx}]" + edge.ep2.component = f"{switch_port.name}[{i + 3*degree}]" + + + # Access → Fabric links + + uplink_start = 4 * degree + + for idx in range(num_switches): + + edge = self.edges.add( + scheme=InfrastructureEdge.ONE2ONE, + link=access_link.name + ) + + edge.ep1.instance = f"{access_switches.name}[{idx}]" + edge.ep1.component = f"{access_port.name}[0]" + + edge.ep2.instance = f"{fabric_switches.name}[{idx}]" + edge.ep2.component = f"{switch_port.name}[{uplink_start}]" + + + # Hosts → Access switches + + host_index = 0 + + for sw_idx in range(num_switches): + + for h in range(hosts_per_access): + + for nic in range(hn): + + port_index = h * hn + nic + + edge = self.edges.add( + scheme=InfrastructureEdge.ONE2ONE, + link=host_link.name + ) + + edge.ep1.instance = f"{hosts.name}[{host_index}]" + edge.ep1.component = f"{host_nic.name}[{nic}]" + + edge.ep2.instance = f"{access_switches.name}[{sw_idx}]" + edge.ep2.component = f"{access_port.name}[{port_index}]" + + host_index += 1 \ No newline at end of file diff --git a/src/infragraph/blueprints/fabrics/multi_host_debruijn_fabric.py b/src/infragraph/blueprints/fabrics/multi_host_debruijn_fabric.py new file mode 100644 index 0000000..0ef3027 --- /dev/null +++ b/src/infragraph/blueprints/fabrics/multi_host_debruijn_fabric.py @@ -0,0 +1,159 @@ +from infragraph import * +from infragraph.infragraph_service import InfraGraphService +import itertools + +from infragraph.blueprints.devices.generic.server import Server +from infragraph.blueprints.devices.generic.generic_switch import Switch + + +class DeBruijnFabricWithMultiHost(Infrastructure): + """ + DeBruijn Fabric with Multiple Hosts per Switch + Redundant Fabric Links + + Inputs + ------ + switch : fabric switch + server : host device + order : DeBruijn order + + Ports + + 0..d-1 primary outgoing + d..2d-1 redundant outgoing + 2d..3d-1 primary incoming + 3d..4d-1 redundant incoming + 4d..sp-1 host ports + """ + + def __init__(self, switch: Switch, server: Server, order: int): + + super().__init__( + name="multi-host-redundant-debruijn", + description=f"DeBruijn Fabric (k={order})" + ) + + self.order = order + + # add devices + self.devices.append(switch) + self.devices.append(server) + + # components + switch_port = InfraGraphService.get_component(switch, Component.PORT) + host_nic = InfraGraphService.get_component(server, Component.NIC) + + sp = switch_port.count + hn = host_nic.count + + degree = sp // 8 + if degree < 1: + raise ValueError("Not enough switch ports") + + # host ports = half of total + host_ports = sp // 2 + + if host_ports % hn != 0: + raise ValueError( + f"Host NICs ({hn}) must divide available host ports ({host_ports})" + ) + + hosts_per_switch = host_ports // hn + self.degree = degree + + # alphabet + alphabet = [str(i) for i in range(degree)] + + # DeBruijn nodes + nodes = [''.join(p) for p in itertools.product(alphabet, repeat=order)] + self.nodes = nodes + + num_switches = len(nodes) + + # instances + switches = self.instances.add( + name="switch", + device=switch.name, + count=num_switches + ) + + total_hosts = num_switches * hosts_per_switch + + hosts = self.instances.add( + name="host", + device=server.name, + count=total_hosts + ) + + node_index = {node: i for i, node in enumerate(nodes)} + + # links + fabric_link = self.links.add( + name="fabric-link", + description="DeBruijn connectivity" + ) + fabric_link.physical.bandwidth.gigabits_per_second = 400 + + host_link = self.links.add( + name="host-link", + description="Host to switch connectivity" + ) + host_link.physical.bandwidth.gigabits_per_second = 100 + + + # Fabric edges + for node in nodes: + + src_idx = node_index[node] + + for i, digit in enumerate(alphabet): + + next_node = node[1:] + digit + dst_idx = node_index[next_node] + + # Primary link + edge = self.edges.add( + scheme=InfrastructureEdge.ONE2ONE, + link=fabric_link.name + ) + edge.ep1.instance = f"{switches.name}[{src_idx}]" + edge.ep1.component = f"{switch_port.name}[{i}]" + + edge.ep2.instance = f"{switches.name}[{dst_idx}]" + edge.ep2.component = f"{switch_port.name}[{i + 2*degree}]" + + # Redundant link + edge = self.edges.add( + scheme=InfrastructureEdge.ONE2ONE, + link=fabric_link.name + ) + edge.ep1.instance = f"{switches.name}[{src_idx}]" + edge.ep1.component = f"{switch_port.name}[{i + degree}]" + + edge.ep2.instance = f"{switches.name}[{dst_idx}]" + edge.ep2.component = f"{switch_port.name}[{i + 3*degree}]" + + + # Host edges (MULTIPLE HOST) + host_port_start = 4 * degree + host_global_idx = 0 + + for sw_idx in range(num_switches): + + for h in range(hosts_per_switch): + + for nic in range(hn): + + edge = self.edges.add( + scheme=InfrastructureEdge.ONE2ONE, + link=host_link.name + ) + + edge.ep1.instance = f"{hosts.name}[{host_global_idx}]" + edge.ep1.component = f"{host_nic.name}[{nic}]" + + port_offset = h * hn + nic + + edge.ep2.instance = f"{switches.name}[{sw_idx}]" + edge.ep2.component = f"{switch_port.name}[{host_port_start + port_offset}]" + + host_global_idx += 1 \ No newline at end of file diff --git a/src/tests/test_blueprints/test_hybrid_debruijn_fabric.py b/src/tests/test_blueprints/test_hybrid_debruijn_fabric.py new file mode 100644 index 0000000..72c49d7 --- /dev/null +++ b/src/tests/test_blueprints/test_hybrid_debruijn_fabric.py @@ -0,0 +1,37 @@ +from infragraph.infragraph_service import InfraGraphService +from infragraph.blueprints.devices.generic.server import Server +from infragraph.blueprints.devices.generic.generic_switch import Switch +from infragraph.blueprints.fabrics.hybrid_debruijn_fabric import DeBruijnFabricWithAccessLayer +import networkx +import yaml + +def dump_yaml(debruijn_fabric, filename): + with open(filename + ".yaml", "w") as file: + data = debruijn_fabric.serialize("dict") + yaml.dump(data, file, default_flow_style=False, indent=4) + +def test_debruijn_fabric_access(): + + # Devices + switch = Switch(port_count=16) + server = Server() + + # fabric + fabric = DeBruijnFabricWithAccessLayer( + switch=switch, + server=server, + order=3, + + ) + dump_yaml(fabric,"hybrid_debruijn") + service = InfraGraphService() + service.set_graph(fabric) + + g = service.get_networkx_graph() + + print(networkx.write_network_text(g, vertical_chains=True)) + + + +if __name__ == "__main__": + test_debruijn_fabric_access() \ No newline at end of file diff --git a/src/tests/test_blueprints/test_multi_host_debruijn_fabric.py b/src/tests/test_blueprints/test_multi_host_debruijn_fabric.py new file mode 100644 index 0000000..9bde48a --- /dev/null +++ b/src/tests/test_blueprints/test_multi_host_debruijn_fabric.py @@ -0,0 +1,35 @@ +from infragraph.infragraph_service import InfraGraphService +from infragraph.blueprints.devices.generic.server import Server +from infragraph.blueprints.devices.generic.generic_switch import Switch +from infragraph.blueprints.fabrics.multi_host_debruijn_fabric import DeBruijnFabricWithMultiHost +import networkx +import yaml + + +def dump_yaml(debruijn_fabric, filename): + with open(filename + ".yaml", "w") as file: + data = debruijn_fabric.serialize("dict") + yaml.dump(data, file, default_flow_style=False, indent=4) + + +def test_debruijn(): + switch = Switch(port_count=16) + server = Server() + + fabric = DeBruijnFabricWithMultiHost( + switch=switch, + server=server, + order=3, + ) + + service = InfraGraphService() + service.set_graph(fabric) + + dump_yaml(fabric, "multi_host_debruijn") + + g = service.get_networkx_graph() + #print(g) + + +if __name__ == "__main__": + test_debruijn() \ No newline at end of file From 6654216a8af2d2ea1509d03accf0b022d0d21646 Mon Sep 17 00:00:00 2001 From: Monjistha99 Date: Mon, 11 May 2026 11:36:54 +0530 Subject: [PATCH 2/5] Update test_multi_host_debruijn_fabric.py --- src/tests/test_blueprints/test_multi_host_debruijn_fabric.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/tests/test_blueprints/test_multi_host_debruijn_fabric.py b/src/tests/test_blueprints/test_multi_host_debruijn_fabric.py index 9bde48a..60ac61a 100644 --- a/src/tests/test_blueprints/test_multi_host_debruijn_fabric.py +++ b/src/tests/test_blueprints/test_multi_host_debruijn_fabric.py @@ -25,11 +25,9 @@ def test_debruijn(): service = InfraGraphService() service.set_graph(fabric) - dump_yaml(fabric, "multi_host_debruijn") - g = service.get_networkx_graph() #print(g) if __name__ == "__main__": - test_debruijn() \ No newline at end of file + test_debruijn() From 7511be050b232115f527f7ac7c96a6b9dc9df8eb Mon Sep 17 00:00:00 2001 From: Monjistha99 Date: Mon, 11 May 2026 11:37:28 +0530 Subject: [PATCH 3/5] Update test_hybrid_debruijn_fabric.py --- src/tests/test_blueprints/test_hybrid_debruijn_fabric.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tests/test_blueprints/test_hybrid_debruijn_fabric.py b/src/tests/test_blueprints/test_hybrid_debruijn_fabric.py index 72c49d7..611853d 100644 --- a/src/tests/test_blueprints/test_hybrid_debruijn_fabric.py +++ b/src/tests/test_blueprints/test_hybrid_debruijn_fabric.py @@ -23,7 +23,7 @@ def test_debruijn_fabric_access(): order=3, ) - dump_yaml(fabric,"hybrid_debruijn") + service = InfraGraphService() service.set_graph(fabric) @@ -34,4 +34,4 @@ def test_debruijn_fabric_access(): if __name__ == "__main__": - test_debruijn_fabric_access() \ No newline at end of file + test_debruijn_fabric_access() From 959d396ace41adca08c565a622125c740f2562f1 Mon Sep 17 00:00:00 2001 From: monjnask Date: Tue, 12 May 2026 17:45:41 +0530 Subject: [PATCH 4/5] Updated DeBruijn fabric blueprints and tests --- .../fabrics/hybrid_debruijn_fabric.py | 336 +++++++----------- .../fabrics/multi_host_debruijn_fabric.py | 263 ++++++-------- .../test_hybrid_debruijn_fabric.py | 88 +++-- .../test_multi_host_debruijn_fabric.py | 84 +++-- 4 files changed, 335 insertions(+), 436 deletions(-) diff --git a/src/infragraph/blueprints/fabrics/hybrid_debruijn_fabric.py b/src/infragraph/blueprints/fabrics/hybrid_debruijn_fabric.py index 2f07806..c72b2c3 100644 --- a/src/infragraph/blueprints/fabrics/hybrid_debruijn_fabric.py +++ b/src/infragraph/blueprints/fabrics/hybrid_debruijn_fabric.py @@ -1,207 +1,129 @@ -from infragraph import * -from infragraph.infragraph_service import InfraGraphService -import itertools - -from infragraph.blueprints.devices.generic.server import Server -from infragraph.blueprints.devices.generic.generic_switch import Switch - - -class DeBruijnFabricWithAccessLayer(Infrastructure): - """ - De Bruijn Fabric with Access Layer - - Inputs: - switch : fabric switch device - server : host device - order : order of DeBruijn graph - - Derived: - sp = switch radix - degree = sp / 8 - - Fabric Switch Ports - ------------------- - 0..degree-1 primary outgoing - degree..2degree-1 redundant outgoing - 2degree..3degree-1 primary incoming - 3degree..4degree-1 redundant incoming - 4degree..sp-1 access switch uplinks - - Access Switch Ports - ------------------- - 0..(sp/2 -1) hosts - remaining fabric switch connection + unused - """ - - def __init__(self, switch: Switch, server: Server, order: int): - - super().__init__( - name="debruijn-fabric-access", - description=f"DeBruijn Fabric with Access Layer (order={order})" - ) - - self.order = order - - # devices - self.devices.append(switch) - self.devices.append(server) - - # components - switch_port = InfraGraphService.get_component(switch, Component.PORT) - host_nic = InfraGraphService.get_component(server, Component.NIC) - - sp = switch_port.count - hn = host_nic.count - - if sp % 8 != 0: - raise ValueError("Switch radix must be divisible by 8") - - degree = sp // 8 - self.degree = degree - - # host capacity - host_ports = sp // 2 - - if host_ports % hn != 0: - raise ValueError("Host NIC count must divide available host ports") - - hosts_per_access = host_ports // hn - - # alphabet - alphabet = [str(i) for i in range(degree)] - - # DeBruijn nodes - nodes = [''.join(p) for p in itertools.product(alphabet, repeat=order)] - self.nodes = nodes - - num_switches = len(nodes) - - # Fabric switches - fabric_switches = self.instances.add( - name="fabric_switch", - device=switch.name, - count=num_switches - ) - - # Access switch device - access_switch = Switch(port_count=sp) - self.devices.append(access_switch) - - access_port = InfraGraphService.get_component(access_switch, Component.PORT) - - # Access switches - access_switches = self.instances.add( - name="access_switch", - device=access_switch.name, - count=num_switches - ) - - # Hosts - total_hosts = num_switches * hosts_per_access - - hosts = self.instances.add( - name="host", - device=server.name, - count=total_hosts - ) - - node_index = {node: i for i, node in enumerate(nodes)} - - # links - fabric_link = self.links.add( - name="fabric-link", - description="DeBruijn fabric connectivity" - ) - fabric_link.physical.bandwidth.gigabits_per_second = 400 - - access_link = self.links.add( - name="access-uplink", - description="Access switch to fabric switch" - ) - access_link.physical.bandwidth.gigabits_per_second = 200 - - host_link = self.links.add( - name="host-link", - description="Host to access switch" - ) - host_link.physical.bandwidth.gigabits_per_second = 100 - - - # Fabric edges (with redundancy) - - for node in nodes: - - src_idx = node_index[node] - - for i, digit in enumerate(alphabet): - - next_node = node[1:] + digit - dst_idx = node_index[next_node] - - # primary fabric link - edge = self.edges.add( - scheme=InfrastructureEdge.ONE2ONE, - link=fabric_link.name - ) - - edge.ep1.instance = f"{fabric_switches.name}[{src_idx}]" - edge.ep1.component = f"{switch_port.name}[{i}]" - - edge.ep2.instance = f"{fabric_switches.name}[{dst_idx}]" - edge.ep2.component = f"{switch_port.name}[{i + 2*degree}]" - - # redundant fabric link - edge = self.edges.add( - scheme=InfrastructureEdge.ONE2ONE, - link=fabric_link.name - ) - - edge.ep1.instance = f"{fabric_switches.name}[{src_idx}]" - edge.ep1.component = f"{switch_port.name}[{i + degree}]" - - edge.ep2.instance = f"{fabric_switches.name}[{dst_idx}]" - edge.ep2.component = f"{switch_port.name}[{i + 3*degree}]" - - - # Access → Fabric links - - uplink_start = 4 * degree - - for idx in range(num_switches): - - edge = self.edges.add( - scheme=InfrastructureEdge.ONE2ONE, - link=access_link.name - ) - - edge.ep1.instance = f"{access_switches.name}[{idx}]" - edge.ep1.component = f"{access_port.name}[0]" - - edge.ep2.instance = f"{fabric_switches.name}[{idx}]" - edge.ep2.component = f"{switch_port.name}[{uplink_start}]" - - - # Hosts → Access switches - - host_index = 0 - - for sw_idx in range(num_switches): - - for h in range(hosts_per_access): - - for nic in range(hn): - - port_index = h * hn + nic - - edge = self.edges.add( - scheme=InfrastructureEdge.ONE2ONE, - link=host_link.name - ) - - edge.ep1.instance = f"{hosts.name}[{host_index}]" - edge.ep1.component = f"{host_nic.name}[{nic}]" - - edge.ep2.instance = f"{access_switches.name}[{sw_idx}]" - edge.ep2.component = f"{access_port.name}[{port_index}]" - - host_index += 1 \ No newline at end of file +from infragraph import * +from infragraph.infragraph_service import InfraGraphService +import itertools + +class HybridDeBruijnFabric(Infrastructure): + """ + A Hybrid of De Bruijn Fabric and Clos Fabric with Access Layer of Rack Switches + + Inputs: + switch : fabric switch device + server : host device + order : order of DeBruijn graph + + Derived: + switch_port.count = switch radix + degree = switch_port.count / 8 + + Fabric Switch Ports: + 0..degree-1 primary outgoing + degree..2degree-1 redundant outgoing + 2degree..3degree-1 primary incoming + 3degree..4degree-1 redundant incoming + 4degree..switch_port.count-1 access switch uplinks + + Access Switch Ports: + 0..(switch_port.count/2 -1) hosts + remaining fabric switch connection + unused + + """ + + def __init__(self, switch: Device, server: Device, order: int): + super().__init__( + name="hybrid-debruijn-fabric", + description=f"DeBruijn Fabric With Rack Switches(order={order})", + ) + + self.devices.append(switch) + self.devices.append(server) + + switch_port = InfraGraphService.get_component(switch, Component.PORT) + host_nic = InfraGraphService.get_component(server, Component.NIC) + + if switch_port.count % 8 != 0: + raise ValueError("Switch radix must be divisible by 8") + + # degree of graph = connected neighbour nodes + degree = switch_port.count // 8 + host_ports = switch_port.count // 2 + + if degree < 1: + raise ValueError("Not enough switch ports") + + if host_ports % host_nic.count != 0: + raise ValueError("Host NIC count must divide available host ports") + + # Each access switch dedicates half of its ports to hosts + # host count is based on NICs per host + hosts_per_access_switch = host_ports // host_nic.count + + # Build de bruijn node labels. + # For degree d and order n, the fabric has d^n switches, each having unique label + alphabet = [str(i) for i in range(degree)] + nodes = ["".join(p) for p in itertools.product(alphabet, repeat=order)] + num_switches = len(nodes) + + # Create one fabric switch and one access switch per de bruijn node + fabric_switches = self.instances.add(name="fabric_switch", device=switch.name, count=num_switches) + access_switches = self.instances.add(name="access_switch", device=switch.name, count=num_switches) + + # Create Hosts per access/rack switch + total_hosts = num_switches * hosts_per_access_switch + hosts = self.instances.add(name="host", device=server.name, count=total_hosts) + node_index = {node: i for i, node in enumerate(nodes)} + + # Create links + # fabric link connects fabric switches + # access links connects fabric switch and access switch + fabric_link = self.links.add(name="fabric-link", description="DeBruijn fabric connectivity") + fabric_link.physical.bandwidth.gigabits_per_second = 400 + access_link = self.links.add(name="access-uplink", description="Access switch to fabric switch") + access_link.physical.bandwidth.gigabits_per_second = 200 + host_link = self.links.add(name="host-link", description="Host to access switch") + host_link.physical.bandwidth.gigabits_per_second = 100 + + # Added de bruijn fabric edges + # Routing - shifting node label left and appending each alphabet digit of destination node + # two types of link - primary link, redundant link + for node in nodes: + src_idx = node_index[node] + for i, digit in enumerate(alphabet): + next_node = node[1:] + digit + dst_idx = node_index[next_node] + + # primary link + edge = self.edges.add(scheme=InfrastructureEdge.ONE2ONE, link=fabric_link.name) + edge.ep1.instance = f"{fabric_switches.name}[{src_idx}]" + edge.ep1.component = f"{switch_port.name}[{i}]" + edge.ep2.instance = f"{fabric_switches.name}[{dst_idx}]" + edge.ep2.component = f"{switch_port.name}[{i + 2*degree}]" + + # redundant link + edge = self.edges.add(scheme=InfrastructureEdge.ONE2ONE, link=fabric_link.name) + edge.ep1.instance = f"{fabric_switches.name}[{src_idx}]" + edge.ep1.component = f"{switch_port.name}[{i + degree}]" + edge.ep2.instance = f"{fabric_switches.name}[{dst_idx}]" + edge.ep2.component = f"{switch_port.name}[{i + 3*degree}]" + + # Added access switch to fabric switch edges + uplink_start = 4 * degree + for idx in range(num_switches): + edge = self.edges.add(scheme=InfrastructureEdge.ONE2ONE, link=access_link.name) + edge.ep1.instance = f"{access_switches.name}[{idx}]" + edge.ep1.component = f"{switch_port.name}[0]" + edge.ep2.instance = f"{fabric_switches.name}[{idx}]" + edge.ep2.component = f"{switch_port.name}[{uplink_start}]" + + # Attach hosts to access switch + host_index = 0 + for sw_idx in range(num_switches): + for h in range(hosts_per_access_switch): + for nic in range(host_nic.count): + port_index = h * host_nic.count + nic + edge = self.edges.add(scheme=InfrastructureEdge.ONE2ONE, link=host_link.name) + edge.ep1.instance = f"{hosts.name}[{host_index}]" + edge.ep1.component = f"{host_nic.name}[{nic}]" + edge.ep2.instance = f"{access_switches.name}[{sw_idx}]" + edge.ep2.component = f"{switch_port.name}[{port_index}]" + host_index += 1 + diff --git a/src/infragraph/blueprints/fabrics/multi_host_debruijn_fabric.py b/src/infragraph/blueprints/fabrics/multi_host_debruijn_fabric.py index 0ef3027..3f7ad89 100644 --- a/src/infragraph/blueprints/fabrics/multi_host_debruijn_fabric.py +++ b/src/infragraph/blueprints/fabrics/multi_host_debruijn_fabric.py @@ -1,159 +1,104 @@ -from infragraph import * -from infragraph.infragraph_service import InfraGraphService -import itertools - -from infragraph.blueprints.devices.generic.server import Server -from infragraph.blueprints.devices.generic.generic_switch import Switch - - -class DeBruijnFabricWithMultiHost(Infrastructure): - """ - DeBruijn Fabric with Multiple Hosts per Switch + Redundant Fabric Links - - Inputs - ------ - switch : fabric switch - server : host device - order : DeBruijn order - - Ports - - 0..d-1 primary outgoing - d..2d-1 redundant outgoing - 2d..3d-1 primary incoming - 3d..4d-1 redundant incoming - 4d..sp-1 host ports - """ - - def __init__(self, switch: Switch, server: Server, order: int): - - super().__init__( - name="multi-host-redundant-debruijn", - description=f"DeBruijn Fabric (k={order})" - ) - - self.order = order - - # add devices - self.devices.append(switch) - self.devices.append(server) - - # components - switch_port = InfraGraphService.get_component(switch, Component.PORT) - host_nic = InfraGraphService.get_component(server, Component.NIC) - - sp = switch_port.count - hn = host_nic.count - - degree = sp // 8 - if degree < 1: - raise ValueError("Not enough switch ports") - - # host ports = half of total - host_ports = sp // 2 - - if host_ports % hn != 0: - raise ValueError( - f"Host NICs ({hn}) must divide available host ports ({host_ports})" - ) - - hosts_per_switch = host_ports // hn - self.degree = degree - - # alphabet - alphabet = [str(i) for i in range(degree)] - - # DeBruijn nodes - nodes = [''.join(p) for p in itertools.product(alphabet, repeat=order)] - self.nodes = nodes - - num_switches = len(nodes) - - # instances - switches = self.instances.add( - name="switch", - device=switch.name, - count=num_switches - ) - - total_hosts = num_switches * hosts_per_switch - - hosts = self.instances.add( - name="host", - device=server.name, - count=total_hosts - ) - - node_index = {node: i for i, node in enumerate(nodes)} - - # links - fabric_link = self.links.add( - name="fabric-link", - description="DeBruijn connectivity" - ) - fabric_link.physical.bandwidth.gigabits_per_second = 400 - - host_link = self.links.add( - name="host-link", - description="Host to switch connectivity" - ) - host_link.physical.bandwidth.gigabits_per_second = 100 - - - # Fabric edges - for node in nodes: - - src_idx = node_index[node] - - for i, digit in enumerate(alphabet): - - next_node = node[1:] + digit - dst_idx = node_index[next_node] - - # Primary link - edge = self.edges.add( - scheme=InfrastructureEdge.ONE2ONE, - link=fabric_link.name - ) - edge.ep1.instance = f"{switches.name}[{src_idx}]" - edge.ep1.component = f"{switch_port.name}[{i}]" - - edge.ep2.instance = f"{switches.name}[{dst_idx}]" - edge.ep2.component = f"{switch_port.name}[{i + 2*degree}]" - - # Redundant link - edge = self.edges.add( - scheme=InfrastructureEdge.ONE2ONE, - link=fabric_link.name - ) - edge.ep1.instance = f"{switches.name}[{src_idx}]" - edge.ep1.component = f"{switch_port.name}[{i + degree}]" - - edge.ep2.instance = f"{switches.name}[{dst_idx}]" - edge.ep2.component = f"{switch_port.name}[{i + 3*degree}]" - - - # Host edges (MULTIPLE HOST) - host_port_start = 4 * degree - host_global_idx = 0 - - for sw_idx in range(num_switches): - - for h in range(hosts_per_switch): - - for nic in range(hn): - - edge = self.edges.add( - scheme=InfrastructureEdge.ONE2ONE, - link=host_link.name - ) - - edge.ep1.instance = f"{hosts.name}[{host_global_idx}]" - edge.ep1.component = f"{host_nic.name}[{nic}]" - - port_offset = h * hn + nic - - edge.ep2.instance = f"{switches.name}[{sw_idx}]" - edge.ep2.component = f"{switch_port.name}[{host_port_start + port_offset}]" - - host_global_idx += 1 \ No newline at end of file +from infragraph import * +from infragraph.infragraph_service import InfraGraphService +import itertools + + +class MultiHostDeBruijnFabric(Infrastructure): + """ + DeBruijn Fabric with Multiple Hosts per Switch + Redundant Fabric Links + + Inputs + switch : fabric switch + server : host device + order : DeBruijn order + + Ports + 0..d-1 primary outgoing + d..2d-1 redundant outgoing + 2d..3d-1 primary incoming + 3d..4d-1 redundant incoming + 4d..switch_port.count-1 host ports + """ + + def __init__(self, switch: Device, server: Device, order: int): + super().__init__( + name="multi-host-redundant-debruijn", + description=f"DeBruijn Fabric (k={order})", + ) + + self.devices.append(switch) + self.devices.append(server) + + switch_port = InfraGraphService.get_component(switch, Component.PORT) + host_nic = InfraGraphService.get_component(server, Component.NIC) + + degree = switch_port.count // 8 + host_ports = switch_port.count // 2 + + if degree < 1: + raise ValueError("Not enough switch ports") + + if host_ports % host_nic.count != 0: + raise ValueError( + f"Host NICs ({host_nic.count}) must divide available host ports ({host_ports})" + ) + + # Each access switch dedicates half of its ports to hosts + # host count is based on NICs per host + hosts_per_switch = host_ports // host_nic.count + + # Build de bruijn node labels. + # For degree d and order n, the fabric has d^n switches, each having unique label + alphabet = [str(i) for i in range(degree)] + nodes = ["".join(p) for p in itertools.product(alphabet, repeat=order)] + num_switches = len(nodes) + + # Create fabric switches and Hosts + switches = self.instances.add(name="switch", device=switch.name, count=num_switches) + hosts = self.instances.add(name="host", device=server.name, count=num_switches * hosts_per_switch) + node_index = {node: i for i, node in enumerate(nodes)} + + # Added links + # fabric link connects fabric switches + # host links connects hosts with fabric switches + fabric_link = self.links.add(name="fabric-link", description="DeBruijn connectivity") + fabric_link.physical.bandwidth.gigabits_per_second = 400 + host_link = self.links.add(name="host-link", description="Host to switch connectivity") + host_link.physical.bandwidth.gigabits_per_second = 100 + + # Added de bruijn fabric edges + # Routing - shifting node label left and appending each alphabet digit of destination node + # two types of link - primary link, redundant link + for node in nodes: + src_idx = node_index[node] + for i, digit in enumerate(alphabet): + next_node = node[1:] + digit + dst_idx = node_index[next_node] + + # Primary link + edge = self.edges.add(scheme=InfrastructureEdge.ONE2ONE, link=fabric_link.name) + edge.ep1.instance = f"{switches.name}[{src_idx}]" + edge.ep1.component = f"{switch_port.name}[{i}]" + edge.ep2.instance = f"{switches.name}[{dst_idx}]" + edge.ep2.component = f"{switch_port.name}[{i + 2*degree}]" + + # Redundant link + edge = self.edges.add(scheme=InfrastructureEdge.ONE2ONE, link=fabric_link.name) + edge.ep1.instance = f"{switches.name}[{src_idx}]" + edge.ep1.component = f"{switch_port.name}[{i + degree}]" + edge.ep2.instance = f"{switches.name}[{dst_idx}]" + edge.ep2.component = f"{switch_port.name}[{i + 3*degree}]" + + # Attach hosts to access switch + host_port_start = 4 * degree + host_global_idx = 0 + for sw_idx in range(num_switches): + for h in range(hosts_per_switch): + for nic in range(host_nic.count): + port_offset = h * host_nic.count + nic + edge = self.edges.add(scheme=InfrastructureEdge.ONE2ONE, link=host_link.name) + edge.ep1.instance = f"{hosts.name}[{host_global_idx}]" + edge.ep1.component = f"{host_nic.name}[{nic}]" + edge.ep2.instance = f"{switches.name}[{sw_idx}]" + edge.ep2.component = (f"{switch_port.name}[{host_port_start + port_offset}]") + host_global_idx += 1 diff --git a/src/tests/test_blueprints/test_hybrid_debruijn_fabric.py b/src/tests/test_blueprints/test_hybrid_debruijn_fabric.py index 611853d..35bae23 100644 --- a/src/tests/test_blueprints/test_hybrid_debruijn_fabric.py +++ b/src/tests/test_blueprints/test_hybrid_debruijn_fabric.py @@ -1,37 +1,51 @@ -from infragraph.infragraph_service import InfraGraphService -from infragraph.blueprints.devices.generic.server import Server -from infragraph.blueprints.devices.generic.generic_switch import Switch -from infragraph.blueprints.fabrics.hybrid_debruijn_fabric import DeBruijnFabricWithAccessLayer -import networkx -import yaml - -def dump_yaml(debruijn_fabric, filename): - with open(filename + ".yaml", "w") as file: - data = debruijn_fabric.serialize("dict") - yaml.dump(data, file, default_flow_style=False, indent=4) - -def test_debruijn_fabric_access(): - - # Devices - switch = Switch(port_count=16) - server = Server() - - # fabric - fabric = DeBruijnFabricWithAccessLayer( - switch=switch, - server=server, - order=3, - - ) - - service = InfraGraphService() - service.set_graph(fabric) - - g = service.get_networkx_graph() - - print(networkx.write_network_text(g, vertical_chains=True)) - - - -if __name__ == "__main__": - test_debruijn_fabric_access() +from infragraph.infragraph_service import InfraGraphService +from infragraph.blueprints.devices.generic.server import Server +from infragraph.blueprints.devices.generic.generic_switch import Switch +from infragraph.blueprints.fabrics.hybrid_debruijn_fabric import HybridDeBruijnFabric +from infragraph.blueprints.devices.nvidia.dgx import NvidiaDGX +import networkx +import pytest + +DGX_PROFILES = [ + "dgx1", + "dgx2", + "dgx_a100", + "dgx_h100", + "dgx_gb200", +] +@pytest.mark.asyncio +async def test_hybrid_debruijn_fabric(): + """ + Generate a hybrid debruijn fabric + + """ + switch = Switch(port_count=16) + server = Server() + fabric = HybridDeBruijnFabric(switch, server, 3) + + service = InfraGraphService() + service.set_graph(fabric) + + graph = service.get_networkx_graph() + print(networkx.write_network_text(graph, vertical_chains=True)) + +@pytest.mark.asyncio +@pytest.mark.parametrize("dgx_profile", DGX_PROFILES) +async def test_hybrid_debruijn_fabric_with_dgx(dgx_profile): + """ + Generate a hybrid debruijn fabric with each supported DGX device + + """ + switch = Switch(port_count=16) + dgx = NvidiaDGX(dgx_profile) + fabric = HybridDeBruijnFabric(switch, dgx, 3) + + service = InfraGraphService() + service.set_graph(fabric) + + graph = service.get_networkx_graph() + print(networkx.write_network_text(graph, vertical_chains=True)) + + +if __name__ == "__main__": + pytest.main(["-s", __file__]) diff --git a/src/tests/test_blueprints/test_multi_host_debruijn_fabric.py b/src/tests/test_blueprints/test_multi_host_debruijn_fabric.py index 60ac61a..051bf6c 100644 --- a/src/tests/test_blueprints/test_multi_host_debruijn_fabric.py +++ b/src/tests/test_blueprints/test_multi_host_debruijn_fabric.py @@ -1,33 +1,51 @@ -from infragraph.infragraph_service import InfraGraphService -from infragraph.blueprints.devices.generic.server import Server -from infragraph.blueprints.devices.generic.generic_switch import Switch -from infragraph.blueprints.fabrics.multi_host_debruijn_fabric import DeBruijnFabricWithMultiHost -import networkx -import yaml - - -def dump_yaml(debruijn_fabric, filename): - with open(filename + ".yaml", "w") as file: - data = debruijn_fabric.serialize("dict") - yaml.dump(data, file, default_flow_style=False, indent=4) - - -def test_debruijn(): - switch = Switch(port_count=16) - server = Server() - - fabric = DeBruijnFabricWithMultiHost( - switch=switch, - server=server, - order=3, - ) - - service = InfraGraphService() - service.set_graph(fabric) - - g = service.get_networkx_graph() - #print(g) - - -if __name__ == "__main__": - test_debruijn() +from infragraph.infragraph_service import InfraGraphService +from infragraph.blueprints.devices.generic.server import Server +from infragraph.blueprints.devices.generic.generic_switch import Switch +from infragraph.blueprints.fabrics.multi_host_debruijn_fabric import MultiHostDeBruijnFabric +from infragraph.blueprints.devices.nvidia.dgx import NvidiaDGX +import networkx +import pytest + +DGX_PROFILES = [ + "dgx1", + "dgx2", + "dgx_a100", + "dgx_h100", + "dgx_gb200", +] +@pytest.mark.asyncio +async def test_debruijn_multi_host_fabric(): + """ + Generate a debruijn fabric with multiple hosts per switch + + """ + switch = Switch(port_count=16) + server = Server() + fabric = MultiHostDeBruijnFabric(switch, server, 3) + + service = InfraGraphService() + service.set_graph(fabric) + + graph = service.get_networkx_graph() + print(networkx.write_network_text(graph, vertical_chains=True)) + +@pytest.mark.asyncio +@pytest.mark.parametrize("dgx_profile", DGX_PROFILES) +async def test_debruijn_multi_host_fabric_with_dgx(dgx_profile): + """ + Generate a debruijn fabric with each supported DGX device + + """ + switch = Switch(port_count=16) + dgx = NvidiaDGX(dgx_profile) + fabric = MultiHostDeBruijnFabric(switch, dgx, 3) + + service = InfraGraphService() + service.set_graph(fabric) + + graph = service.get_networkx_graph() + print(networkx.write_network_text(graph, vertical_chains=True)) + + +if __name__ == "__main__": + pytest.main(["-s", __file__]) From feb9e4faf32bb4bea81af6bb9d7212aa812ca566 Mon Sep 17 00:00:00 2001 From: monjnask Date: Fri, 22 May 2026 12:29:48 +0530 Subject: [PATCH 5/5] updated hybrid and multihost debruijn --- .../blueprints/fabrics/hybrid_debruijn_fabric.py | 11 ++++++++--- .../blueprints/fabrics/multi_host_debruijn_fabric.py | 6 +++--- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/infragraph/blueprints/fabrics/hybrid_debruijn_fabric.py b/src/infragraph/blueprints/fabrics/hybrid_debruijn_fabric.py index c72b2c3..be0a681 100644 --- a/src/infragraph/blueprints/fabrics/hybrid_debruijn_fabric.py +++ b/src/infragraph/blueprints/fabrics/hybrid_debruijn_fabric.py @@ -34,12 +34,14 @@ def __init__(self, switch: Device, server: Device, order: int): description=f"DeBruijn Fabric With Rack Switches(order={order})", ) - self.devices.append(switch) - self.devices.append(server) - switch_port = InfraGraphService.get_component(switch, Component.PORT) host_nic = InfraGraphService.get_component(server, Component.NIC) + # The switch radix must divide evenly across the full port plan: + # half the ports are for fabric links and half are for host/access links; + # within the fabric half, ports are split into incoming and outgoing; + # within both incoming and outgoing groups, ports are split again into + # primary and redundant links. Therefore switch port must be divisible by 8 (2*2*2) if switch_port.count % 8 != 0: raise ValueError("Switch radix must be divisible by 8") @@ -57,6 +59,9 @@ def __init__(self, switch: Device, server: Device, order: int): # host count is based on NICs per host hosts_per_access_switch = host_ports // host_nic.count + self.devices.append(switch) + self.devices.append(server) + # Build de bruijn node labels. # For degree d and order n, the fabric has d^n switches, each having unique label alphabet = [str(i) for i in range(degree)] diff --git a/src/infragraph/blueprints/fabrics/multi_host_debruijn_fabric.py b/src/infragraph/blueprints/fabrics/multi_host_debruijn_fabric.py index 3f7ad89..3a7ac29 100644 --- a/src/infragraph/blueprints/fabrics/multi_host_debruijn_fabric.py +++ b/src/infragraph/blueprints/fabrics/multi_host_debruijn_fabric.py @@ -26,9 +26,6 @@ def __init__(self, switch: Device, server: Device, order: int): description=f"DeBruijn Fabric (k={order})", ) - self.devices.append(switch) - self.devices.append(server) - switch_port = InfraGraphService.get_component(switch, Component.PORT) host_nic = InfraGraphService.get_component(server, Component.NIC) @@ -47,6 +44,9 @@ def __init__(self, switch: Device, server: Device, order: int): # host count is based on NICs per host hosts_per_switch = host_ports // host_nic.count + self.devices.append(switch) + self.devices.append(server) + # Build de bruijn node labels. # For degree d and order n, the fabric has d^n switches, each having unique label alphabet = [str(i) for i in range(degree)]