Skip to content
This repository has been archived by the owner on Dec 10, 2024. It is now read-only.

WIP: Core: Lazy MIFs #42

Closed
wants to merge 66 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
66 commits
Select commit Hold shift + click to select a range
946234d
Add motivating test
iopapamanoglou Sep 6, 2024
b91e80e
Merge branch 'main' into feature/lazy_mifs
iopapamanoglou Sep 17, 2024
514a057
mif special hiearchical
iopapamanoglou Sep 17, 2024
023fad3
gif multiconnect; better test timings
iopapamanoglou Sep 17, 2024
91aa984
WIP: Stack up connect works
iopapamanoglou Sep 17, 2024
14bdc64
ruff
iopapamanoglou Sep 17, 2024
5d82322
WIP: Multipaths broken
iopapamanoglou Sep 18, 2024
2c8c506
make static
iopapamanoglou Sep 18, 2024
0ff150a
BFS superset; Fix up connect
iopapamanoglou Sep 18, 2024
8f51e67
basic linkderived idea
iopapamanoglou Sep 18, 2024
24a7268
Cleaner stack elems; Specialize Hierarchical edge cases
iopapamanoglou Sep 18, 2024
bd94885
Specialize relationship is LinkParent
iopapamanoglou Sep 18, 2024
ba8e6d1
Partial hierarchy
iopapamanoglou Sep 19, 2024
5b28dff
refactor visualize
iopapamanoglou Sep 19, 2024
314b72d
Node top name; Prettify & Filter interactive graph
iopapamanoglou Sep 19, 2024
6d3a9ee
depth filter; better legend
iopapamanoglou Sep 19, 2024
a1d1b0d
visualize: nested nodes
iopapamanoglou Sep 19, 2024
cf18853
non-connected filter
iopapamanoglou Sep 19, 2024
62c672e
better naming
iopapamanoglou Sep 19, 2024
d766eab
Fix shallow links
iopapamanoglou Sep 20, 2024
18e43a8
Fix power source short detect
iopapamanoglou Sep 20, 2024
8194b1e
Works! Now speed optimize
iopapamanoglou Sep 20, 2024
7f2191b
dst filter
iopapamanoglou Sep 20, 2024
a28043a
move dataclass def (10x speedup)
iopapamanoglou Sep 20, 2024
9684b50
test logging
iopapamanoglou Sep 20, 2024
e599367
add get_connected perf test
iopapamanoglou Sep 20, 2024
17e7f54
LinkDirectDerived
iopapamanoglou Sep 23, 2024
dd67d52
Make specialize equivalent to connect in path search
iopapamanoglou Sep 23, 2024
47d33c3
Performance improve
iopapamanoglou Sep 23, 2024
6deb551
Less aggressive but way faster inline promise filter
iopapamanoglou Sep 23, 2024
c905b57
Generator interfaces
iopapamanoglou Sep 24, 2024
6e86bac
deque bfs
iopapamanoglou Sep 24, 2024
d7fc688
BFS generator
iopapamanoglou Sep 24, 2024
0a92b1e
Fix dynamic params
iopapamanoglou Sep 24, 2024
03645d5
Rewrote a lot of BFS; Parameter explicit resolution; Tests pass; Stil…
iopapamanoglou Sep 24, 2024
2d7263c
some logging cleanup
iopapamanoglou Sep 24, 2024
f942a78
Split bfs & pathfinder; Perf metrics
iopapamanoglou Oct 15, 2024
ff85361
special bfs
iopapamanoglou Oct 15, 2024
61fff2c
path cache
iopapamanoglou Oct 15, 2024
90d1499
sorted paths
iopapamanoglou Oct 15, 2024
7bce7c9
tryin out reorder vs prune
iopapamanoglou Oct 15, 2024
e56f018
WIP: C++ graph
iopapamanoglou Oct 16, 2024
526aed8
More cpp graph progress
iopapamanoglou Oct 16, 2024
3e31cf3
c++ works; linkcond & split path missing
iopapamanoglou Oct 16, 2024
bbee9e2
c++ perf counters
iopapamanoglou Oct 16, 2024
b578d09
python counters
iopapamanoglou Oct 17, 2024
adc019a
indiv measure switch
iopapamanoglou Oct 17, 2024
eadd395
max paths heuristic
iopapamanoglou Oct 17, 2024
edc7d17
configurable
iopapamanoglou Oct 17, 2024
8e3c16e
path lengths
iopapamanoglou Oct 17, 2024
66ea20d
only show total on indiv
iopapamanoglou Oct 17, 2024
3090714
more c++ pef insights
iopapamanoglou Oct 17, 2024
1a35778
optimize parameter merge
iopapamanoglou Oct 17, 2024
8fb4c67
improve deque speed
iopapamanoglou Oct 17, 2024
6d12938
more measuring
iopapamanoglou Oct 17, 2024
4b658a7
preoptimize cgraph; measure graph construct seperately
iopapamanoglou Oct 18, 2024
efb7643
impl optimizations; new test; py == c++ impl
iopapamanoglou Oct 18, 2024
f0a8833
fast edges
iopapamanoglou Oct 18, 2024
7c8547d
linkshallow
iopapamanoglou Oct 18, 2024
90950ae
fix shallow; make const
iopapamanoglou Oct 18, 2024
c37ec7c
split tests
iopapamanoglou Oct 18, 2024
68b63b7
more const
iopapamanoglou Oct 18, 2024
d73405c
reenable graph cache
iopapamanoglou Oct 18, 2024
c6db409
conditionals
iopapamanoglou Oct 18, 2024
88401a2
multi paths
iopapamanoglou Oct 18, 2024
2bd78af
fix implied test
iopapamanoglou Oct 18, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "src/faebryk/core/cpp/pybind11"]
path = src/faebryk/core/cpp/pybind11
url = https://github.com/pybind/pybind11.git
348 changes: 347 additions & 1 deletion poetry.lock

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ pytest = ">=7.1.3,<9.0.0"
viztracer = "^0.16.3"
pyinstrument = "^4.7.1"
gprof2dot = "^2024.6.6"
dash = "^2.18.1"
dash-cytoscape = "^1.0.2"

