Spaces:
Running
Running
Merge pull request #168 from biggraph/darabos-ws-docs
Browse files- examples/fake_data.py +5 -0
- lynxkite-app/pyproject.toml +1 -0
- lynxkite-app/web/package-lock.json +46 -0
- lynxkite-app/web/package.json +1 -0
- lynxkite-app/web/src/Tooltip.tsx +21 -0
- lynxkite-app/web/src/index.css +59 -20
- lynxkite-app/web/src/workspace/LynxKiteEdge.tsx +30 -0
- lynxkite-app/web/src/workspace/Workspace.tsx +9 -1
- lynxkite-app/web/src/workspace/nodes/LynxKiteNode.tsx +25 -4
- lynxkite-app/web/src/workspace/nodes/NodeParameter.tsx +36 -9
- lynxkite-core/pyproject.toml +1 -0
- lynxkite-core/src/lynxkite/core/ops.py +22 -3
- lynxkite-graph-analytics/src/lynxkite_graph_analytics/lynxkite_ops.py +3 -8
- lynxkite-graph-analytics/src/lynxkite_graph_analytics/pytorch/pytorch_ops.py +3 -1
examples/fake_data.py
CHANGED
|
@@ -7,6 +7,11 @@ faker = Faker()
|
|
| 7 |
|
| 8 |
@op("LynxKite Graph Analytics", "Fake data")
|
| 9 |
def fake(*, n=10):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 10 |
df = pd.DataFrame(
|
| 11 |
{
|
| 12 |
"name": [faker.name() for _ in range(n)],
|
|
|
|
| 7 |
|
| 8 |
@op("LynxKite Graph Analytics", "Fake data")
|
| 9 |
def fake(*, n=10):
|
| 10 |
+
"""Creates a DataFrame with random-generated names and postal addresses.
|
| 11 |
+
|
| 12 |
+
Parameters:
|
| 13 |
+
n: Number of rows to create.
|
| 14 |
+
"""
|
| 15 |
df = pd.DataFrame(
|
| 16 |
{
|
| 17 |
"name": [faker.name() for _ in range(n)],
|
lynxkite-app/pyproject.toml
CHANGED
|
@@ -10,6 +10,7 @@ dependencies = [
|
|
| 10 |
"orjson>=3.10.13",
|
| 11 |
"pycrdt-websocket>=0.15.3",
|
| 12 |
"sse-starlette>=2.2.1",
|
|
|
|
| 13 |
]
|
| 14 |
|
| 15 |
[project.optional-dependencies]
|
|
|
|
| 10 |
"orjson>=3.10.13",
|
| 11 |
"pycrdt-websocket>=0.15.3",
|
| 12 |
"sse-starlette>=2.2.1",
|
| 13 |
+
"griffe>=1.7.3",
|
| 14 |
]
|
| 15 |
|
| 16 |
[project.optional-dependencies]
|
lynxkite-app/web/package-lock.json
CHANGED
|
@@ -30,6 +30,7 @@
|
|
| 30 |
"react-error-boundary": "^5.0.0",
|
| 31 |
"react-markdown": "^9.0.1",
|
| 32 |
"react-router-dom": "^7.5.2",
|
|
|
|
| 33 |
"swr": "^2.2.5",
|
| 34 |
"unplugin-icons": "^0.21.0",
|
| 35 |
"y-monaco": "^0.1.6",
|
|
@@ -940,6 +941,31 @@
|
|
| 940 |
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
| 941 |
}
|
| 942 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 943 |
"node_modules/@humanfs/core": {
|
| 944 |
"version": "0.19.1",
|
| 945 |
"resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
|
|
@@ -2879,6 +2905,12 @@
|
|
| 2879 |
"integrity": "sha512-JhZUT7JFcQy/EzW605k/ktHtncoo9vnyW/2GspNYwFlN1C/WmjuV/xtS04e9SOkL2sTdw0VAZ2UGCcQ9lR6p6w==",
|
| 2880 |
"license": "MIT"
|
| 2881 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2882 |
"node_modules/client-only": {
|
| 2883 |
"version": "0.0.1",
|
| 2884 |
"resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz",
|
|
@@ -6239,6 +6271,20 @@
|
|
| 6239 |
"react-dom": ">=18"
|
| 6240 |
}
|
| 6241 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 6242 |
"node_modules/read-cache": {
|
| 6243 |
"version": "1.0.0",
|
| 6244 |
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
|
|
|
|
| 30 |
"react-error-boundary": "^5.0.0",
|
| 31 |
"react-markdown": "^9.0.1",
|
| 32 |
"react-router-dom": "^7.5.2",
|
| 33 |
+
"react-tooltip": "^5.28.1",
|
| 34 |
"swr": "^2.2.5",
|
| 35 |
"unplugin-icons": "^0.21.0",
|
| 36 |
"y-monaco": "^0.1.6",
|
|
|
|
| 941 |
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
| 942 |
}
|
| 943 |
},
|
| 944 |
+
"node_modules/@floating-ui/core": {
|
| 945 |
+
"version": "1.6.9",
|
| 946 |
+
"resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.9.tgz",
|
| 947 |
+
"integrity": "sha512-uMXCuQ3BItDUbAMhIXw7UPXRfAlOAvZzdK9BWpE60MCn+Svt3aLn9jsPTi/WNGlRUu2uI0v5S7JiIUsbsvh3fw==",
|
| 948 |
+
"license": "MIT",
|
| 949 |
+
"dependencies": {
|
| 950 |
+
"@floating-ui/utils": "^0.2.9"
|
| 951 |
+
}
|
| 952 |
+
},
|
| 953 |
+
"node_modules/@floating-ui/dom": {
|
| 954 |
+
"version": "1.6.13",
|
| 955 |
+
"resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.13.tgz",
|
| 956 |
+
"integrity": "sha512-umqzocjDgNRGTuO7Q8CU32dkHkECqI8ZdMZ5Swb6QAM0t5rnlrN3lGo1hdpscRd3WS8T6DKYK4ephgIH9iRh3w==",
|
| 957 |
+
"license": "MIT",
|
| 958 |
+
"dependencies": {
|
| 959 |
+
"@floating-ui/core": "^1.6.0",
|
| 960 |
+
"@floating-ui/utils": "^0.2.9"
|
| 961 |
+
}
|
| 962 |
+
},
|
| 963 |
+
"node_modules/@floating-ui/utils": {
|
| 964 |
+
"version": "0.2.9",
|
| 965 |
+
"resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.9.tgz",
|
| 966 |
+
"integrity": "sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==",
|
| 967 |
+
"license": "MIT"
|
| 968 |
+
},
|
| 969 |
"node_modules/@humanfs/core": {
|
| 970 |
"version": "0.19.1",
|
| 971 |
"resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
|
|
|
|
| 2905 |
"integrity": "sha512-JhZUT7JFcQy/EzW605k/ktHtncoo9vnyW/2GspNYwFlN1C/WmjuV/xtS04e9SOkL2sTdw0VAZ2UGCcQ9lR6p6w==",
|
| 2906 |
"license": "MIT"
|
| 2907 |
},
|
| 2908 |
+
"node_modules/classnames": {
|
| 2909 |
+
"version": "2.5.1",
|
| 2910 |
+
"resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz",
|
| 2911 |
+
"integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==",
|
| 2912 |
+
"license": "MIT"
|
| 2913 |
+
},
|
| 2914 |
"node_modules/client-only": {
|
| 2915 |
"version": "0.0.1",
|
| 2916 |
"resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz",
|
|
|
|
| 6271 |
"react-dom": ">=18"
|
| 6272 |
}
|
| 6273 |
},
|
| 6274 |
+
"node_modules/react-tooltip": {
|
| 6275 |
+
"version": "5.28.1",
|
| 6276 |
+
"resolved": "https://registry.npmjs.org/react-tooltip/-/react-tooltip-5.28.1.tgz",
|
| 6277 |
+
"integrity": "sha512-ZA4oHwoIIK09TS7PvSLFcRlje1wGZaxw6xHvfrzn6T82UcMEfEmHVCad16Gnr4NDNDh93HyN037VK4HDi5odfQ==",
|
| 6278 |
+
"license": "MIT",
|
| 6279 |
+
"dependencies": {
|
| 6280 |
+
"@floating-ui/dom": "^1.6.1",
|
| 6281 |
+
"classnames": "^2.3.0"
|
| 6282 |
+
},
|
| 6283 |
+
"peerDependencies": {
|
| 6284 |
+
"react": ">=16.14.0",
|
| 6285 |
+
"react-dom": ">=16.14.0"
|
| 6286 |
+
}
|
| 6287 |
+
},
|
| 6288 |
"node_modules/read-cache": {
|
| 6289 |
"version": "1.0.0",
|
| 6290 |
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
|
lynxkite-app/web/package.json
CHANGED
|
@@ -33,6 +33,7 @@
|
|
| 33 |
"react-error-boundary": "^5.0.0",
|
| 34 |
"react-markdown": "^9.0.1",
|
| 35 |
"react-router-dom": "^7.5.2",
|
|
|
|
| 36 |
"swr": "^2.2.5",
|
| 37 |
"unplugin-icons": "^0.21.0",
|
| 38 |
"y-monaco": "^0.1.6",
|
|
|
|
| 33 |
"react-error-boundary": "^5.0.0",
|
| 34 |
"react-markdown": "^9.0.1",
|
| 35 |
"react-router-dom": "^7.5.2",
|
| 36 |
+
"react-tooltip": "^5.28.1",
|
| 37 |
"swr": "^2.2.5",
|
| 38 |
"unplugin-icons": "^0.21.0",
|
| 39 |
"y-monaco": "^0.1.6",
|
lynxkite-app/web/src/Tooltip.tsx
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { useId } from "react";
|
| 2 |
+
import Markdown from "react-markdown";
|
| 3 |
+
import { Tooltip as ReactTooltip } from "react-tooltip";
|
| 4 |
+
|
| 5 |
+
export default function Tooltip(props: any) {
|
| 6 |
+
const id = useId();
|
| 7 |
+
if (!props.doc) return null;
|
| 8 |
+
return (
|
| 9 |
+
<>
|
| 10 |
+
<a data-tooltip-id={id} tabIndex={0}>
|
| 11 |
+
{props.children}
|
| 12 |
+
</a>
|
| 13 |
+
<ReactTooltip id={id} className="tooltip" place="top-end">
|
| 14 |
+
{props.doc.map?.(
|
| 15 |
+
(section: any, i: number) =>
|
| 16 |
+
section.kind === "text" && <Markdown key={i}>{section.value}</Markdown>,
|
| 17 |
+
) ?? <Markdown>{props.doc}</Markdown>}
|
| 18 |
+
</ReactTooltip>
|
| 19 |
+
</>
|
| 20 |
+
);
|
| 21 |
+
}
|
lynxkite-app/web/src/index.css
CHANGED
|
@@ -68,11 +68,6 @@ body {
|
|
| 68 |
font-size: 12px;
|
| 69 |
}
|
| 70 |
|
| 71 |
-
.title-icon {
|
| 72 |
-
margin-left: 5px;
|
| 73 |
-
float: right;
|
| 74 |
-
}
|
| 75 |
-
|
| 76 |
.node-container {
|
| 77 |
padding: 8px;
|
| 78 |
position: relative;
|
|
@@ -93,8 +88,21 @@ body {
|
|
| 93 |
}
|
| 94 |
}
|
| 95 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 96 |
.expanded .lynxkite-node {
|
| 97 |
-
overflow-y: auto;
|
| 98 |
height: 100%;
|
| 99 |
}
|
| 100 |
|
|
@@ -114,6 +122,13 @@ body {
|
|
| 114 |
--status-color-2: #0000;
|
| 115 |
--status-color-3: #0000;
|
| 116 |
transition: --status-color-1 0.3s, --status-color-2 0.3s, --status-color-3 0.3s;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 117 |
}
|
| 118 |
|
| 119 |
.lynxkite-node .title.active {
|
|
@@ -143,24 +158,40 @@ body {
|
|
| 143 |
visibility: hidden;
|
| 144 |
}
|
| 145 |
|
| 146 |
-
.react-flow__handle-left
|
| 147 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 148 |
}
|
| 149 |
|
| 150 |
-
.react-flow__handle-right
|
| 151 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 152 |
}
|
| 153 |
|
| 154 |
-
.react-flow__handle-top
|
| 155 |
-
top: -
|
| 156 |
-
|
| 157 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 158 |
}
|
| 159 |
|
| 160 |
-
.react-flow__handle-bottom
|
| 161 |
-
|
| 162 |
-
|
| 163 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 164 |
}
|
| 165 |
|
| 166 |
.node-container:hover .handle-name {
|
|
@@ -180,6 +211,13 @@ body {
|
|
| 180 |
display: block;
|
| 181 |
}
|
| 182 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 183 |
.param-name {
|
| 184 |
display: block;
|
| 185 |
font-size: 10px;
|
|
@@ -453,8 +491,9 @@ body {
|
|
| 453 |
.react-flow__handle {
|
| 454 |
border-color: black;
|
| 455 |
background: white;
|
| 456 |
-
width:
|
| 457 |
-
height:
|
|
|
|
| 458 |
}
|
| 459 |
|
| 460 |
.react-flow__arrowhead * {
|
|
|
|
| 68 |
font-size: 12px;
|
| 69 |
}
|
| 70 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 71 |
.node-container {
|
| 72 |
padding: 8px;
|
| 73 |
position: relative;
|
|
|
|
| 88 |
}
|
| 89 |
}
|
| 90 |
|
| 91 |
+
.tooltip {
|
| 92 |
+
padding: 8px;
|
| 93 |
+
border-radius: 4px;
|
| 94 |
+
opacity: 1;
|
| 95 |
+
text-align: left;
|
| 96 |
+
background: #fffa;
|
| 97 |
+
color: black;
|
| 98 |
+
box-shadow: 0px 5px 50px 0px rgba(0, 0, 0, 0.1);
|
| 99 |
+
backdrop-filter: blur(10px);
|
| 100 |
+
font-size: 16px;
|
| 101 |
+
font-weight: initial;
|
| 102 |
+
max-width: 300px;
|
| 103 |
+
}
|
| 104 |
+
|
| 105 |
.expanded .lynxkite-node {
|
|
|
|
| 106 |
height: 100%;
|
| 107 |
}
|
| 108 |
|
|
|
|
| 122 |
--status-color-2: #0000;
|
| 123 |
--status-color-3: #0000;
|
| 124 |
transition: --status-color-1 0.3s, --status-color-2 0.3s, --status-color-3 0.3s;
|
| 125 |
+
display: flex;
|
| 126 |
+
flex-direction: row;
|
| 127 |
+
gap: 10px;
|
| 128 |
+
|
| 129 |
+
.title-title {
|
| 130 |
+
flex: 1;
|
| 131 |
+
}
|
| 132 |
}
|
| 133 |
|
| 134 |
.lynxkite-node .title.active {
|
|
|
|
| 158 |
visibility: hidden;
|
| 159 |
}
|
| 160 |
|
| 161 |
+
.react-flow__handle-left {
|
| 162 |
+
left: -5px;
|
| 163 |
+
|
| 164 |
+
.handle-name {
|
| 165 |
+
right: 30px;
|
| 166 |
+
}
|
| 167 |
}
|
| 168 |
|
| 169 |
+
.react-flow__handle-right {
|
| 170 |
+
right: -5px;
|
| 171 |
+
|
| 172 |
+
.handle-name {
|
| 173 |
+
left: 30px;
|
| 174 |
+
}
|
| 175 |
}
|
| 176 |
|
| 177 |
+
.react-flow__handle-top {
|
| 178 |
+
top: -5px;
|
| 179 |
+
|
| 180 |
+
.handle-name {
|
| 181 |
+
top: -3px;
|
| 182 |
+
left: 13px;
|
| 183 |
+
backdrop-filter: none;
|
| 184 |
+
}
|
| 185 |
}
|
| 186 |
|
| 187 |
+
.react-flow__handle-bottom {
|
| 188 |
+
bottom: -5px;
|
| 189 |
+
|
| 190 |
+
.handle-name {
|
| 191 |
+
top: 0px;
|
| 192 |
+
left: 13px;
|
| 193 |
+
backdrop-filter: none;
|
| 194 |
+
}
|
| 195 |
}
|
| 196 |
|
| 197 |
.node-container:hover .handle-name {
|
|
|
|
| 211 |
display: block;
|
| 212 |
}
|
| 213 |
|
| 214 |
+
.param-name-row {
|
| 215 |
+
display: flex;
|
| 216 |
+
flex-direction: row;
|
| 217 |
+
justify-content: space-between;
|
| 218 |
+
align-items: end;
|
| 219 |
+
}
|
| 220 |
+
|
| 221 |
.param-name {
|
| 222 |
display: block;
|
| 223 |
font-size: 10px;
|
|
|
|
| 491 |
.react-flow__handle {
|
| 492 |
border-color: black;
|
| 493 |
background: white;
|
| 494 |
+
width: 20px;
|
| 495 |
+
height: 20px;
|
| 496 |
+
border-width: 2px;
|
| 497 |
}
|
| 498 |
|
| 499 |
.react-flow__arrowhead * {
|
lynxkite-app/web/src/workspace/LynxKiteEdge.tsx
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { BaseEdge, Position } from "@xyflow/react";
|
| 2 |
+
|
| 3 |
+
function addOffset(x: number, y: number, p: Position, offset: number) {
|
| 4 |
+
if (p === Position.Top) return `${x},${y - offset}`;
|
| 5 |
+
if (p === Position.Bottom) return `${x},${y + offset}`;
|
| 6 |
+
if (p === Position.Left) return `${x - offset},${y}`;
|
| 7 |
+
return `${x + offset},${y}`;
|
| 8 |
+
}
|
| 9 |
+
|
| 10 |
+
export default function LynxKiteEdge(props: any) {
|
| 11 |
+
const offset = 0.3 * Math.hypot(props.targetX - props.sourceX, props.targetY - props.sourceY);
|
| 12 |
+
const s = addOffset(props.sourceX, props.sourceY, props.sourcePosition, 0);
|
| 13 |
+
const sc = addOffset(props.sourceX, props.sourceY, props.sourcePosition, offset);
|
| 14 |
+
const tc = addOffset(props.targetX, props.targetY, props.targetPosition, offset);
|
| 15 |
+
const t = addOffset(props.targetX, props.targetY, props.targetPosition, 0);
|
| 16 |
+
const path = `M${s} C${sc} ${tc} ${t}`;
|
| 17 |
+
return (
|
| 18 |
+
<>
|
| 19 |
+
<BaseEdge
|
| 20 |
+
id={props.id}
|
| 21 |
+
path={path}
|
| 22 |
+
{...props}
|
| 23 |
+
style={{
|
| 24 |
+
strokeWidth: 2,
|
| 25 |
+
stroke: "black",
|
| 26 |
+
}}
|
| 27 |
+
/>
|
| 28 |
+
</>
|
| 29 |
+
);
|
| 30 |
+
}
|
lynxkite-app/web/src/workspace/Workspace.tsx
CHANGED
|
@@ -34,6 +34,7 @@ import favicon from "../assets/favicon.ico";
|
|
| 34 |
import { usePath } from "../common.ts";
|
| 35 |
// import NodeWithTableView from './NodeWithTableView';
|
| 36 |
import EnvironmentSelector from "./EnvironmentSelector";
|
|
|
|
| 37 |
import { LynxKiteState } from "./LynxKiteState";
|
| 38 |
import NodeSearch, { type OpsOp, type Catalog, type Catalogs } from "./NodeSearch.tsx";
|
| 39 |
import NodeWithGraphCreationView from "./nodes/GraphCreationNode.tsx";
|
|
@@ -186,6 +187,12 @@ function LynxKiteFlow() {
|
|
| 186 |
}),
|
| 187 |
[],
|
| 188 |
);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 189 |
|
| 190 |
// Global keyboard shortcuts.
|
| 191 |
useEffect(() => {
|
|
@@ -280,7 +287,7 @@ function LynxKiteFlow() {
|
|
| 280 |
(connection: Connection) => {
|
| 281 |
setSuppressSearchUntil(Date.now() + 200);
|
| 282 |
const edge = {
|
| 283 |
-
id: `${connection.source} ${connection.target}`,
|
| 284 |
source: connection.source,
|
| 285 |
sourceHandle: connection.sourceHandle!,
|
| 286 |
target: connection.target,
|
|
@@ -374,6 +381,7 @@ function LynxKiteFlow() {
|
|
| 374 |
nodes={nodes}
|
| 375 |
edges={edges}
|
| 376 |
nodeTypes={nodeTypes}
|
|
|
|
| 377 |
fitView
|
| 378 |
onNodesChange={onNodesChange}
|
| 379 |
onEdgesChange={onEdgesChange}
|
|
|
|
| 34 |
import { usePath } from "../common.ts";
|
| 35 |
// import NodeWithTableView from './NodeWithTableView';
|
| 36 |
import EnvironmentSelector from "./EnvironmentSelector";
|
| 37 |
+
import LynxKiteEdge from "./LynxKiteEdge.tsx";
|
| 38 |
import { LynxKiteState } from "./LynxKiteState";
|
| 39 |
import NodeSearch, { type OpsOp, type Catalog, type Catalogs } from "./NodeSearch.tsx";
|
| 40 |
import NodeWithGraphCreationView from "./nodes/GraphCreationNode.tsx";
|
|
|
|
| 187 |
}),
|
| 188 |
[],
|
| 189 |
);
|
| 190 |
+
const edgeTypes = useMemo(
|
| 191 |
+
() => ({
|
| 192 |
+
default: LynxKiteEdge,
|
| 193 |
+
}),
|
| 194 |
+
[],
|
| 195 |
+
);
|
| 196 |
|
| 197 |
// Global keyboard shortcuts.
|
| 198 |
useEffect(() => {
|
|
|
|
| 287 |
(connection: Connection) => {
|
| 288 |
setSuppressSearchUntil(Date.now() + 200);
|
| 289 |
const edge = {
|
| 290 |
+
id: `${connection.source} ${connection.sourceHandle} ${connection.target} ${connection.targetHandle}`,
|
| 291 |
source: connection.source,
|
| 292 |
sourceHandle: connection.sourceHandle!,
|
| 293 |
target: connection.target,
|
|
|
|
| 381 |
nodes={nodes}
|
| 382 |
edges={edges}
|
| 383 |
nodeTypes={nodeTypes}
|
| 384 |
+
edgeTypes={edgeTypes}
|
| 385 |
fitView
|
| 386 |
onNodesChange={onNodesChange}
|
| 387 |
onEdgesChange={onEdgesChange}
|
lynxkite-app/web/src/workspace/nodes/LynxKiteNode.tsx
CHANGED
|
@@ -1,9 +1,17 @@
|
|
| 1 |
import { Handle, NodeResizeControl, type Position, useReactFlow } from "@xyflow/react";
|
|
|
|
| 2 |
import { ErrorBoundary } from "react-error-boundary";
|
| 3 |
// @ts-ignore
|
|
|
|
|
|
|
| 4 |
import ChevronDownRight from "~icons/tabler/chevron-down-right.jsx";
|
| 5 |
// @ts-ignore
|
|
|
|
|
|
|
|
|
|
|
|
|
| 6 |
import Skull from "~icons/tabler/skull.jsx";
|
|
|
|
| 7 |
|
| 8 |
interface LynxKiteNodeProps {
|
| 9 |
id: string;
|
|
@@ -44,9 +52,11 @@ function getHandles(inputs: any[], outputs: any[]) {
|
|
| 44 |
}
|
| 45 |
|
| 46 |
const OP_COLORS: { [key: string]: string } = {
|
|
|
|
| 47 |
orange: "oklch(75% 0.2 55)",
|
|
|
|
| 48 |
blue: "oklch(75% 0.2 230)",
|
| 49 |
-
|
| 50 |
};
|
| 51 |
|
| 52 |
function LynxKiteNodeComponent(props: LynxKiteNodeProps) {
|
|
@@ -81,9 +91,20 @@ function LynxKiteNodeComponent(props: LynxKiteNodeProps) {
|
|
| 81 |
style={titleStyle}
|
| 82 |
onClick={titleClicked}
|
| 83 |
>
|
| 84 |
-
{data.title}
|
| 85 |
-
{data.error &&
|
| 86 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 87 |
</div>
|
| 88 |
{expanded && (
|
| 89 |
<>
|
|
|
|
| 1 |
import { Handle, NodeResizeControl, type Position, useReactFlow } from "@xyflow/react";
|
| 2 |
+
import type React from "react";
|
| 3 |
import { ErrorBoundary } from "react-error-boundary";
|
| 4 |
// @ts-ignore
|
| 5 |
+
import AlertTriangle from "~icons/tabler/alert-triangle-filled.jsx";
|
| 6 |
+
// @ts-ignore
|
| 7 |
import ChevronDownRight from "~icons/tabler/chevron-down-right.jsx";
|
| 8 |
// @ts-ignore
|
| 9 |
+
import Dots from "~icons/tabler/dots.jsx";
|
| 10 |
+
// @ts-ignore
|
| 11 |
+
import Help from "~icons/tabler/question-mark.jsx";
|
| 12 |
+
// @ts-ignore
|
| 13 |
import Skull from "~icons/tabler/skull.jsx";
|
| 14 |
+
import Tooltip from "../../Tooltip";
|
| 15 |
|
| 16 |
interface LynxKiteNodeProps {
|
| 17 |
id: string;
|
|
|
|
| 52 |
}
|
| 53 |
|
| 54 |
const OP_COLORS: { [key: string]: string } = {
|
| 55 |
+
pink: "oklch(75% 0.2 0)",
|
| 56 |
orange: "oklch(75% 0.2 55)",
|
| 57 |
+
green: "oklch(75% 0.2 150)",
|
| 58 |
blue: "oklch(75% 0.2 230)",
|
| 59 |
+
purple: "oklch(75% 0.2 290)",
|
| 60 |
};
|
| 61 |
|
| 62 |
function LynxKiteNodeComponent(props: LynxKiteNodeProps) {
|
|
|
|
| 91 |
style={titleStyle}
|
| 92 |
onClick={titleClicked}
|
| 93 |
>
|
| 94 |
+
<span className="title-title">{data.title}</span>
|
| 95 |
+
{data.error && (
|
| 96 |
+
<Tooltip doc={`Error: ${data.error}`}>
|
| 97 |
+
<AlertTriangle />
|
| 98 |
+
</Tooltip>
|
| 99 |
+
)}
|
| 100 |
+
{expanded || (
|
| 101 |
+
<Tooltip doc="Click to expand node">
|
| 102 |
+
<Dots />
|
| 103 |
+
</Tooltip>
|
| 104 |
+
)}
|
| 105 |
+
<Tooltip doc={data.meta?.value?.doc}>
|
| 106 |
+
<Help />
|
| 107 |
+
</Tooltip>
|
| 108 |
</div>
|
| 109 |
{expanded && (
|
| 110 |
<>
|
lynxkite-app/web/src/workspace/nodes/NodeParameter.tsx
CHANGED
|
@@ -1,6 +1,9 @@
|
|
| 1 |
import { useRef } from "react";
|
| 2 |
// @ts-ignore
|
| 3 |
import ArrowsHorizontal from "~icons/tabler/arrows-horizontal.jsx";
|
|
|
|
|
|
|
|
|
|
| 4 |
import NodeGroupParameter from "./NodeGroupParameter";
|
| 5 |
|
| 6 |
const BOOLEAN = "<class 'bool'>";
|
|
@@ -9,8 +12,19 @@ const MODEL_TRAINING_INPUT_MAPPING =
|
|
| 9 |
const MODEL_INFERENCE_INPUT_MAPPING =
|
| 10 |
"<class 'lynxkite_graph_analytics.ml_ops.ModelInferenceInputMapping'>";
|
| 11 |
const MODEL_OUTPUT_MAPPING = "<class 'lynxkite_graph_analytics.ml_ops.ModelOutputMapping'>";
|
| 12 |
-
|
| 13 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 14 |
}
|
| 15 |
|
| 16 |
function Input({
|
|
@@ -195,18 +209,31 @@ interface NodeParameterProps {
|
|
| 195 |
|
| 196 |
export type UpdateOptions = { delay?: number };
|
| 197 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 198 |
export default function NodeParameter({ name, value, meta, data, setParam }: NodeParameterProps) {
|
|
|
|
| 199 |
function onChange(value: any, opts?: UpdateOptions) {
|
| 200 |
setParam(meta.name, value, opts || {});
|
| 201 |
}
|
| 202 |
return meta?.type?.format === "collapsed" ? (
|
| 203 |
<label className="param">
|
| 204 |
-
<ParamName name={name} />
|
| 205 |
<button className="collapsed-param">⋯</button>
|
| 206 |
</label>
|
| 207 |
) : meta?.type?.format === "textarea" ? (
|
| 208 |
<label className="param">
|
| 209 |
-
<ParamName name={name} />
|
| 210 |
<textarea
|
| 211 |
className="textarea textarea-bordered w-full"
|
| 212 |
rows={6}
|
|
@@ -219,7 +246,7 @@ export default function NodeParameter({ name, value, meta, data, setParam }: Nod
|
|
| 219 |
<NodeGroupParameter meta={meta} data={data} setParam={setParam} />
|
| 220 |
) : meta?.type?.enum ? (
|
| 221 |
<label className="param">
|
| 222 |
-
<ParamName name={name} />
|
| 223 |
<select
|
| 224 |
className="select select-bordered w-full"
|
| 225 |
value={value || meta.type.enum[0]}
|
|
@@ -246,22 +273,22 @@ export default function NodeParameter({ name, value, meta, data, setParam }: Nod
|
|
| 246 |
</div>
|
| 247 |
) : meta?.type?.type === MODEL_TRAINING_INPUT_MAPPING ? (
|
| 248 |
<label className="param">
|
| 249 |
-
<ParamName name={name} />
|
| 250 |
<ModelMapping value={value} data={data} variant="training input" onChange={onChange} />
|
| 251 |
</label>
|
| 252 |
) : meta?.type?.type === MODEL_INFERENCE_INPUT_MAPPING ? (
|
| 253 |
<label className="param">
|
| 254 |
-
<ParamName name={name} />
|
| 255 |
<ModelMapping value={value} data={data} variant="inference input" onChange={onChange} />
|
| 256 |
</label>
|
| 257 |
) : meta?.type?.type === MODEL_OUTPUT_MAPPING ? (
|
| 258 |
<label className="param">
|
| 259 |
-
<ParamName name={name} />
|
| 260 |
<ModelMapping value={value} data={data} variant="output" onChange={onChange} />
|
| 261 |
</label>
|
| 262 |
) : (
|
| 263 |
<label className="param">
|
| 264 |
-
<ParamName name={name} />
|
| 265 |
<Input value={value} onChange={onChange} />
|
| 266 |
</label>
|
| 267 |
);
|
|
|
|
| 1 |
import { useRef } from "react";
|
| 2 |
// @ts-ignore
|
| 3 |
import ArrowsHorizontal from "~icons/tabler/arrows-horizontal.jsx";
|
| 4 |
+
// @ts-ignore
|
| 5 |
+
import Help from "~icons/tabler/question-mark.jsx";
|
| 6 |
+
import Tooltip from "../../Tooltip";
|
| 7 |
import NodeGroupParameter from "./NodeGroupParameter";
|
| 8 |
|
| 9 |
const BOOLEAN = "<class 'bool'>";
|
|
|
|
| 12 |
const MODEL_INFERENCE_INPUT_MAPPING =
|
| 13 |
"<class 'lynxkite_graph_analytics.ml_ops.ModelInferenceInputMapping'>";
|
| 14 |
const MODEL_OUTPUT_MAPPING = "<class 'lynxkite_graph_analytics.ml_ops.ModelOutputMapping'>";
|
| 15 |
+
|
| 16 |
+
function ParamName({ name, doc }: { name: string; doc: string }) {
|
| 17 |
+
const help = doc && (
|
| 18 |
+
<Tooltip doc={doc} width={200}>
|
| 19 |
+
<Help />
|
| 20 |
+
</Tooltip>
|
| 21 |
+
);
|
| 22 |
+
return (
|
| 23 |
+
<div className="param-name-row">
|
| 24 |
+
<span className="param-name bg-base-200">{name.replace(/_/g, " ")}</span>
|
| 25 |
+
{help}
|
| 26 |
+
</div>
|
| 27 |
+
);
|
| 28 |
}
|
| 29 |
|
| 30 |
function Input({
|
|
|
|
| 209 |
|
| 210 |
export type UpdateOptions = { delay?: number };
|
| 211 |
|
| 212 |
+
function findDocs(docs: any, parameter: string) {
|
| 213 |
+
for (const sec of docs) {
|
| 214 |
+
if (sec.kind === "parameters") {
|
| 215 |
+
for (const p of sec.value) {
|
| 216 |
+
if (p.name === parameter) {
|
| 217 |
+
return p.description;
|
| 218 |
+
}
|
| 219 |
+
}
|
| 220 |
+
}
|
| 221 |
+
}
|
| 222 |
+
}
|
| 223 |
+
|
| 224 |
export default function NodeParameter({ name, value, meta, data, setParam }: NodeParameterProps) {
|
| 225 |
+
const doc = findDocs(data.meta?.value?.doc ?? [], name);
|
| 226 |
function onChange(value: any, opts?: UpdateOptions) {
|
| 227 |
setParam(meta.name, value, opts || {});
|
| 228 |
}
|
| 229 |
return meta?.type?.format === "collapsed" ? (
|
| 230 |
<label className="param">
|
| 231 |
+
<ParamName name={name} doc={doc} />
|
| 232 |
<button className="collapsed-param">⋯</button>
|
| 233 |
</label>
|
| 234 |
) : meta?.type?.format === "textarea" ? (
|
| 235 |
<label className="param">
|
| 236 |
+
<ParamName name={name} doc={doc} />
|
| 237 |
<textarea
|
| 238 |
className="textarea textarea-bordered w-full"
|
| 239 |
rows={6}
|
|
|
|
| 246 |
<NodeGroupParameter meta={meta} data={data} setParam={setParam} />
|
| 247 |
) : meta?.type?.enum ? (
|
| 248 |
<label className="param">
|
| 249 |
+
<ParamName name={name} doc={doc} />
|
| 250 |
<select
|
| 251 |
className="select select-bordered w-full"
|
| 252 |
value={value || meta.type.enum[0]}
|
|
|
|
| 273 |
</div>
|
| 274 |
) : meta?.type?.type === MODEL_TRAINING_INPUT_MAPPING ? (
|
| 275 |
<label className="param">
|
| 276 |
+
<ParamName name={name} doc={doc} />
|
| 277 |
<ModelMapping value={value} data={data} variant="training input" onChange={onChange} />
|
| 278 |
</label>
|
| 279 |
) : meta?.type?.type === MODEL_INFERENCE_INPUT_MAPPING ? (
|
| 280 |
<label className="param">
|
| 281 |
+
<ParamName name={name} doc={doc} />
|
| 282 |
<ModelMapping value={value} data={data} variant="inference input" onChange={onChange} />
|
| 283 |
</label>
|
| 284 |
) : meta?.type?.type === MODEL_OUTPUT_MAPPING ? (
|
| 285 |
<label className="param">
|
| 286 |
+
<ParamName name={name} doc={doc} />
|
| 287 |
<ModelMapping value={value} data={data} variant="output" onChange={onChange} />
|
| 288 |
</label>
|
| 289 |
) : (
|
| 290 |
<label className="param">
|
| 291 |
+
<ParamName name={name} doc={doc} />
|
| 292 |
<Input value={value} onChange={onChange} />
|
| 293 |
</label>
|
| 294 |
);
|
lynxkite-core/pyproject.toml
CHANGED
|
@@ -5,6 +5,7 @@ description = "A lightweight dependency for authoring LynxKite operations and ex
|
|
| 5 |
readme = "README.md"
|
| 6 |
requires-python = ">=3.11"
|
| 7 |
dependencies = [
|
|
|
|
| 8 |
]
|
| 9 |
|
| 10 |
[project.optional-dependencies]
|
|
|
|
| 5 |
readme = "README.md"
|
| 6 |
requires-python = ">=3.11"
|
| 7 |
dependencies = [
|
| 8 |
+
"griffe>=1.7.3",
|
| 9 |
]
|
| 10 |
|
| 11 |
[project.optional-dependencies]
|
lynxkite-core/src/lynxkite/core/ops.py
CHANGED
|
@@ -1,19 +1,22 @@
|
|
| 1 |
"""API for implementing LynxKite operations."""
|
| 2 |
|
| 3 |
from __future__ import annotations
|
|
|
|
| 4 |
import asyncio
|
| 5 |
import enum
|
| 6 |
import functools
|
|
|
|
| 7 |
import importlib
|
| 8 |
import inspect
|
| 9 |
import pathlib
|
| 10 |
import subprocess
|
| 11 |
import traceback
|
| 12 |
-
import joblib
|
| 13 |
import types
|
| 14 |
-
import pydantic
|
| 15 |
import typing
|
| 16 |
from dataclasses import dataclass
|
|
|
|
|
|
|
|
|
|
| 17 |
from typing_extensions import Annotated
|
| 18 |
|
| 19 |
if typing.TYPE_CHECKING:
|
|
@@ -180,6 +183,7 @@ class Op(BaseConfig):
|
|
| 180 |
# TODO: Make type an enum with the possible values.
|
| 181 |
type: str = "basic" # The UI to use for this operation.
|
| 182 |
color: str = "orange" # The color of the operation in the UI.
|
|
|
|
| 183 |
|
| 184 |
def __call__(self, *inputs, **params):
|
| 185 |
# Convert parameters.
|
|
@@ -236,6 +240,7 @@ def op(
|
|
| 236 |
"""Decorator for defining an operation."""
|
| 237 |
|
| 238 |
def decorator(func):
|
|
|
|
| 239 |
sig = inspect.signature(func)
|
| 240 |
_view = view
|
| 241 |
if view == "matplotlib":
|
|
@@ -262,6 +267,7 @@ def op(
|
|
| 262 |
_outputs = [Output(name="output", type=None)] if view == "basic" else []
|
| 263 |
op = Op(
|
| 264 |
func=func,
|
|
|
|
| 265 |
name=name,
|
| 266 |
params=_params,
|
| 267 |
inputs=inputs,
|
|
@@ -279,10 +285,11 @@ def op(
|
|
| 279 |
|
| 280 |
def matplotlib_to_image(func):
|
| 281 |
"""Decorator for converting a matplotlib figure to an image."""
|
| 282 |
-
import matplotlib.pyplot as plt
|
| 283 |
import base64
|
| 284 |
import io
|
| 285 |
|
|
|
|
|
|
|
| 286 |
@functools.wraps(func)
|
| 287 |
def wrapper(*args, **kwargs):
|
| 288 |
func(*args, **kwargs)
|
|
@@ -423,3 +430,15 @@ def run_user_script(script_path: pathlib.Path):
|
|
| 423 |
spec = importlib.util.spec_from_file_location(script_path.stem, str(script_path))
|
| 424 |
module = importlib.util.module_from_spec(spec)
|
| 425 |
spec.loader.exec_module(module)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
"""API for implementing LynxKite operations."""
|
| 2 |
|
| 3 |
from __future__ import annotations
|
| 4 |
+
|
| 5 |
import asyncio
|
| 6 |
import enum
|
| 7 |
import functools
|
| 8 |
+
import json
|
| 9 |
import importlib
|
| 10 |
import inspect
|
| 11 |
import pathlib
|
| 12 |
import subprocess
|
| 13 |
import traceback
|
|
|
|
| 14 |
import types
|
|
|
|
| 15 |
import typing
|
| 16 |
from dataclasses import dataclass
|
| 17 |
+
|
| 18 |
+
import joblib
|
| 19 |
+
import pydantic
|
| 20 |
from typing_extensions import Annotated
|
| 21 |
|
| 22 |
if typing.TYPE_CHECKING:
|
|
|
|
| 183 |
# TODO: Make type an enum with the possible values.
|
| 184 |
type: str = "basic" # The UI to use for this operation.
|
| 185 |
color: str = "orange" # The color of the operation in the UI.
|
| 186 |
+
doc: object = None
|
| 187 |
|
| 188 |
def __call__(self, *inputs, **params):
|
| 189 |
# Convert parameters.
|
|
|
|
| 240 |
"""Decorator for defining an operation."""
|
| 241 |
|
| 242 |
def decorator(func):
|
| 243 |
+
doc = get_doc(func)
|
| 244 |
sig = inspect.signature(func)
|
| 245 |
_view = view
|
| 246 |
if view == "matplotlib":
|
|
|
|
| 267 |
_outputs = [Output(name="output", type=None)] if view == "basic" else []
|
| 268 |
op = Op(
|
| 269 |
func=func,
|
| 270 |
+
doc=doc,
|
| 271 |
name=name,
|
| 272 |
params=_params,
|
| 273 |
inputs=inputs,
|
|
|
|
| 285 |
|
| 286 |
def matplotlib_to_image(func):
|
| 287 |
"""Decorator for converting a matplotlib figure to an image."""
|
|
|
|
| 288 |
import base64
|
| 289 |
import io
|
| 290 |
|
| 291 |
+
import matplotlib.pyplot as plt
|
| 292 |
+
|
| 293 |
@functools.wraps(func)
|
| 294 |
def wrapper(*args, **kwargs):
|
| 295 |
func(*args, **kwargs)
|
|
|
|
| 430 |
spec = importlib.util.spec_from_file_location(script_path.stem, str(script_path))
|
| 431 |
module = importlib.util.module_from_spec(spec)
|
| 432 |
spec.loader.exec_module(module)
|
| 433 |
+
|
| 434 |
+
|
| 435 |
+
def get_doc(func):
|
| 436 |
+
"""Griffe is an optional dependency. When available, we returned the parsed docstring."""
|
| 437 |
+
try:
|
| 438 |
+
import griffe
|
| 439 |
+
except ImportError:
|
| 440 |
+
return func.__doc__
|
| 441 |
+
if func.__doc__ is None:
|
| 442 |
+
return None
|
| 443 |
+
doc = griffe.Docstring(func.__doc__).parse("google")
|
| 444 |
+
return json.loads(json.dumps(doc, cls=griffe.JSONEncoder))
|
lynxkite-graph-analytics/src/lynxkite_graph_analytics/lynxkite_ops.py
CHANGED
|
@@ -8,7 +8,6 @@ from collections import deque
|
|
| 8 |
|
| 9 |
from . import core
|
| 10 |
import grandcypher
|
| 11 |
-
import joblib
|
| 12 |
import matplotlib
|
| 13 |
import networkx as nx
|
| 14 |
import pandas as pd
|
|
@@ -16,7 +15,6 @@ import polars as pl
|
|
| 16 |
import json
|
| 17 |
|
| 18 |
|
| 19 |
-
mem = joblib.Memory(".joblib-cache")
|
| 20 |
op = ops.op_registration(core.ENV)
|
| 21 |
|
| 22 |
|
|
@@ -82,8 +80,7 @@ def import_parquet(*, filename: str):
|
|
| 82 |
return pd.read_parquet(filename)
|
| 83 |
|
| 84 |
|
| 85 |
-
@op("Import CSV")
|
| 86 |
-
@mem.cache
|
| 87 |
def import_csv(*, filename: str, columns: str = "<from file>", separator: str = "<auto>"):
|
| 88 |
"""Imports a CSV file."""
|
| 89 |
return pd.read_csv(
|
|
@@ -93,8 +90,7 @@ def import_csv(*, filename: str, columns: str = "<from file>", separator: str =
|
|
| 93 |
)
|
| 94 |
|
| 95 |
|
| 96 |
-
@op("Import GraphML")
|
| 97 |
-
@mem.cache
|
| 98 |
def import_graphml(*, filename: str):
|
| 99 |
"""Imports a GraphML file."""
|
| 100 |
files = fsspec.open_files(filename, compression="infer")
|
|
@@ -105,8 +101,7 @@ def import_graphml(*, filename: str):
|
|
| 105 |
raise ValueError(f"No .graphml file found at {filename}")
|
| 106 |
|
| 107 |
|
| 108 |
-
@op("Graph from OSM")
|
| 109 |
-
@mem.cache
|
| 110 |
def import_osm(*, location: str):
|
| 111 |
import osmnx as ox
|
| 112 |
|
|
|
|
| 8 |
|
| 9 |
from . import core
|
| 10 |
import grandcypher
|
|
|
|
| 11 |
import matplotlib
|
| 12 |
import networkx as nx
|
| 13 |
import pandas as pd
|
|
|
|
| 15 |
import json
|
| 16 |
|
| 17 |
|
|
|
|
| 18 |
op = ops.op_registration(core.ENV)
|
| 19 |
|
| 20 |
|
|
|
|
| 80 |
return pd.read_parquet(filename)
|
| 81 |
|
| 82 |
|
| 83 |
+
@op("Import CSV", slow=True)
|
|
|
|
| 84 |
def import_csv(*, filename: str, columns: str = "<from file>", separator: str = "<auto>"):
|
| 85 |
"""Imports a CSV file."""
|
| 86 |
return pd.read_csv(
|
|
|
|
| 90 |
)
|
| 91 |
|
| 92 |
|
| 93 |
+
@op("Import GraphML", slow=True)
|
|
|
|
| 94 |
def import_graphml(*, filename: str):
|
| 95 |
"""Imports a GraphML file."""
|
| 96 |
files = fsspec.open_files(filename, compression="infer")
|
|
|
|
| 101 |
raise ValueError(f"No .graphml file found at {filename}")
|
| 102 |
|
| 103 |
|
| 104 |
+
@op("Graph from OSM", slow=True)
|
|
|
|
| 105 |
def import_osm(*, location: str):
|
| 106 |
import osmnx as ox
|
| 107 |
|
lynxkite-graph-analytics/src/lynxkite_graph_analytics/pytorch/pytorch_ops.py
CHANGED
|
@@ -8,7 +8,7 @@ from .pytorch_core import op, reg, ENV
|
|
| 8 |
|
| 9 |
reg("Input: tensor", outputs=["output"], params=[P.basic("name")])
|
| 10 |
reg("Input: graph edges", outputs=["edges"])
|
| 11 |
-
reg("Input: sequential", outputs=["y"])
|
| 12 |
reg("Output", inputs=["x"], outputs=["x"], params=[P.basic("name")])
|
| 13 |
|
| 14 |
|
|
@@ -19,6 +19,7 @@ def lstm(x, *, input_size=1024, hidden_size=1024, dropout=0.0):
|
|
| 19 |
|
| 20 |
reg(
|
| 21 |
"Neural ODE",
|
|
|
|
| 22 |
inputs=["x"],
|
| 23 |
params=[
|
| 24 |
P.basic("relative_tolerance"),
|
|
@@ -99,6 +100,7 @@ def concatenate(a, b):
|
|
| 99 |
|
| 100 |
reg(
|
| 101 |
"Graph conv",
|
|
|
|
| 102 |
inputs=["x", "edges"],
|
| 103 |
outputs=["x"],
|
| 104 |
params=[P.options("type", ["GCNConv", "GATConv", "GATv2Conv", "SAGEConv"])],
|
|
|
|
| 8 |
|
| 9 |
reg("Input: tensor", outputs=["output"], params=[P.basic("name")])
|
| 10 |
reg("Input: graph edges", outputs=["edges"])
|
| 11 |
+
reg("Input: sequential", outputs=["y"], params=[P.basic("name")])
|
| 12 |
reg("Output", inputs=["x"], outputs=["x"], params=[P.basic("name")])
|
| 13 |
|
| 14 |
|
|
|
|
| 19 |
|
| 20 |
reg(
|
| 21 |
"Neural ODE",
|
| 22 |
+
color="blue",
|
| 23 |
inputs=["x"],
|
| 24 |
params=[
|
| 25 |
P.basic("relative_tolerance"),
|
|
|
|
| 100 |
|
| 101 |
reg(
|
| 102 |
"Graph conv",
|
| 103 |
+
color="blue",
|
| 104 |
inputs=["x", "edges"],
|
| 105 |
outputs=["x"],
|
| 106 |
params=[P.options("type", ["GCNConv", "GATConv", "GATv2Conv", "SAGEConv"])],
|