darabos commited on
Commit
027b639
·
unverified ·
2 Parent(s): 01d25c5 11a84ac

Merge pull request #168 from biggraph/darabos-ws-docs

Browse files
examples/ODE-GNN.lynxkite.json CHANGED
@@ -1,12 +1,5 @@
1
  {
2
  "edges": [
3
- {
4
- "id": "Input: embedding 1 Graph conv 1",
5
- "source": "Input: embedding 1",
6
- "sourceHandle": "x",
7
- "target": "Graph conv 1",
8
- "targetHandle": "x"
9
- },
10
  {
11
  "id": "Input: graph edges 1 Graph conv 1",
12
  "source": "Input: graph edges 1",
@@ -21,13 +14,6 @@
21
  "target": "Activation 1",
22
  "targetHandle": "x"
23
  },
24
- {
25
- "id": "Activation 1 Repeat 1",
26
- "source": "Activation 1",
27
- "sourceHandle": "x",
28
- "target": "Repeat 1",
29
- "targetHandle": "input"
30
- },
31
  {
32
  "id": "Repeat 1 Graph conv 1",
33
  "source": "Repeat 1",
@@ -43,86 +29,84 @@
43
  "targetHandle": "x"
44
  },
45
  {
46
- "id": "Input: zeros 1 LSTM 1",
47
- "source": "Input: zeros 1",
48
  "sourceHandle": "x",
49
- "target": "LSTM 1",
50
- "targetHandle": "h"
51
  },
52
  {
53
- "id": "Recurrent chain 1 LSTM 1",
54
- "source": "Recurrent chain 1",
55
  "sourceHandle": "output",
56
- "target": "LSTM 1",
57
- "targetHandle": "h"
58
  },
59
  {
60
- "id": "LSTM 1 Recurrent chain 1",
61
- "source": "LSTM 1",
62
- "sourceHandle": "h",
63
- "target": "Recurrent chain 1",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
64
  "targetHandle": "input"
65
  },
66
  {
67
- "id": "Activation 1 Concatenate 1",
68
  "source": "Activation 1",
69
- "sourceHandle": "x",
70
  "target": "Concatenate 1",
71
  "targetHandle": "a"
72
  },
73
  {
74
- "id": "LSTM 1 Concatenate 1",
75
  "source": "LSTM 1",
76
- "sourceHandle": "x",
77
  "target": "Concatenate 1",
78
  "targetHandle": "b"
79
  },
80
  {
81
- "id": "Input: label 1 MSE loss 1",
82
- "source": "Input: label 1",
83
- "sourceHandle": "y",
84
- "target": "MSE loss 1",
85
- "targetHandle": "y"
86
- },
87
- {
88
- "id": "MSE loss 1 Optimizer 1",
89
- "source": "MSE loss 1",
90
- "sourceHandle": "loss",
91
- "target": "Optimizer 1",
92
- "targetHandle": "loss"
93
- },
94
- {
95
- "id": "Concatenate 1 Neural ODE 2",
96
  "source": "Concatenate 1",
97
- "sourceHandle": "x",
98
  "target": "Neural ODE 2",
99
  "targetHandle": "x"
100
- },
101
- {
102
- "id": "Neural ODE 2 MSE loss 1",
103
- "source": "Neural ODE 2",
104
- "sourceHandle": "x",
105
- "target": "MSE loss 1",
106
- "targetHandle": "x"
107
  }
108
  ],
109
  "env": "PyTorch model",
110
  "nodes": [
111
  {
112
  "data": {
 
 
113
  "display": null,
114
  "error": null,
 
115
  "meta": {
 
 
116
  "inputs": [
117
  {
118
- "name": "edges",
119
  "position": "bottom",
120
  "type": {
121
  "type": "tensor"
122
  }
123
  },
124
  {
125
- "name": "x",
126
  "position": "bottom",
127
  "type": {
128
  "type": "tensor"
@@ -141,7 +125,7 @@
141
  ],
142
  "params": [
143
  {
144
- "default": "1",
145
  "name": "type",
146
  "type": {
147
  "enum": [
@@ -156,17 +140,17 @@
156
  "type": "basic"
157
  },
158
  "params": {
159
- "type": 1.0
160
  },
161
- "status": "planned",
162
  "title": "Graph conv"
163
  },
164
  "dragHandle": ".bg-primary",
165
  "height": 200.0,
166
  "id": "Graph conv 1",
167
  "position": {
168
- "x": 350.98078368755864,
169
- "y": 195.0
170
  },
171
  "type": "basic",
172
  "width": 200.0
@@ -177,7 +161,10 @@
177
  "collapsed": null,
178
  "display": null,
179
  "error": null,
 
180
  "meta": {
 
 
181
  "inputs": [
182
  {
183
  "name": "input",
@@ -204,6 +191,13 @@
204
  "type": {
205
  "type": "<class 'int'>"
206
  }
 
 
 
 
 
 
 
207
  }
208
  ],
209
  "type": "basic"
@@ -211,15 +205,15 @@
211
  "params": {
212
  "times": "5"
213
  },
214
- "status": "planned",
215
  "title": "Repeat"
216
  },
217
  "dragHandle": ".bg-primary",
218
  "height": 200.0,
219
  "id": "Repeat 1",
220
  "position": {
221
- "x": -94.15168677219138,
222
- "y": 14.525356969883305
223
  },
224
  "type": "basic",
225
  "width": 200.0
@@ -230,30 +224,33 @@
230
  "collapsed": true,
231
  "display": null,
232
  "error": null,
 
233
  "meta": {
 
 
234
  "inputs": [
235
  {
236
  "name": "a",
237
  "position": "bottom",
238
  "type": {
239
- "type": "tensor"
240
  }
241
  },
242
  {
243
  "name": "b",
244
  "position": "bottom",
245
  "type": {
246
- "type": "tensor"
247
  }
248
  }
249
  ],
250
  "name": "Concatenate",
251
  "outputs": [
252
  {
253
- "name": "x",
254
  "position": "top",
255
  "type": {
256
- "type": "tensor"
257
  }
258
  }
259
  ],
@@ -261,7 +258,7 @@
261
  "type": "basic"
262
  },
263
  "params": {},
264
- "status": "planned",
265
  "title": "Concatenate"
266
  },
267
  "dragHandle": ".bg-primary",
@@ -280,7 +277,10 @@
280
  "collapsed": true,
281
  "display": null,
282
  "error": null,
 
283
  "meta": {
 
 
284
  "inputs": [],
285
  "name": "Input: graph edges",
286
  "outputs": [
@@ -296,50 +296,15 @@
296
  "type": "basic"
297
  },
298
  "params": {},
299
- "status": "planned",
300
  "title": "Input: graph edges"
301
  },
302
  "dragHandle": ".bg-primary",
303
  "height": 200.0,
304
  "id": "Input: graph edges 1",
305
  "position": {
306
- "x": 515.6535517374441,
307
- "y": 545.4709559884296
308
- },
309
- "type": "basic",
310
- "width": 200.0
311
- },
312
- {
313
- "data": {
314
- "__execution_delay": null,
315
- "collapsed": true,
316
- "display": null,
317
- "error": null,
318
- "meta": {
319
- "inputs": [],
320
- "name": "Input: embedding",
321
- "outputs": [
322
- {
323
- "name": "x",
324
- "position": "top",
325
- "type": {
326
- "type": "tensor"
327
- }
328
- }
329
- ],
330
- "params": [],
331
- "type": "basic"
332
- },
333
- "params": {},
334
- "status": "planned",
335
- "title": "Input: embedding"
336
- },
337
- "dragHandle": ".bg-primary",
338
- "height": 200.0,
339
- "id": "Input: embedding 1",
340
- "position": {
341
- "x": 246.6527948448857,
342
- "y": 551.6313504198322
343
  },
344
  "type": "basic",
345
  "width": 200.0
@@ -348,34 +313,37 @@
348
  "data": {
349
  "display": null,
350
  "error": null,
 
351
  "meta": {
 
 
352
  "inputs": [
353
  {
354
  "name": "x",
355
  "position": "bottom",
356
  "type": {
357
- "type": "tensor"
358
  }
359
  }
360
  ],
361
  "name": "Activation",
362
  "outputs": [
363
  {
364
- "name": "x",
365
  "position": "top",
366
  "type": {
367
- "type": "tensor"
368
  }
369
  }
370
  ],
371
  "params": [
372
  {
373
- "default": "1",
374
  "name": "type",
375
  "type": {
376
  "enum": [
377
  "ReLU",
378
- "LeakyReLU",
379
  "Tanh",
380
  "Mish"
381
  ]
@@ -387,7 +355,7 @@
387
  "params": {
388
  "type": 1.0
389
  },
390
- "status": "planned",
391
  "title": "Activation"
392
  },
393
  "dragHandle": ".bg-primary",
@@ -402,68 +370,86 @@
402
  },
403
  {
404
  "data": {
405
- "__execution_delay": null,
406
- "collapsed": true,
407
  "display": null,
408
  "error": null,
 
409
  "meta": {
 
 
410
  "inputs": [
411
- {
412
- "name": "h",
413
- "position": "bottom",
414
- "type": {
415
- "type": "tensor"
416
- }
417
- },
418
  {
419
  "name": "x",
420
  "position": "bottom",
421
  "type": {
422
- "type": "tensor"
423
  }
424
  }
425
  ],
426
  "name": "LSTM",
427
  "outputs": [
428
  {
429
- "name": "h",
430
  "position": "top",
431
  "type": {
432
- "type": "tensor"
 
 
 
 
 
 
 
 
 
433
  }
434
  },
435
  {
436
- "name": "x",
437
- "position": "top",
438
  "type": {
439
- "type": "tensor"
 
 
 
 
 
 
 
440
  }
441
  }
442
  ],
443
- "params": [],
444
  "type": "basic"
445
  },
446
- "params": {},
447
- "status": "planned",
 
 
 
 
448
  "title": "LSTM"
449
  },
450
  "dragHandle": ".bg-primary",
451
- "height": 200.0,
452
  "id": "LSTM 1",
453
  "position": {
454
  "x": 960.0,
455
  "y": 135.0
456
  },
457
  "type": "basic",
458
- "width": 200.0
459
  },
460
  {
461
  "data": {
462
- "__execution_delay": null,
463
- "collapsed": true,
464
  "display": null,
465
  "error": null,
 
466
  "meta": {
 
 
467
  "inputs": [],
468
  "name": "Input: sequential",
469
  "outputs": [
@@ -475,97 +461,29 @@
475
  }
476
  }
477
  ],
478
- "params": [],
479
- "type": "basic"
480
- },
481
- "params": {},
482
- "status": "planned",
483
- "title": "Input: sequential"
484
- },
485
- "dragHandle": ".bg-primary",
486
- "height": 200.0,
487
- "id": "Input: sequential 1",
488
- "position": {
489
- "x": 1005.0,
490
- "y": 510.0
491
- },
492
- "type": "basic",
493
- "width": 200.0
494
- },
495
- {
496
- "data": {
497
- "__execution_delay": null,
498
- "collapsed": true,
499
- "display": null,
500
- "error": null,
501
- "meta": {
502
- "inputs": [],
503
- "name": "Input: zeros",
504
- "outputs": [
505
  {
506
- "name": "x",
507
- "position": "top",
508
  "type": {
509
- "type": "tensor"
510
  }
511
  }
512
  ],
513
- "params": [],
514
  "type": "basic"
515
  },
516
- "params": {},
517
- "status": "planned",
518
- "title": "Input: zeros"
519
- },
520
- "dragHandle": ".bg-primary",
521
- "height": 200.0,
522
- "id": "Input: zeros 1",
523
- "position": {
524
- "x": 1290.0,
525
- "y": 405.0
526
- },
527
- "type": "basic",
528
- "width": 200.0
529
- },
530
- {
531
- "data": {
532
- "__execution_delay": null,
533
- "collapsed": true,
534
- "display": null,
535
- "error": null,
536
- "meta": {
537
- "inputs": [
538
- {
539
- "name": "input",
540
- "position": "top",
541
- "type": {
542
- "type": "tensor"
543
- }
544
- }
545
- ],
546
- "name": "Recurrent chain",
547
- "outputs": [
548
- {
549
- "name": "output",
550
- "position": "bottom",
551
- "type": {
552
- "type": "tensor"
553
- }
554
- }
555
- ],
556
- "params": [],
557
- "type": "basic"
558
  },
559
- "params": {},
560
- "status": "planned",
561
- "title": "Recurrent chain"
562
  },
563
  "dragHandle": ".bg-primary",
564
  "height": 200.0,
565
- "id": "Recurrent chain 1",
566
  "position": {
567
- "x": 1224.6603040746108,
568
- "y": 135.44839862151363
569
  },
570
  "type": "basic",
571
  "width": 200.0
@@ -576,30 +494,33 @@
576
  "collapsed": true,
577
  "display": null,
578
  "error": null,
 
579
  "meta": {
 
 
580
  "inputs": [
581
  {
582
  "name": "x",
583
  "position": "bottom",
584
  "type": {
585
- "type": "tensor"
586
  }
587
  },
588
  {
589
  "name": "y",
590
  "position": "bottom",
591
  "type": {
592
- "type": "tensor"
593
  }
594
  }
595
  ],
596
  "name": "MSE loss",
597
  "outputs": [
598
  {
599
- "name": "loss",
600
  "position": "top",
601
  "type": {
602
- "type": "tensor"
603
  }
604
  }
605
  ],
@@ -607,50 +528,15 @@
607
  "type": "basic"
608
  },
609
  "params": {},
610
- "status": "planned",
611
  "title": "MSE loss"
612
  },
613
  "dragHandle": ".bg-primary",
614
  "height": 200.0,
615
  "id": "MSE loss 1",
616
  "position": {
617
- "x": 915.0,
618
- "y": -900.0
619
- },
620
- "type": "basic",
621
- "width": 200.0
622
- },
623
- {
624
- "data": {
625
- "__execution_delay": null,
626
- "collapsed": true,
627
- "display": null,
628
- "error": null,
629
- "meta": {
630
- "inputs": [],
631
- "name": "Input: label",
632
- "outputs": [
633
- {
634
- "name": "y",
635
- "position": "top",
636
- "type": {
637
- "type": "tensor"
638
- }
639
- }
640
- ],
641
- "params": [],
642
- "type": "basic"
643
- },
644
- "params": {},
645
- "status": "planned",
646
- "title": "Input: label"
647
- },
648
- "dragHandle": ".bg-primary",
649
- "height": 200.0,
650
- "id": "Input: label 1",
651
- "position": {
652
- "x": 1095.0,
653
- "y": -450.0
654
  },
655
  "type": "basic",
656
  "width": 200.0
@@ -659,7 +545,10 @@
659
  "data": {
660
  "display": null,
661
  "error": null,
 
662
  "meta": {
 
 
663
  "inputs": [
664
  {
665
  "name": "loss",
@@ -673,14 +562,7 @@
673
  "outputs": [],
674
  "params": [
675
  {
676
- "default": 0.001,
677
- "name": "lr",
678
- "type": {
679
- "type": "<class 'float'>"
680
- }
681
- },
682
- {
683
- "default": "1",
684
  "name": "type",
685
  "type": {
686
  "enum": [
@@ -693,6 +575,13 @@
693
  "Galore AdamW"
694
  ]
695
  }
 
 
 
 
 
 
 
696
  }
697
  ],
698
  "type": "basic"
@@ -701,24 +590,29 @@
701
  "lr": 0.001,
702
  "type": 1.0
703
  },
704
- "status": "planned",
705
  "title": "Optimizer"
706
  },
707
  "dragHandle": ".bg-primary",
708
  "height": 247.0,
709
  "id": "Optimizer 1",
710
  "position": {
711
- "x": 915.3430278730226,
712
- "y": -1268.0577550022126
713
  },
714
  "type": "basic",
715
  "width": 190.0
716
  },
717
  {
718
  "data": {
 
 
719
  "display": null,
720
  "error": null,
 
721
  "meta": {
 
 
722
  "inputs": [
723
  {
724
  "name": "x",
@@ -739,6 +633,13 @@
739
  }
740
  ],
741
  "params": [
 
 
 
 
 
 
 
742
  {
743
  "default": null,
744
  "name": "absolute_tolerance",
@@ -747,7 +648,7 @@
747
  }
748
  },
749
  {
750
- "default": "1",
751
  "name": "method",
752
  "type": {
753
  "enum": [
@@ -763,10 +664,53 @@
763
  "implicit_adams"
764
  ]
765
  }
766
- },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
767
  {
768
  "default": null,
769
- "name": "relative_tolerance",
770
  "type": {
771
  "type": "None"
772
  }
@@ -775,19 +719,65 @@
775
  "type": "basic"
776
  },
777
  "params": {
778
- "absolute_tolerance": null,
779
- "method": 1.0,
780
- "relative_tolerance": null
781
  },
782
- "status": "planned",
783
- "title": "Neural ODE"
784
  },
785
  "dragHandle": ".bg-primary",
786
  "height": 200.0,
787
- "id": "Neural ODE 2",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
788
  "position": {
789
- "x": 342.3226409443945,
790
- "y": -687.1882072175634
791
  },
792
  "type": "basic",
793
  "width": 200.0
 
1
  {
2
  "edges": [
 
 
 
 
 
 
 
3
  {
4
  "id": "Input: graph edges 1 Graph conv 1",
5
  "source": "Input: graph edges 1",
 
14
  "target": "Activation 1",
15
  "targetHandle": "x"
16
  },
 
 
 
 
 
 
 
17
  {
18
  "id": "Repeat 1 Graph conv 1",
19
  "source": "Repeat 1",
 
29
  "targetHandle": "x"
30
  },
31
  {
32
+ "id": "Neural ODE 2 MSE loss 1",
33
+ "source": "Neural ODE 2",
34
  "sourceHandle": "x",
35
+ "target": "MSE loss 1",
36
+ "targetHandle": "x"
37
  },
38
  {
39
+ "id": "Input: tensor 1 Graph conv 1",
40
+ "source": "Input: tensor 1",
41
  "sourceHandle": "output",
42
+ "target": "Graph conv 1",
43
+ "targetHandle": "x"
44
  },
45
  {
46
+ "id": "Input: tensor 2 MSE loss 1",
47
+ "source": "Input: tensor 2",
48
+ "sourceHandle": "output",
49
+ "target": "MSE loss 1",
50
+ "targetHandle": "y"
51
+ },
52
+ {
53
+ "id": "MSE loss 1 Optimizer 1",
54
+ "source": "MSE loss 1",
55
+ "sourceHandle": "output",
56
+ "target": "Optimizer 1",
57
+ "targetHandle": "loss"
58
+ },
59
+ {
60
+ "id": "Activation 1 output Repeat 1 input",
61
+ "source": "Activation 1",
62
+ "sourceHandle": "output",
63
+ "target": "Repeat 1",
64
  "targetHandle": "input"
65
  },
66
  {
67
+ "id": "Activation 1 output Concatenate 1 a",
68
  "source": "Activation 1",
69
+ "sourceHandle": "output",
70
  "target": "Concatenate 1",
71
  "targetHandle": "a"
72
  },
73
  {
74
+ "id": "LSTM 1 output Concatenate 1 b",
75
  "source": "LSTM 1",
76
+ "sourceHandle": "output",
77
  "target": "Concatenate 1",
78
  "targetHandle": "b"
79
  },
80
  {
81
+ "id": "Concatenate 1 output Neural ODE 2 x",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
82
  "source": "Concatenate 1",
83
+ "sourceHandle": "output",
84
  "target": "Neural ODE 2",
85
  "targetHandle": "x"
 
 
 
 
 
 
 
86
  }
87
  ],
88
  "env": "PyTorch model",
89
  "nodes": [
90
  {
91
  "data": {
92
+ "__execution_delay": 0.0,
93
+ "collapsed": null,
94
  "display": null,
95
  "error": null,
96
+ "input_metadata": null,
97
  "meta": {
98
+ "color": "blue",
99
+ "doc": null,
100
  "inputs": [
101
  {
102
+ "name": "x",
103
  "position": "bottom",
104
  "type": {
105
  "type": "tensor"
106
  }
107
  },
108
  {
109
+ "name": "edges",
110
  "position": "bottom",
111
  "type": {
112
  "type": "tensor"
 
125
  ],
126
  "params": [
127
  {
128
+ "default": "GCNConv",
129
  "name": "type",
130
  "type": {
131
  "enum": [
 
140
  "type": "basic"
141
  },
142
  "params": {
143
+ "type": "SAGEConv"
144
  },
145
+ "status": "done",
146
  "title": "Graph conv"
147
  },
148
  "dragHandle": ".bg-primary",
149
  "height": 200.0,
150
  "id": "Graph conv 1",
151
  "position": {
152
+ "x": 353.5291731060978,
153
+ "y": 191.17741587219126
154
  },
155
  "type": "basic",
156
  "width": 200.0
 
161
  "collapsed": null,
162
  "display": null,
163
  "error": null,
164
+ "input_metadata": null,
165
  "meta": {
166
+ "color": "orange",
167
+ "doc": null,
168
  "inputs": [
169
  {
170
  "name": "input",
 
191
  "type": {
192
  "type": "<class 'int'>"
193
  }
194
+ },
195
+ {
196
+ "default": false,
197
+ "name": "same_weights",
198
+ "type": {
199
+ "type": "<class 'bool'>"
200
+ }
201
  }
202
  ],
203
  "type": "basic"
 
205
  "params": {
206
  "times": "5"
207
  },
208
+ "status": "done",
209
  "title": "Repeat"
210
  },
211
  "dragHandle": ".bg-primary",
212
  "height": 200.0,
213
  "id": "Repeat 1",
214
  "position": {
215
+ "x": 55.8483132278086,
216
+ "y": 54.525356969883305
217
  },
218
  "type": "basic",
219
  "width": 200.0
 
224
  "collapsed": true,
225
  "display": null,
226
  "error": null,
227
+ "input_metadata": null,
228
  "meta": {
229
+ "color": "orange",
230
+ "doc": null,
231
  "inputs": [
232
  {
233
  "name": "a",
234
  "position": "bottom",
235
  "type": {
236
+ "type": "<class 'inspect._empty'>"
237
  }
238
  },
239
  {
240
  "name": "b",
241
  "position": "bottom",
242
  "type": {
243
+ "type": "<class 'inspect._empty'>"
244
  }
245
  }
246
  ],
247
  "name": "Concatenate",
248
  "outputs": [
249
  {
250
+ "name": "output",
251
  "position": "top",
252
  "type": {
253
+ "type": "None"
254
  }
255
  }
256
  ],
 
258
  "type": "basic"
259
  },
260
  "params": {},
261
+ "status": "done",
262
  "title": "Concatenate"
263
  },
264
  "dragHandle": ".bg-primary",
 
277
  "collapsed": true,
278
  "display": null,
279
  "error": null,
280
+ "input_metadata": null,
281
  "meta": {
282
+ "color": "orange",
283
+ "doc": null,
284
  "inputs": [],
285
  "name": "Input: graph edges",
286
  "outputs": [
 
296
  "type": "basic"
297
  },
298
  "params": {},
299
+ "status": "done",
300
  "title": "Input: graph edges"
301
  },
302
  "dragHandle": ".bg-primary",
303
  "height": 200.0,
304
  "id": "Input: graph edges 1",
305
  "position": {
306
+ "x": 457.3761886867096,
307
+ "y": 530.2733532051268
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
308
  },
309
  "type": "basic",
310
  "width": 200.0
 
313
  "data": {
314
  "display": null,
315
  "error": null,
316
+ "input_metadata": null,
317
  "meta": {
318
+ "color": "orange",
319
+ "doc": null,
320
  "inputs": [
321
  {
322
  "name": "x",
323
  "position": "bottom",
324
  "type": {
325
+ "type": "<class 'inspect._empty'>"
326
  }
327
  }
328
  ],
329
  "name": "Activation",
330
  "outputs": [
331
  {
332
+ "name": "output",
333
  "position": "top",
334
  "type": {
335
+ "type": "None"
336
  }
337
  }
338
  ],
339
  "params": [
340
  {
341
+ "default": "ReLU",
342
  "name": "type",
343
  "type": {
344
  "enum": [
345
  "ReLU",
346
+ "Leaky_ReLU",
347
  "Tanh",
348
  "Mish"
349
  ]
 
355
  "params": {
356
  "type": 1.0
357
  },
358
+ "status": "done",
359
  "title": "Activation"
360
  },
361
  "dragHandle": ".bg-primary",
 
370
  },
371
  {
372
  "data": {
373
+ "__execution_delay": 0.0,
374
+ "collapsed": false,
375
  "display": null,
376
  "error": null,
377
+ "input_metadata": null,
378
  "meta": {
379
+ "color": "blue",
380
+ "doc": null,
381
  "inputs": [
 
 
 
 
 
 
 
382
  {
383
  "name": "x",
384
  "position": "bottom",
385
  "type": {
386
+ "type": "<class 'inspect._empty'>"
387
  }
388
  }
389
  ],
390
  "name": "LSTM",
391
  "outputs": [
392
  {
393
+ "name": "output",
394
  "position": "top",
395
  "type": {
396
+ "type": "None"
397
+ }
398
+ }
399
+ ],
400
+ "params": [
401
+ {
402
+ "default": 1024.0,
403
+ "name": "input_size",
404
+ "type": {
405
+ "type": "<class 'int'>"
406
  }
407
  },
408
  {
409
+ "default": 1024.0,
410
+ "name": "hidden_size",
411
  "type": {
412
+ "type": "<class 'int'>"
413
+ }
414
+ },
415
+ {
416
+ "default": 0.0,
417
+ "name": "dropout",
418
+ "type": {
419
+ "type": "<class 'float'>"
420
  }
421
  }
422
  ],
 
423
  "type": "basic"
424
  },
425
+ "params": {
426
+ "dropout": "0.2",
427
+ "hidden_size": "128",
428
+ "input_size": "128"
429
+ },
430
+ "status": "done",
431
  "title": "LSTM"
432
  },
433
  "dragHandle": ".bg-primary",
434
+ "height": 312.0,
435
  "id": "LSTM 1",
436
  "position": {
437
  "x": 960.0,
438
  "y": 135.0
439
  },
440
  "type": "basic",
441
+ "width": 204.0
442
  },
443
  {
444
  "data": {
445
+ "__execution_delay": 0.0,
446
+ "collapsed": false,
447
  "display": null,
448
  "error": null,
449
+ "input_metadata": null,
450
  "meta": {
451
+ "color": "orange",
452
+ "doc": null,
453
  "inputs": [],
454
  "name": "Input: sequential",
455
  "outputs": [
 
461
  }
462
  }
463
  ],
464
+ "params": [
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
465
  {
466
+ "default": null,
467
+ "name": "name",
468
  "type": {
469
+ "type": "None"
470
  }
471
  }
472
  ],
 
473
  "type": "basic"
474
  },
475
+ "params": {
476
+ "name": "time series"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
477
  },
478
+ "status": "done",
479
+ "title": "Input: sequential"
 
480
  },
481
  "dragHandle": ".bg-primary",
482
  "height": 200.0,
483
+ "id": "Input: sequential 1",
484
  "position": {
485
+ "x": 961.5476403063019,
486
+ "y": 511.8105149872374
487
  },
488
  "type": "basic",
489
  "width": 200.0
 
494
  "collapsed": true,
495
  "display": null,
496
  "error": null,
497
+ "input_metadata": null,
498
  "meta": {
499
+ "color": "orange",
500
+ "doc": null,
501
  "inputs": [
502
  {
503
  "name": "x",
504
  "position": "bottom",
505
  "type": {
506
+ "type": "<class 'inspect._empty'>"
507
  }
508
  },
509
  {
510
  "name": "y",
511
  "position": "bottom",
512
  "type": {
513
+ "type": "<class 'inspect._empty'>"
514
  }
515
  }
516
  ],
517
  "name": "MSE loss",
518
  "outputs": [
519
  {
520
+ "name": "output",
521
  "position": "top",
522
  "type": {
523
+ "type": "None"
524
  }
525
  }
526
  ],
 
528
  "type": "basic"
529
  },
530
  "params": {},
531
+ "status": "done",
532
  "title": "MSE loss"
533
  },
534
  "dragHandle": ".bg-primary",
535
  "height": 200.0,
536
  "id": "MSE loss 1",
537
  "position": {
538
+ "x": 627.1281170292506,
539
+ "y": -957.9364795915974
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
540
  },
541
  "type": "basic",
542
  "width": 200.0
 
545
  "data": {
546
  "display": null,
547
  "error": null,
548
+ "input_metadata": null,
549
  "meta": {
550
+ "color": "green",
551
+ "doc": null,
552
  "inputs": [
553
  {
554
  "name": "loss",
 
562
  "outputs": [],
563
  "params": [
564
  {
565
+ "default": "AdamW",
 
 
 
 
 
 
 
566
  "name": "type",
567
  "type": {
568
  "enum": [
 
575
  "Galore AdamW"
576
  ]
577
  }
578
+ },
579
+ {
580
+ "default": 0.001,
581
+ "name": "lr",
582
+ "type": {
583
+ "type": "<class 'float'>"
584
+ }
585
  }
586
  ],
587
  "type": "basic"
 
590
  "lr": 0.001,
591
  "type": 1.0
592
  },
593
+ "status": "done",
594
  "title": "Optimizer"
595
  },
596
  "dragHandle": ".bg-primary",
597
  "height": 247.0,
598
  "id": "Optimizer 1",
599
  "position": {
600
+ "x": 632.9026898639854,
601
+ "y": -1321.9205758725257
602
  },
603
  "type": "basic",
604
  "width": 190.0
605
  },
606
  {
607
  "data": {
608
+ "__execution_delay": 0.0,
609
+ "collapsed": null,
610
  "display": null,
611
  "error": null,
612
+ "input_metadata": null,
613
  "meta": {
614
+ "color": "blue",
615
+ "doc": null,
616
  "inputs": [
617
  {
618
  "name": "x",
 
633
  }
634
  ],
635
  "params": [
636
+ {
637
+ "default": null,
638
+ "name": "relative_tolerance",
639
+ "type": {
640
+ "type": "None"
641
+ }
642
+ },
643
  {
644
  "default": null,
645
  "name": "absolute_tolerance",
 
648
  }
649
  },
650
  {
651
+ "default": "dopri8",
652
  "name": "method",
653
  "type": {
654
  "enum": [
 
664
  "implicit_adams"
665
  ]
666
  }
667
+ }
668
+ ],
669
+ "type": "basic"
670
+ },
671
+ "params": {
672
+ "absolute_tolerance": "",
673
+ "method": 1.0,
674
+ "relative_tolerance": null
675
+ },
676
+ "status": "done",
677
+ "title": "Neural ODE"
678
+ },
679
+ "dragHandle": ".bg-primary",
680
+ "height": 309.0,
681
+ "id": "Neural ODE 2",
682
+ "position": {
683
+ "x": 424.3358615871781,
684
+ "y": -784.0744992448747
685
+ },
686
+ "type": "basic",
687
+ "width": 308.0
688
+ },
689
+ {
690
+ "data": {
691
+ "__execution_delay": 0.0,
692
+ "collapsed": false,
693
+ "display": null,
694
+ "error": null,
695
+ "input_metadata": null,
696
+ "meta": {
697
+ "color": "orange",
698
+ "doc": null,
699
+ "inputs": [],
700
+ "name": "Input: tensor",
701
+ "outputs": [
702
+ {
703
+ "name": "output",
704
+ "position": "top",
705
+ "type": {
706
+ "type": "tensor"
707
+ }
708
+ }
709
+ ],
710
+ "params": [
711
  {
712
  "default": null,
713
+ "name": "name",
714
  "type": {
715
  "type": "None"
716
  }
 
719
  "type": "basic"
720
  },
721
  "params": {
722
+ "name": "embedding"
 
 
723
  },
724
+ "status": "done",
725
+ "title": "Input: tensor"
726
  },
727
  "dragHandle": ".bg-primary",
728
  "height": 200.0,
729
+ "id": "Input: tensor 1",
730
+ "position": {
731
+ "x": 231.36175845539515,
732
+ "y": 526.043774935379
733
+ },
734
+ "type": "basic",
735
+ "width": 200.0
736
+ },
737
+ {
738
+ "data": {
739
+ "__execution_delay": 0.0,
740
+ "collapsed": false,
741
+ "display": null,
742
+ "error": null,
743
+ "input_metadata": null,
744
+ "meta": {
745
+ "color": "orange",
746
+ "doc": null,
747
+ "inputs": [],
748
+ "name": "Input: tensor",
749
+ "outputs": [
750
+ {
751
+ "name": "output",
752
+ "position": "top",
753
+ "type": {
754
+ "type": "tensor"
755
+ }
756
+ }
757
+ ],
758
+ "params": [
759
+ {
760
+ "default": null,
761
+ "name": "name",
762
+ "type": {
763
+ "type": "None"
764
+ }
765
+ }
766
+ ],
767
+ "type": "basic"
768
+ },
769
+ "params": {
770
+ "name": "label"
771
+ },
772
+ "status": "done",
773
+ "title": "Input: tensor"
774
+ },
775
+ "dragHandle": ".bg-primary",
776
+ "height": 200.0,
777
+ "id": "Input: tensor 2",
778
  "position": {
779
+ "x": 787.538183263333,
780
+ "y": -801.4448540205135
781
  },
782
  "type": "basic",
783
  "width": 200.0
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 .handle-name {
147
- right: 20px;
 
 
 
 
148
  }
149
 
150
- .react-flow__handle-right .handle-name {
151
- left: 20px;
 
 
 
 
152
  }
153
 
154
- .react-flow__handle-top .handle-name {
155
- top: -20px;
156
- left: 5px;
157
- backdrop-filter: none;
 
 
 
 
158
  }
159
 
160
- .react-flow__handle-bottom .handle-name {
161
- top: 0px;
162
- left: 5px;
163
- backdrop-filter: none;
 
 
 
 
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: 10px;
457
- height: 10px;
 
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
- green: "oklch(75% 0.2 130)",
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 && <span className="title-icon">⚠️</span>}
86
- {expanded || <span className="title-icon">⋯</span>}
 
 
 
 
 
 
 
 
 
 
 
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
- function ParamName({ name }: { name: string }) {
13
- return <span className="param-name bg-base-200">{name.replace(/_/g, " ")}</span>;
 
 
 
 
 
 
 
 
 
 
 
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"])],