[tool.pytest.ini_options]
addopts = ["--import-mode=importlib"]
Expand Down
166 changes: 166 additions & 0 deletions src/faebryk/core/bfs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
# This file is part of the faebryk project
# SPDX-License-Identifier: MIT
import itertools
import logging
from collections import defaultdict, deque
from typing import Any, Generator, Iterable, Self

from faebryk.core.graphinterface import GraphInterface
from faebryk.core.link import Link
from faebryk.libs.util import DefaultFactoryDict

logger = logging.getLogger(__name__)


class BFSPath:
link_cache: dict[tuple[GraphInterface, GraphInterface], Link] = DefaultFactoryDict(
lambda x: x[0].is_connected_to(x[1])
)

def __init__(
self,
path: list[GraphInterface],
visited_ref: dict[GraphInterface, Any],
) -> None:
self.path: list[GraphInterface] = path
self.visited_ref: dict[GraphInterface, Any] = visited_ref
self.confidence = 1.0
self.filtered = False
self.path_data: dict[str, Any] = {}
self.stop = False

@property
def last(self) -> GraphInterface:
return self.path[-1]

def get_link(self, edge: tuple[GraphInterface, GraphInterface]) -> Link:
return self.link_cache[edge]

@property
def last_edge(self) -> tuple[GraphInterface, GraphInterface] | None:
if len(self.path) < 2:
return None
return self.path[-2], self.path[-1]

@property
def first(self) -> GraphInterface:
return self.path[0]

@classmethod
def from_base(cls, base: "BFSPath", node: GraphInterface):
out = cls(
base.path + [node],
visited_ref=base.visited_ref,
)
out.confidence = base.confidence
out.filtered = base.filtered
out.path_data = base.path_data
out.stop = base.stop
return out

def __add__(self, node: GraphInterface) -> Self:
return self.from_base(self, node)

def __contains__(self, node: GraphInterface) -> bool:
return node in self.path

@property
def edges(self) -> Iterable[tuple[GraphInterface, GraphInterface]]:
return itertools.pairwise(self.path)

def __len__(self) -> int:
return len(self.path)

def __getitem__(self, idx: int) -> GraphInterface:
return self.path[idx]

# The partial visit stuff is pretty weird, let me try to explain:
# If a node is not fully visited through a path, it means that there might still
# be paths that lead to this node that are more interesting. Thus we give the caller
# the chance to explore those other paths.
# If at a later point the caller discovers that the current path is fully visited
# after all, it can mark it.
@property
def strong(self) -> bool:
return self.confidence == 1.0

def mark_visited(self):
# TODO
for node in self.path:
self.visited_ref[node] = [self]
# self.visited_ref.update(self.path)


