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

Commit

Permalink
Core: C++ core (#113)
Browse files Browse the repository at this point in the history
Rewrite core (Graph, GraphInterface, Node, Link) in C++.

* Basic Graph infra in C++
* Split cpp files; Parallel compile; GIF & Node functions
* LinkPointer
* add LinkDirectConditional
* use GraphFunctions
* pickerbug: first lcsc attach, then descriptive props
* Remove networkx, cmake glob
* pint 3.13 fix

* Cherry picked features from lazy mifs
- timeout for basic tests
- linkconditional failure enum
- node root id
- extra util: typename, consume, DefaultFactoryDict, ConfigFlagInt
- better erc for shorted power rails
- TBD resolve before eq
- remove mif.get_direct_connections
- parameter merge speedup (check is)
- parameter add is_dynamic trait (no impl yet)
- add multi connect
- make parent name optional
- add linkdirectderived stub
- add is-uplink, is-downlink to gif
- remove poetry.lock
  • Loading branch information
iopapamanoglou authored Nov 7, 2024
1 parent 6827e7d commit 9c4af44
Show file tree
Hide file tree
Showing 133 changed files with 3,092 additions and 3,351 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -99,3 +99,5 @@ _local

# OS stuff
.DS_Store

.envrc
5 changes: 4 additions & 1 deletion examples/iterative_design_nand.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import typer

import faebryk.library._F as F
from faebryk.core.graph import GraphFunctions
from faebryk.core.module import Module
from faebryk.libs.brightness import TypicalLuminousIntensity
from faebryk.libs.examples.buildutil import apply_design_to_pcb
Expand Down Expand Up @@ -119,7 +120,9 @@ def App():
app.add(c)

# parametrizing
for _, t in app.get_graph().nodes_with_trait(F.ElectricLogic.has_pulls):
for _, t in GraphFunctions(app.get_graph()).nodes_with_trait(
F.ElectricLogic.has_pulls
):
for pull_resistor in (r for r in t.get_pulls() if r):
pull_resistor.resistance.merge(F.Range.from_center_rel(100 * P.kohm, 0.05))
power_source.power.voltage.merge(3 * P.V)
Expand Down
2,025 changes: 0 additions & 2,025 deletions poetry.lock

This file was deleted.

17 changes: 12 additions & 5 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
[build-system]
# using scikit-build-core>0.9.3 disables editable mode
requires = ["hatchling", "scikit-build-core==0.9.2", "nanobind~=2.2.0", "hatch-vcs"]
requires = [
"hatchling",
"scikit-build-core==0.9.2",
"nanobind~=2.2.0",
"hatch-vcs",
]
build-backend = "hatchling.build"

[project]
Expand All @@ -17,9 +22,7 @@ classifiers = [
]
requires-python = ">=3.12,<3.13"
dependencies = [
"networkx==3.3",
"numpy>=1.24.3,<3.0.0",
"scipy>=1.11.1,<=1.14.0",
"matplotlib~=3.7.1",
"sexpdata==1.0.2",
"black~=24.4.2",
Expand All @@ -36,7 +39,8 @@ dependencies = [
"typer>=0.12,<0.13",
"isort~=5.6.4",
"ruff>=0.6.4,<0.7.0",
"pint~=0.24.3",
# Waiting for a new release that works on python 3.13
"pint @ git+https://github.com/hgrecco/pint.git@a8bcb6ee1d0d61278bf17e332bc1aa473672e273",
"deprecated~=1.2.14",
"more-itertools~=10.4.0",
"psutil~=6.0.0",
Expand Down Expand Up @@ -66,6 +70,9 @@ faebryk = "faebryk.tools.main:__main__"

[tool.hatch]

[tool.hatch.metadata]
allow-direct-references = true

[tool.hatch.env]
#requires = ["hatch-pip-compile"]

Expand Down Expand Up @@ -93,7 +100,7 @@ cmake.source-dir = "src/faebryk/core/cpp"
[tool.pytest]
[tool.pytest.ini_options]
# loadscope to run tests for each file in same worker
addopts = ["--import-mode=importlib", "--numprocesses=auto", "--dist=loadscope"]
addopts = ["--import-mode=importlib", "--numprocesses=auto"]
filterwarnings = ["ignore:.*:DeprecationWarning"]
testpaths = ["test"]

Expand Down
5 changes: 0 additions & 5 deletions src/faebryk/core/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,6 @@

logger = logging.getLogger(__name__)

LINK_TB = ConfigFlag(
"LINK_TB",
False,
"Save stack trace for each link. Warning: Very slow! Just use for debug",
)
ID_REPR = ConfigFlag("ID_REPR", False, "Add object id to repr")


Expand Down
3 changes: 2 additions & 1 deletion src/faebryk/core/cpp/.clang-format
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ BreakConstructorInitializers: BeforeComma
ConstructorInitializerIndentWidth: 2
AllowShortFunctionsOnASingleLine: None
AllowShortLambdasOnASingleLine: None
ColumnLimit: 89
ColumnLimit: 89
SeparateDefinitionBlocks: Always
7 changes: 6 additions & 1 deletion src/faebryk/core/cpp/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ message(STATUS "Python_EXECUTABLE: ${Python_EXECUTABLE}")
message(STATUS "Python_INCLUDE_DIR: ${Python_INCLUDE_DIR}")
message(STATUS "Python_LIBRARY: ${Python_LIBRARY}")

# To make a debug build, run cmake with -DCMAKE_BUILD_TYPE=Debug
# or in environment: FBRK_CPP_DEBUG_BUILD=y
if (NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
set(CMAKE_BUILD_TYPE Release CACHE STRING "Choose the type of build." FORCE)
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo")
Expand All @@ -56,7 +58,10 @@ if(${EDITABLE} AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
endif()
# source files ---------------------------------------------------------
include_directories(${CMAKE_SOURCE_DIR}/include)
set(SOURCE_FILES src/main.cpp)
file(GLOB_RECURSE SOURCE_FILES
"${CMAKE_SOURCE_DIR}/src/*.cpp"
)


# build ----------------------------------------------------------------
nanobind_add_module(${PROJECT_NAME} ${SOURCE_FILES})
Expand Down
69 changes: 47 additions & 22 deletions src/faebryk/core/cpp/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,15 @@
import logging
from importlib.metadata import Distribution

from faebryk.libs.util import ConfigFlag, at_exit, global_lock

logger = logging.getLogger(__name__)


LEAK_WARNINGS = ConfigFlag("CPP_LEAK_WARNINGS", default=False)
DEBUG_BUILD = ConfigFlag("CPP_DEBUG_BUILD", default=False)


# Check if installed as editable
def is_editable_install():
distro = Distribution.from_name("faebryk")
Expand Down Expand Up @@ -58,27 +64,33 @@ def compile_and_load():
if arch in ["arm64", "x86_64"]:
other_flags += [f"-DCMAKE_OSX_ARCHITECTURES={arch}"]

run_live(
[
"cmake",
"-S",
str(_cmake_dir),
"-B",
str(_build_dir),
"-DEDITABLE=1",
"-DPython_EXECUTABLE=" + sys.executable,
*other_flags,
],
logger=logger,
)
run_live(
[
"cmake",
"--build",
str(_build_dir),
],
logger=logger,
)
if DEBUG_BUILD:
other_flags += ["-DCMAKE_BUILD_TYPE=Debug"]

with global_lock(_build_dir / "lock", timeout_s=60):
run_live(
[
"cmake",
"-S",
str(_cmake_dir),
"-B",
str(_build_dir),
"-DEDITABLE=1",
"-DPython_EXECUTABLE=" + sys.executable,
*other_flags,
],
logger=logger,
)
run_live(
[
"cmake",
"--build",
str(_build_dir),
"--",
"-j",
],
logger=logger,
)

if not _build_dir.exists():
raise RuntimeError("build directory not found")
Expand All @@ -97,7 +109,8 @@ def compile_and_load():
+ "\n"
+ "# This file is auto-generated by nanobind.\n"
+ "# Do not edit this file directly; edit the corresponding\n"
+ "# C++ file instead."
+ "# C++ file instead.\n"
# + "from typing import overload\n"
+ pyi_source.read_text(),
is_pyi=True,
)
Expand All @@ -111,3 +124,15 @@ def compile_and_load():
from faebryk_core_cpp_editable import * # type: ignore # noqa: E402, F403
else:
from faebryk_core_cpp import * # type: ignore # noqa: E402, F403


def cleanup():
if LEAK_WARNINGS:
print("\n--- Nanobind leakage analysis ".ljust(80, "-"))
# nanobind automatically prints leaked objects at exit
from faebryk.core.cpp import set_leak_warnings

set_leak_warnings(bool(LEAK_WARNINGS))


at_exit(cleanup, on_exception=False)
146 changes: 145 additions & 1 deletion src/faebryk/core/cpp/__init__.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,150 @@
# This file is auto-generated by nanobind.
# Do not edit this file directly; edit the corresponding
# C++ file instead.
import enum
from collections.abc import Callable, Sequence, Set
from typing import overload

def add(arg0: int, arg1: int, /) -> int:
class Graph:
def __init__(self) -> None: ...
def get_edges(self, arg: GraphInterface, /) -> dict[GraphInterface, Link]: ...
def invalidate(self) -> None: ...
@property
def node_count(self) -> int: ...
@property
def edge_count(self) -> int: ...
def node_projection(self) -> set[Node]: ...
def nodes_by_names(self, arg: Set[str], /) -> list[tuple[Node, str]]: ...
def bfs_visit(
self,
filter: Callable[[Sequence[GraphInterface], Link], bool],
start: Sequence[GraphInterface],
) -> set[GraphInterface]: ...
def __repr__(self) -> str: ...

class GraphInterface:
def __init__(self) -> None: ...
def __repr__(self) -> str: ...
def get_graph(self) -> Graph: ...
@property
def G(self) -> Graph: ...
def get_gif_edges(self) -> set[GraphInterface]: ...
@property
def edges(self) -> dict[GraphInterface, Link]: ...
@property
def node(self) -> Node: ...
@node.setter
def node(self, arg: Node, /) -> None: ...
def is_connected_to(self, arg: GraphInterface, /) -> Link | None: ...
@property
def name(self) -> str: ...
@name.setter
def name(self, arg: str, /) -> None: ...
def get_connected_nodes(self, types: Sequence[type]) -> set[Node]: ...
@overload
def connect(self, other: GraphInterface) -> None: ...
@overload
def connect(self, others: Sequence[GraphInterface]) -> None: ...
@overload
def connect(self, other: GraphInterface, link: Link) -> None: ...

class GraphInterfaceHierarchical(GraphInterface):
def __init__(self, is_parent: bool) -> None: ...
def get_parent(self) -> tuple[Node, str] | None: ...
def get_children(self) -> list[Node]: ...
@property
def is_parent(self) -> bool: ...
def disconnect_parent(self) -> None: ...

class GraphInterfaceModuleConnection(GraphInterface):
def __init__(self) -> None: ...

class GraphInterfaceModuleSibling(GraphInterfaceHierarchical):
def __init__(self, is_parent: bool) -> None: ...

class GraphInterfaceReference(GraphInterface):
def __init__(self) -> None: ...
def get_referenced_gif(self) -> GraphInterfaceSelf: ...
def get_reference(self) -> Node: ...

class GraphInterfaceReferenceUnboundError(Exception):
pass

class GraphInterfaceSelf(GraphInterface):
def __init__(self) -> None: ...

class Link:
pass

class LinkDirect(Link):
def __init__(self) -> None: ...

class LinkDirectConditional(LinkDirect):
def __init__(
self,
arg: Callable[
[GraphInterface, GraphInterface], LinkDirectConditionalFilterResult
],
/,
) -> None: ...

class LinkDirectConditionalFilterResult(enum.Enum):
FILTER_PASS = 0

FILTER_FAIL_RECOVERABLE = 1

FILTER_FAIL_UNRECOVERABLE = 2

class LinkFilteredException(Exception):
pass

class LinkNamedParent(LinkParent):
def __init__(self, arg: str, /) -> None: ...

class LinkParent(Link):
def __init__(self) -> None: ...

class LinkPointer(Link):
def __init__(self) -> None: ...

class LinkSibling(LinkPointer):
def __init__(self) -> None: ...

class Node:
def __init__(self) -> None: ...
@staticmethod
def transfer_ownership(arg: Node, /) -> Node: ...
def get_graph(self) -> Graph: ...
@property
def self_gif(self) -> GraphInterfaceSelf: ...
@property
def children(self) -> GraphInterfaceHierarchical: ...
@property
def parent(self) -> GraphInterfaceHierarchical: ...
def get_children(
self,
direct_only: bool,
types: Sequence[type] | None = None,
include_root: bool = False,
f_filter: Callable[[Node], bool] | None = None,
sort: bool = True,
) -> list[Node]: ...
def get_parent(self) -> tuple[Node, str] | None: ...
def get_parent_force(self) -> tuple[Node, str]: ...
def get_name(self, accept_no_parent: bool = False) -> str: ...
def get_hierarchy(self) -> list[tuple[Node, str]]: ...
def get_full_name(self, types: bool = False) -> str: ...
def __repr__(self) -> str: ...

class NodeException(Exception):
pass

class NodeNoParent(Exception):
pass

def add(i: int, j: int = 1) -> int:
"""A function that adds two numbers"""

def call_python_function(func: Callable[[], int]) -> int: ...
def print_obj(obj: object) -> None: ...
def set_leak_warnings(value: bool) -> None: ...
7 changes: 7 additions & 0 deletions src/faebryk/core/cpp/cpp_helpers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# This file is part of the faebryk project
# SPDX-License-Identifier: MIT


import logging

logger = logging.getLogger(__name__)
Loading

0 comments on commit 9c4af44

Please sign in to comment.