def insert_sorted(target: deque | list, item, key, asc=True):
for i, p in enumerate(target):
if (asc and key(item) < key(p)) or (not asc and key(item) > key(p)):
target.insert(i, item)
return i
target.append(item)
return len(target) - 1


def bfs_visit(
roots: Iterable[GraphInterface],
) -> Generator[BFSPath, None, None]:
"""
Generic BFS (not depending on Graph)
Returns all visited nodes.
"""

visited: defaultdict[GraphInterface, list[BFSPath]] = defaultdict(list)
open_path_queue: deque[BFSPath] = deque()

def handle_path(path: BFSPath):
if path.stop:
open_path_queue.clear()
return

if path.filtered:
return

# old_paths = visited[path.last]

# promise_cnt = path.path_data.get("promise_depth", 0)
# for p in old_paths:
# p_cnt = p.path_data.get("promise_depth", 0)
# if promise_cnt > p_cnt:
# print("Pruned")
# return

# def metric(x: BFSPath):
# # promise_cnt = x.path_data.get("promise_depth", 0)
# return (1 - x.confidence), len(x)

# insert_sorted(old_paths, path, key=metric)
visited[path.last]
if path.strong:
path.mark_visited()

# insert_sorted(open_path_queue, path, key=lambda x: (len(x), (1 - x.confidence)))

Check failure on line 140 in src/faebryk/core/bfs.py

View workflow job for this annotation

GitHub Actions / test

Ruff (E501)

src/faebryk/core/bfs.py:140:89: E501 Line too long (90 > 88)
open_path_queue.append(path)

# yield identity paths
for root in roots:
path = BFSPath([root], visited)
yield path
handle_path(path)

while open_path_queue:
open_path = open_path_queue.popleft()

edges = set(open_path.last.edges)
for neighbour in edges:
# visited
if neighbour in visited:
# complete path
if any(x.strong for x in visited[neighbour]):
continue
# visited in path (loop)
if neighbour in open_path:
continue

new_path = open_path + neighbour

yield new_path
handle_path(new_path)
8 changes: 8 additions & 0 deletions src/faebryk/core/cpp/.clang-format
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
Language: Cpp
IndentWidth: 4
UseTab: Never
BreakConstructorInitializers: BeforeComma
ConstructorInitializerIndentWidth: 2
AllowShortFunctionsOnASingleLine: None
AllowShortLambdasOnASingleLine: None
ColumnLimit: 89
21 changes: 21 additions & 0 deletions src/faebryk/core/cpp/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
cmake_minimum_required(VERSION 3.4...3.18)
project(faebryk_core_cpp)

# Specify the Python version you're using
set(PYBIND11_PYTHON_VERSION 3.12)

set(CMAKE_CXX_STANDARD 20)
message(STATUS "C++ version: ${CMAKE_CXX_COMPILER_VERSION}")

# turn on optimization
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O2")

# TODO remove
# enable debug symbols
set(CMAKE_BUILD_TYPE Debug)

add_subdirectory(pybind11)

include_directories(${CMAKE_SOURCE_DIR}/include)

pybind11_add_module(faebryk_core_cpp src/main.cpp)
30 changes: 30 additions & 0 deletions src/faebryk/core/cpp/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# This file is part of the faebryk project
# SPDX-License-Identifier: MIT

import pathlib
import subprocess
import sys

print(f"Python version: {sys.version}")
print(f"Python executable: {sys.executable}")

cpp_dir = pathlib.Path(__file__).parent
pybind11_dir = cpp_dir / "pybind11"
build_dir = cpp_dir / "build"

# check if pybind11 is available
if not pybind11_dir.exists():
raise RuntimeError("pybind11 not found")

# recompile
# subprocess.run(["rm", "-rf", str(build_dir)], check=True)
subprocess.run(["cmake", "-S", str(cpp_dir), "-B", str(build_dir)], check=True)
subprocess.run(["cmake", "--build", str(build_dir)], check=True)

if not build_dir.exists():
raise RuntimeError("build directory not found")

# Add the build directory to Python path
sys.path.append(str(cpp_dir / "build"))

import faebryk_core_cpp # noqa: E402

Check failure on line 30 in src/faebryk/core/cpp/__init__.py

View workflow job for this annotation

GitHub Actions / test

Ruff (F401)

src/faebryk/core/cpp/__init__.py:30:8: F401 `faebryk_core_cpp` imported but unused
Loading
Loading