relationalai 0.11.3__py3-none-any.whl → 0.12.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- relationalai/clients/config.py +7 -0
- relationalai/clients/direct_access_client.py +113 -0
- relationalai/clients/snowflake.py +41 -107
- relationalai/clients/use_index_poller.py +349 -188
- relationalai/early_access/dsl/bindings/csv.py +2 -2
- relationalai/early_access/metamodel/rewrite/__init__.py +5 -3
- relationalai/early_access/rel/rewrite/__init__.py +1 -1
- relationalai/errors.py +24 -3
- relationalai/semantics/internal/annotations.py +1 -0
- relationalai/semantics/internal/internal.py +22 -4
- relationalai/semantics/lqp/builtins.py +1 -0
- relationalai/semantics/lqp/executor.py +61 -12
- relationalai/semantics/lqp/intrinsics.py +23 -0
- relationalai/semantics/lqp/model2lqp.py +13 -4
- relationalai/semantics/lqp/passes.py +4 -6
- relationalai/semantics/lqp/primitives.py +12 -1
- relationalai/semantics/{rel → lqp}/rewrite/__init__.py +6 -0
- relationalai/semantics/lqp/rewrite/extract_common.py +362 -0
- relationalai/semantics/metamodel/builtins.py +20 -2
- relationalai/semantics/metamodel/factory.py +3 -2
- relationalai/semantics/metamodel/rewrite/__init__.py +3 -9
- relationalai/semantics/reasoners/graph/core.py +273 -71
- relationalai/semantics/reasoners/optimization/solvers_dev.py +20 -1
- relationalai/semantics/reasoners/optimization/solvers_pb.py +24 -3
- relationalai/semantics/rel/builtins.py +5 -1
- relationalai/semantics/rel/compiler.py +7 -19
- relationalai/semantics/rel/executor.py +2 -2
- relationalai/semantics/rel/rel.py +6 -0
- relationalai/semantics/rel/rel_utils.py +8 -1
- relationalai/semantics/sql/compiler.py +122 -42
- relationalai/semantics/sql/executor/duck_db.py +28 -3
- relationalai/semantics/sql/rewrite/denormalize.py +4 -6
- relationalai/semantics/sql/rewrite/recursive_union.py +23 -3
- relationalai/semantics/sql/sql.py +27 -0
- relationalai/semantics/std/__init__.py +2 -1
- relationalai/semantics/std/datetime.py +4 -0
- relationalai/semantics/std/re.py +83 -0
- relationalai/semantics/std/strings.py +1 -1
- relationalai/tools/cli.py +11 -4
- relationalai/tools/cli_controls.py +445 -60
- relationalai/util/format.py +78 -1
- {relationalai-0.11.3.dist-info → relationalai-0.12.0.dist-info}/METADATA +7 -5
- {relationalai-0.11.3.dist-info → relationalai-0.12.0.dist-info}/RECORD +51 -50
- relationalai/semantics/metamodel/rewrite/gc_nodes.py +0 -58
- relationalai/semantics/metamodel/rewrite/list_types.py +0 -109
- relationalai/semantics/rel/rewrite/extract_common.py +0 -451
- /relationalai/semantics/{rel → lqp}/rewrite/cdc.py +0 -0
- /relationalai/semantics/{metamodel → lqp}/rewrite/extract_keys.py +0 -0
- /relationalai/semantics/{metamodel → lqp}/rewrite/fd_constraints.py +0 -0
- /relationalai/semantics/{rel → lqp}/rewrite/quantify_vars.py +0 -0
- /relationalai/semantics/{metamodel → lqp}/rewrite/splinter.py +0 -0
- {relationalai-0.11.3.dist-info → relationalai-0.12.0.dist-info}/WHEEL +0 -0
- {relationalai-0.11.3.dist-info → relationalai-0.12.0.dist-info}/entry_points.txt +0 -0
- {relationalai-0.11.3.dist-info → relationalai-0.12.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -20,6 +20,7 @@ from relationalai.semantics import (
|
|
|
20
20
|
count, sum, avg,
|
|
21
21
|
)
|
|
22
22
|
from relationalai.docutils import include_in_docs
|
|
23
|
+
from relationalai.semantics.internal import annotations
|
|
23
24
|
from relationalai.semantics.std.math import abs, isnan, isinf, maximum, natural_log, sqrt
|
|
24
25
|
|
|
25
26
|
Numeric = Union[int, float, Decimal]
|
|
@@ -1250,6 +1251,8 @@ class Graph():
|
|
|
1250
1251
|
def _num_nodes(self):
|
|
1251
1252
|
"""Lazily define and cache the self._num_nodes relationship."""
|
|
1252
1253
|
_num_nodes_rel = self._model.Relationship("The graph has {num_nodes:Integer} nodes")
|
|
1254
|
+
_num_nodes_rel.annotate(annotations.track("graphs", "num_nodes"))
|
|
1255
|
+
|
|
1253
1256
|
define(_num_nodes_rel(count(self.Node) | 0))
|
|
1254
1257
|
return _num_nodes_rel
|
|
1255
1258
|
|
|
@@ -1316,6 +1319,7 @@ class Graph():
|
|
|
1316
1319
|
def _num_edges(self):
|
|
1317
1320
|
"""Lazily define and cache the self._num_edges relationship."""
|
|
1318
1321
|
_num_edges_rel = self._model.Relationship("The graph has {num_edges:Integer} edges")
|
|
1322
|
+
_num_edges_rel.annotate(annotations.track("graphs", "num_edges"))
|
|
1319
1323
|
|
|
1320
1324
|
src, dst = self.Node.ref(), self.Node.ref()
|
|
1321
1325
|
if self.directed:
|
|
@@ -1468,7 +1472,9 @@ class Graph():
|
|
|
1468
1472
|
@cached_property
|
|
1469
1473
|
def _neighbor(self):
|
|
1470
1474
|
"""Lazily define and cache the self._neighbor relationship."""
|
|
1471
|
-
|
|
1475
|
+
_neighbor_rel = self._create_neighbor_relationship(nodes_subset=None)
|
|
1476
|
+
_neighbor_rel.annotate(annotations.track("graphs", "neighbor"))
|
|
1477
|
+
return _neighbor_rel
|
|
1472
1478
|
|
|
1473
1479
|
def _neighbor_of(self, nodes_subset: Relationship):
|
|
1474
1480
|
"""
|
|
@@ -1476,7 +1482,9 @@ class Graph():
|
|
|
1476
1482
|
in `nodes_subset`. Note this relationship is not cached; it is
|
|
1477
1483
|
specific to the callsite.
|
|
1478
1484
|
"""
|
|
1479
|
-
|
|
1485
|
+
_neighbor_rel = self._create_neighbor_relationship(nodes_subset=nodes_subset)
|
|
1486
|
+
_neighbor_rel.annotate(annotations.track("graphs", "neighbor_of"))
|
|
1487
|
+
return _neighbor_rel
|
|
1480
1488
|
|
|
1481
1489
|
def _create_neighbor_relationship(self, *, nodes_subset: Optional[Relationship]):
|
|
1482
1490
|
_neighbor_rel = self._model.Relationship(f"{{src:{self._NodeConceptStr}}} has neighbor {{dst:{self._NodeConceptStr}}}")
|
|
@@ -1626,7 +1634,9 @@ class Graph():
|
|
|
1626
1634
|
@cached_property
|
|
1627
1635
|
def _inneighbor(self):
|
|
1628
1636
|
"""Lazily define and cache the self._inneighbor relationship."""
|
|
1629
|
-
|
|
1637
|
+
_inneighbor_rel = self._create_inneighbor_relationship(nodes_subset=None)
|
|
1638
|
+
_inneighbor_rel.annotate(annotations.track("graphs", "inneighbor"))
|
|
1639
|
+
return _inneighbor_rel
|
|
1630
1640
|
|
|
1631
1641
|
def _inneighbor_of(self, nodes_subset: Relationship):
|
|
1632
1642
|
"""
|
|
@@ -1634,7 +1644,9 @@ class Graph():
|
|
|
1634
1644
|
in `nodes_subset`. Note this relationship is not cached; it is
|
|
1635
1645
|
specific to the callsite.
|
|
1636
1646
|
"""
|
|
1637
|
-
|
|
1647
|
+
_inneighbor_rel = self._create_inneighbor_relationship(nodes_subset=nodes_subset)
|
|
1648
|
+
_inneighbor_rel.annotate(annotations.track("graphs", "inneighbor_of"))
|
|
1649
|
+
return _inneighbor_rel
|
|
1638
1650
|
|
|
1639
1651
|
def _create_inneighbor_relationship(self, *, nodes_subset: Optional[Relationship]):
|
|
1640
1652
|
_inneighbor_rel = self._model.Relationship(f"{{dst:{self._NodeConceptStr}}} has inneighbor {{src:{self._NodeConceptStr}}}")
|
|
@@ -1777,7 +1789,9 @@ class Graph():
|
|
|
1777
1789
|
@cached_property
|
|
1778
1790
|
def _outneighbor(self):
|
|
1779
1791
|
"""Lazily define and cache the self._outneighbor relationship."""
|
|
1780
|
-
|
|
1792
|
+
_outneighbor_rel = self._create_outneighbor_relationship(nodes_subset=None)
|
|
1793
|
+
_outneighbor_rel.annotate(annotations.track("graphs", "outneighbor"))
|
|
1794
|
+
return _outneighbor_rel
|
|
1781
1795
|
|
|
1782
1796
|
def _outneighbor_of(self, nodes_subset: Relationship):
|
|
1783
1797
|
"""
|
|
@@ -1785,7 +1799,9 @@ class Graph():
|
|
|
1785
1799
|
in `nodes_subset`. Note this relationship is not cached; it is
|
|
1786
1800
|
specific to the callsite.
|
|
1787
1801
|
"""
|
|
1788
|
-
|
|
1802
|
+
_outneighbor_rel = self._create_outneighbor_relationship(nodes_subset=nodes_subset)
|
|
1803
|
+
_outneighbor_rel.annotate(annotations.track("graphs", "outneighbor_of"))
|
|
1804
|
+
return _outneighbor_rel
|
|
1789
1805
|
|
|
1790
1806
|
def _create_outneighbor_relationship(self, *, nodes_subset: Optional[Relationship]):
|
|
1791
1807
|
_outneighbor_rel = self._model.Relationship(f"{{src:{self._NodeConceptStr}}} has outneighbor {{dst:{self._NodeConceptStr}}}")
|
|
@@ -1914,6 +1930,8 @@ class Graph():
|
|
|
1914
1930
|
def _common_neighbor(self):
|
|
1915
1931
|
"""Lazily define and cache the self._common_neighbor relationship."""
|
|
1916
1932
|
_common_neighbor_rel = self._model.Relationship(f"{{node_a:{self._NodeConceptStr}}} and {{node_b:{self._NodeConceptStr}}} have common neighbor {{node_c:{self._NodeConceptStr}}}")
|
|
1933
|
+
_common_neighbor_rel.annotate(annotations.track("graphs", "common_neighbor"))
|
|
1934
|
+
|
|
1917
1935
|
node_a, node_b, node_c = self.Node.ref(), self.Node.ref(), self.Node.ref()
|
|
1918
1936
|
where(self._neighbor(node_a, node_c), self._neighbor(node_b, node_c)).define(_common_neighbor_rel(node_a, node_b, node_c))
|
|
1919
1937
|
return _common_neighbor_rel
|
|
@@ -2072,7 +2090,9 @@ class Graph():
|
|
|
2072
2090
|
@cached_property
|
|
2073
2091
|
def _degree(self):
|
|
2074
2092
|
"""Lazily define and cache the self._degree relationship."""
|
|
2075
|
-
|
|
2093
|
+
_degree_rel = self._create_degree_relationship(nodes_subset=None)
|
|
2094
|
+
_degree_rel.annotate(annotations.track("graphs", "degree"))
|
|
2095
|
+
return _degree_rel
|
|
2076
2096
|
|
|
2077
2097
|
def _degree_of(self, nodes_subset: Relationship):
|
|
2078
2098
|
"""
|
|
@@ -2080,7 +2100,9 @@ class Graph():
|
|
|
2080
2100
|
in `nodes_subset`. Note this relationship is not cached; it is
|
|
2081
2101
|
specific to the callsite.
|
|
2082
2102
|
"""
|
|
2083
|
-
|
|
2103
|
+
_degree_rel = self._create_degree_relationship(nodes_subset=nodes_subset)
|
|
2104
|
+
_degree_rel.annotate(annotations.track("graphs", "degree_of"))
|
|
2105
|
+
return _degree_rel
|
|
2084
2106
|
|
|
2085
2107
|
def _create_degree_relationship(self, *, nodes_subset: Optional[Relationship]):
|
|
2086
2108
|
_degree_rel = self._model.Relationship(f"{{node:{self._NodeConceptStr}}} has degree {{count:Integer}}")
|
|
@@ -2263,7 +2285,9 @@ class Graph():
|
|
|
2263
2285
|
@cached_property
|
|
2264
2286
|
def _indegree(self):
|
|
2265
2287
|
"""Lazily define and cache the self._indegree relationship."""
|
|
2266
|
-
|
|
2288
|
+
_indegree_rel = self._create_indegree_relationship(nodes_subset=None)
|
|
2289
|
+
_indegree_rel.annotate(annotations.track("graphs", "indegree"))
|
|
2290
|
+
return _indegree_rel
|
|
2267
2291
|
|
|
2268
2292
|
def _indegree_of(self, nodes_subset: Relationship):
|
|
2269
2293
|
"""
|
|
@@ -2271,7 +2295,9 @@ class Graph():
|
|
|
2271
2295
|
in `nodes_subset`. Note this relationship is not cached; it is
|
|
2272
2296
|
specific to the callsite.
|
|
2273
2297
|
"""
|
|
2274
|
-
|
|
2298
|
+
_indegree_rel = self._create_indegree_relationship(nodes_subset=nodes_subset)
|
|
2299
|
+
_indegree_rel.annotate(annotations.track("graphs", "indegree_of"))
|
|
2300
|
+
return _indegree_rel
|
|
2275
2301
|
|
|
2276
2302
|
def _create_indegree_relationship(self, *, nodes_subset: Optional[Relationship]):
|
|
2277
2303
|
_indegree_rel = self._model.Relationship(f"{{node:{self._NodeConceptStr}}} has indegree {{count:Integer}}")
|
|
@@ -2443,7 +2469,9 @@ class Graph():
|
|
|
2443
2469
|
@cached_property
|
|
2444
2470
|
def _outdegree(self):
|
|
2445
2471
|
"""Lazily define and cache the self._outdegree relationship."""
|
|
2446
|
-
|
|
2472
|
+
_outdegree_rel = self._create_outdegree_relationship(nodes_subset=None)
|
|
2473
|
+
_outdegree_rel.annotate(annotations.track("graphs", "outdegree"))
|
|
2474
|
+
return _outdegree_rel
|
|
2447
2475
|
|
|
2448
2476
|
def _outdegree_of(self, nodes_subset: Relationship):
|
|
2449
2477
|
"""
|
|
@@ -2451,7 +2479,9 @@ class Graph():
|
|
|
2451
2479
|
in `nodes_subset`. Note this relationship is not cached; it is
|
|
2452
2480
|
specific to the callsite.
|
|
2453
2481
|
"""
|
|
2454
|
-
|
|
2482
|
+
_outdegree_rel = self._create_outdegree_relationship(nodes_subset=nodes_subset)
|
|
2483
|
+
_outdegree_rel.annotate(annotations.track("graphs", "outdegree_of"))
|
|
2484
|
+
return _outdegree_rel
|
|
2455
2485
|
|
|
2456
2486
|
def _create_outdegree_relationship(self, *, nodes_subset: Optional[Relationship]):
|
|
2457
2487
|
_outdegree_rel = self._model.Relationship(f"{{node:{self._NodeConceptStr}}} has outdegree {{count:Integer}}")
|
|
@@ -2588,7 +2618,9 @@ class Graph():
|
|
|
2588
2618
|
@cached_property
|
|
2589
2619
|
def _weighted_degree(self):
|
|
2590
2620
|
"""Lazily define and cache the self._weighted_degree relationship."""
|
|
2591
|
-
|
|
2621
|
+
_weighted_degree_rel = self._create_weighted_degree_relationship(nodes_subset=None)
|
|
2622
|
+
_weighted_degree_rel.annotate(annotations.track("graphs", "weighted_degree"))
|
|
2623
|
+
return _weighted_degree_rel
|
|
2592
2624
|
|
|
2593
2625
|
def _weighted_degree_of(self, nodes_subset: Relationship):
|
|
2594
2626
|
"""
|
|
@@ -2596,7 +2628,9 @@ class Graph():
|
|
|
2596
2628
|
in `nodes_subset`. Note this relationship is not cached; it is
|
|
2597
2629
|
specific to the callsite.
|
|
2598
2630
|
"""
|
|
2599
|
-
|
|
2631
|
+
_weighted_degree_rel = self._create_weighted_degree_relationship(nodes_subset=nodes_subset)
|
|
2632
|
+
_weighted_degree_rel.annotate(annotations.track("graphs", "weighted_degree_of"))
|
|
2633
|
+
return _weighted_degree_rel
|
|
2600
2634
|
|
|
2601
2635
|
def _create_weighted_degree_relationship(self, *, nodes_subset: Optional[Relationship]):
|
|
2602
2636
|
_weighted_degree_rel = self._model.Relationship(f"{{node:{self._NodeConceptStr}}} has weighted degree {{weight:Float}}")
|
|
@@ -2744,7 +2778,9 @@ class Graph():
|
|
|
2744
2778
|
@cached_property
|
|
2745
2779
|
def _weighted_indegree(self):
|
|
2746
2780
|
"""Lazily define and cache the self._weighted_indegree relationship."""
|
|
2747
|
-
|
|
2781
|
+
_weighted_indegree_rel = self._create_weighted_indegree_relationship(nodes_subset=None)
|
|
2782
|
+
_weighted_indegree_rel.annotate(annotations.track("graphs", "weighted_indegree"))
|
|
2783
|
+
return _weighted_indegree_rel
|
|
2748
2784
|
|
|
2749
2785
|
def _weighted_indegree_of(self, nodes_subset: Relationship):
|
|
2750
2786
|
"""
|
|
@@ -2752,7 +2788,9 @@ class Graph():
|
|
|
2752
2788
|
in `nodes_subset`. Note this relationship is not cached; it is
|
|
2753
2789
|
specific to the callsite.
|
|
2754
2790
|
"""
|
|
2755
|
-
|
|
2791
|
+
_weighted_indegree_rel = self._create_weighted_indegree_relationship(nodes_subset=nodes_subset)
|
|
2792
|
+
_weighted_indegree_rel.annotate(annotations.track("graphs", "weighted_indegree_of"))
|
|
2793
|
+
return _weighted_indegree_rel
|
|
2756
2794
|
|
|
2757
2795
|
def _create_weighted_indegree_relationship(self, *, nodes_subset: Optional[Relationship]):
|
|
2758
2796
|
_weighted_indegree_rel = self._model.Relationship(f"{{node:{self._NodeConceptStr}}} has weighted indegree {{weight:Float}}")
|
|
@@ -2892,7 +2930,9 @@ class Graph():
|
|
|
2892
2930
|
@cached_property
|
|
2893
2931
|
def _weighted_outdegree(self):
|
|
2894
2932
|
"""Lazily define and cache the self._weighted_outdegree relationship."""
|
|
2895
|
-
|
|
2933
|
+
_weighted_outdegree_rel = self._create_weighted_outdegree_relationship(nodes_subset=None)
|
|
2934
|
+
_weighted_outdegree_rel.annotate(annotations.track("graphs", "weighted_outdegree"))
|
|
2935
|
+
return _weighted_outdegree_rel
|
|
2896
2936
|
|
|
2897
2937
|
def _weighted_outdegree_of(self, nodes_subset: Relationship):
|
|
2898
2938
|
"""
|
|
@@ -2900,7 +2940,9 @@ class Graph():
|
|
|
2900
2940
|
in `nodes_subset`. Note this relationship is not cached; it is
|
|
2901
2941
|
specific to the callsite.
|
|
2902
2942
|
"""
|
|
2903
|
-
|
|
2943
|
+
_weighted_outdegree_rel = self._create_weighted_outdegree_relationship(nodes_subset=nodes_subset)
|
|
2944
|
+
_weighted_outdegree_rel.annotate(annotations.track("graphs", "weighted_outdegree_of"))
|
|
2945
|
+
return _weighted_outdegree_rel
|
|
2904
2946
|
|
|
2905
2947
|
def _create_weighted_outdegree_relationship(self, *, nodes_subset: Optional[Relationship]):
|
|
2906
2948
|
_weighted_outdegree_rel = self._model.Relationship(f"{{node:{self._NodeConceptStr}}} has weighted outdegree {{weight:Float}}")
|
|
@@ -3067,7 +3109,9 @@ class Graph():
|
|
|
3067
3109
|
@cached_property
|
|
3068
3110
|
def _degree_centrality(self):
|
|
3069
3111
|
"""Lazily define and cache the self._degree_centrality relationship."""
|
|
3070
|
-
|
|
3112
|
+
_degree_centrality_rel = self._create_degree_centrality_relationship(nodes_subset=None)
|
|
3113
|
+
_degree_centrality_rel.annotate(annotations.track("graphs", "degree_centrality"))
|
|
3114
|
+
return _degree_centrality_rel
|
|
3071
3115
|
|
|
3072
3116
|
def _degree_centrality_of(self, nodes_subset: Relationship):
|
|
3073
3117
|
"""
|
|
@@ -3075,7 +3119,9 @@ class Graph():
|
|
|
3075
3119
|
in `nodes_subset`. Note this relationship is not cached; it is
|
|
3076
3120
|
specific to the callsite.
|
|
3077
3121
|
"""
|
|
3078
|
-
|
|
3122
|
+
_degree_centrality_rel = self._create_degree_centrality_relationship(nodes_subset=nodes_subset)
|
|
3123
|
+
_degree_centrality_rel.annotate(annotations.track("graphs", "degree_centrality_of"))
|
|
3124
|
+
return _degree_centrality_rel
|
|
3079
3125
|
|
|
3080
3126
|
def _create_degree_centrality_relationship(self, *, nodes_subset: Optional[Relationship]):
|
|
3081
3127
|
"""Create a degree centrality relationship, optionally constrained to a subset of nodes."""
|
|
@@ -3572,6 +3618,7 @@ class Graph():
|
|
|
3572
3618
|
def _triangle(self):
|
|
3573
3619
|
"""Lazily define and cache the self._triangle relationship."""
|
|
3574
3620
|
_triangle_rel = self._model.Relationship(f"{{node_a:{self._NodeConceptStr}}} and {{node_b:{self._NodeConceptStr}}} and {{node_c:{self._NodeConceptStr}}} form a triangle")
|
|
3621
|
+
_triangle_rel.annotate(annotations.track("graphs", "triangle"))
|
|
3575
3622
|
|
|
3576
3623
|
a, b, c = self.Node.ref(), self.Node.ref(), self.Node.ref()
|
|
3577
3624
|
|
|
@@ -3714,6 +3761,7 @@ class Graph():
|
|
|
3714
3761
|
def _unique_triangle(self):
|
|
3715
3762
|
"""Lazily define and cache the self._unique_triangle relationship."""
|
|
3716
3763
|
_unique_triangle_rel = self._model.Relationship(f"{{node_a:{self._NodeConceptStr}}} and {{node_b:{self._NodeConceptStr}}} and {{node_c:{self._NodeConceptStr}}} form unique triangle")
|
|
3764
|
+
_unique_triangle_rel.annotate(annotations.track("graphs", "unique_triangle"))
|
|
3717
3765
|
|
|
3718
3766
|
node_a, node_b, node_c = self.Node.ref(), self.Node.ref(), self.Node.ref()
|
|
3719
3767
|
|
|
@@ -3849,6 +3897,7 @@ class Graph():
|
|
|
3849
3897
|
def _num_triangles(self):
|
|
3850
3898
|
"""Lazily define and cache the self._num_triangles relationship."""
|
|
3851
3899
|
_num_triangles_rel = self._model.Relationship("The graph has {num_triangles:Integer} triangles")
|
|
3900
|
+
_num_triangles_rel.annotate(annotations.track("graphs", "num_triangles"))
|
|
3852
3901
|
|
|
3853
3902
|
_num_triangles = Integer.ref()
|
|
3854
3903
|
node_a, node_b, node_c = self.Node.ref(), self.Node.ref(), self.Node.ref()
|
|
@@ -3865,12 +3914,19 @@ class Graph():
|
|
|
3865
3914
|
|
|
3866
3915
|
|
|
3867
3916
|
@include_in_docs
|
|
3868
|
-
def triangle_count(self):
|
|
3917
|
+
def triangle_count(self, *, of: Optional[Relationship] = None):
|
|
3869
3918
|
"""Returns a binary relationship containing the number of unique triangles each node belongs to.
|
|
3870
3919
|
|
|
3871
3920
|
A triangle is a set of three nodes where each node has a directed
|
|
3872
3921
|
or undirected edge to the other two nodes, forming a 3-cycle.
|
|
3873
3922
|
|
|
3923
|
+
Parameters
|
|
3924
|
+
----------
|
|
3925
|
+
of : Relationship, optional
|
|
3926
|
+
A unary relationship containing a subset of the graph's nodes. When
|
|
3927
|
+
provided, constrains the domain of the triangle count computation: only
|
|
3928
|
+
triangle counts of nodes in this relationship are computed and returned.
|
|
3929
|
+
|
|
3874
3930
|
Returns
|
|
3875
3931
|
-------
|
|
3876
3932
|
Relationship
|
|
@@ -3926,6 +3982,31 @@ class Graph():
|
|
|
3926
3982
|
3 4 0
|
|
3927
3983
|
4 5 0
|
|
3928
3984
|
|
|
3985
|
+
>>> # 4. Use 'of' parameter to constrain the set of nodes to compute triangle counts of
|
|
3986
|
+
>>> # Define a subset containing only nodes 1 and 3
|
|
3987
|
+
>>> subset = model.Relationship(f"{{node:{Node}}} is in subset")
|
|
3988
|
+
>>> node = Node.ref()
|
|
3989
|
+
>>> where(union(node.id == 1, node.id == 3)).define(subset(node))
|
|
3990
|
+
>>>
|
|
3991
|
+
>>> # Get triangle counts only of nodes in the subset
|
|
3992
|
+
>>> constrained_triangle_count = graph.triangle_count(of=subset)
|
|
3993
|
+
>>> select(node.id, count).where(constrained_triangle_count(node, count)).inspect()
|
|
3994
|
+
▰▰▰▰ Setup complete
|
|
3995
|
+
id count
|
|
3996
|
+
0 1 1
|
|
3997
|
+
1 3 1
|
|
3998
|
+
|
|
3999
|
+
Notes
|
|
4000
|
+
-----
|
|
4001
|
+
The ``triangle_count()`` method, called with no parameters, computes and caches
|
|
4002
|
+
the full triangle count relationship, providing efficient reuse across multiple
|
|
4003
|
+
calls to ``triangle_count()``. In contrast, ``triangle_count(of=subset)`` computes a
|
|
4004
|
+
constrained relationship specific to the passed-in ``subset`` and that
|
|
4005
|
+
call site. When a significant fraction of the triangle count relation is needed
|
|
4006
|
+
across a program, ``triangle_count()`` is typically more efficient; this is the
|
|
4007
|
+
typical case. Use ``triangle_count(of=subset)`` only when small subsets of the
|
|
4008
|
+
triangle count relationship are needed collectively across the program.
|
|
4009
|
+
|
|
3929
4010
|
See Also
|
|
3930
4011
|
--------
|
|
3931
4012
|
triangle
|
|
@@ -3933,15 +4014,39 @@ class Graph():
|
|
|
3933
4014
|
num_triangles
|
|
3934
4015
|
|
|
3935
4016
|
"""
|
|
4017
|
+
if of is not None:
|
|
4018
|
+
self._validate_node_subset_parameter(of)
|
|
4019
|
+
return self._triangle_count_of(of)
|
|
3936
4020
|
return self._triangle_count
|
|
3937
4021
|
|
|
3938
4022
|
@cached_property
|
|
3939
4023
|
def _triangle_count(self):
|
|
3940
4024
|
"""Lazily define and cache the self._triangle_count relationship."""
|
|
4025
|
+
_triangle_count_rel = self._create_triangle_count_relationship(nodes_subset=None)
|
|
4026
|
+
_triangle_count_rel.annotate(annotations.track("graphs", "triangle_count"))
|
|
4027
|
+
return _triangle_count_rel
|
|
4028
|
+
|
|
4029
|
+
def _triangle_count_of(self, nodes_subset: Relationship):
|
|
4030
|
+
"""
|
|
4031
|
+
Create a triangle count relationship constrained to the subset of nodes
|
|
4032
|
+
in `nodes_subset`. Note this relationship is not cached; it is
|
|
4033
|
+
specific to the callsite.
|
|
4034
|
+
"""
|
|
4035
|
+
_triangle_count_rel = self._create_triangle_count_relationship(nodes_subset=nodes_subset)
|
|
4036
|
+
_triangle_count_rel.annotate(annotations.track("graphs", "triangle_count_of"))
|
|
4037
|
+
return _triangle_count_rel
|
|
4038
|
+
|
|
4039
|
+
def _create_triangle_count_relationship(self, *, nodes_subset: Optional[Relationship]):
|
|
4040
|
+
"""Create a triangle count relationship, optionally constrained to a subset of nodes."""
|
|
3941
4041
|
_triangle_count_rel = self._model.Relationship(f"{{node:{self._NodeConceptStr}}} belongs to {{count:Integer}} triangles")
|
|
3942
4042
|
|
|
4043
|
+
if nodes_subset is None:
|
|
4044
|
+
node_constraint = self.Node # No constraint on nodes.
|
|
4045
|
+
else:
|
|
4046
|
+
node_constraint = nodes_subset(self.Node) # Nodes constrained to given subset.
|
|
4047
|
+
|
|
3943
4048
|
where(
|
|
3944
|
-
|
|
4049
|
+
node_constraint,
|
|
3945
4050
|
_count := self._nonzero_triangle_count_fragment(self.Node) | 0
|
|
3946
4051
|
).define(_triangle_count_rel(self.Node, _count))
|
|
3947
4052
|
|
|
@@ -4061,7 +4166,7 @@ class Graph():
|
|
|
4061
4166
|
|
|
4062
4167
|
|
|
4063
4168
|
@include_in_docs
|
|
4064
|
-
def local_clustering_coefficient(self):
|
|
4169
|
+
def local_clustering_coefficient(self, *, of: Optional[Relationship] = None):
|
|
4065
4170
|
"""Returns a binary relationship containing the local clustering coefficient of each node.
|
|
4066
4171
|
|
|
4067
4172
|
The local clustering coefficient quantifies how close a node's neighbors
|
|
@@ -4070,6 +4175,14 @@ class Graph():
|
|
|
4070
4175
|
directly connecting them, and 1.0 indicates all neighbors have edges
|
|
4071
4176
|
directly connecting them.
|
|
4072
4177
|
|
|
4178
|
+
Parameters
|
|
4179
|
+
----------
|
|
4180
|
+
of : Relationship, optional
|
|
4181
|
+
A unary relationship containing a subset of the graph's nodes. When
|
|
4182
|
+
provided, constrains the domain of the local clustering coefficient
|
|
4183
|
+
computation: only coefficients of nodes in this relationship are
|
|
4184
|
+
computed and returned.
|
|
4185
|
+
|
|
4073
4186
|
Returns
|
|
4074
4187
|
-------
|
|
4075
4188
|
Relationship
|
|
@@ -4096,17 +4209,6 @@ class Graph():
|
|
|
4096
4209
|
| Directed | No | Undirected only. |
|
|
4097
4210
|
| Weighted | Yes | Weights are ignored. |
|
|
4098
4211
|
|
|
4099
|
-
Notes
|
|
4100
|
-
-----
|
|
4101
|
-
The formal definition of the local clustering coefficient (`C`) for a
|
|
4102
|
-
node (`v`) can be given as::
|
|
4103
|
-
|
|
4104
|
-
C(v) = (2 * num_edges) / (degree(v) * (degree(v) - 1))
|
|
4105
|
-
|
|
4106
|
-
Here, `num_edges` represents the number of edges between the
|
|
4107
|
-
neighbors of node `v`, and `degree(v)` represents the degree of the
|
|
4108
|
-
node, i.e., the number of edges connected to the node.
|
|
4109
|
-
|
|
4110
4212
|
Examples
|
|
4111
4213
|
--------
|
|
4112
4214
|
>>> from relationalai.semantics import Model, define, select, Float
|
|
@@ -4142,6 +4244,41 @@ class Graph():
|
|
|
4142
4244
|
3 4 0.333333
|
|
4143
4245
|
4 5 0.000000
|
|
4144
4246
|
|
|
4247
|
+
>>> # 4. Use 'of' parameter to constrain the set of nodes to compute local clustering coefficients of
|
|
4248
|
+
>>> # Define a subset containing only nodes 1 and 3
|
|
4249
|
+
>>> subset = model.Relationship(f"{{node:{Node}}} is in subset")
|
|
4250
|
+
>>> node = Node.ref()
|
|
4251
|
+
>>> where(union(node.id == 1, node.id == 3)).define(subset(node))
|
|
4252
|
+
>>>
|
|
4253
|
+
>>> # Get local clustering coefficients only of nodes in the subset
|
|
4254
|
+
>>> constrained_lcc = graph.local_clustering_coefficient(of=subset)
|
|
4255
|
+
>>> select(node.id, coeff).where(constrained_lcc(node, coeff)).inspect()
|
|
4256
|
+
▰▰▰▰ Setup complete
|
|
4257
|
+
id coeff
|
|
4258
|
+
0 1 1.000000
|
|
4259
|
+
1 3 0.666667
|
|
4260
|
+
|
|
4261
|
+
Notes
|
|
4262
|
+
-----
|
|
4263
|
+
The local clustering coefficient for node `v` is::
|
|
4264
|
+
|
|
4265
|
+
(2 * num_neighbor_edges(v)) / (ext_degree(v) * (ext_degree(v) - 1))
|
|
4266
|
+
|
|
4267
|
+
where `num_neighbor_edges(v)` is the number of edges between
|
|
4268
|
+
the neighbors of node `v`, and `ext_degree(v)` is the degree of the
|
|
4269
|
+
node excluding self-loops. If `ext_degree(v)` is less than 2,
|
|
4270
|
+
the local clustering coefficient is 0.0.
|
|
4271
|
+
|
|
4272
|
+
The ``local_clustering_coefficient()`` method, called with no parameters, computes
|
|
4273
|
+
and caches the full local clustering coefficient relationship, providing efficient
|
|
4274
|
+
reuse across multiple calls to ``local_clustering_coefficient()``. In contrast,
|
|
4275
|
+
``local_clustering_coefficient(of=subset)`` computes a constrained relationship
|
|
4276
|
+
specific to the passed-in ``subset`` and that call site. When a significant fraction
|
|
4277
|
+
of the local clustering coefficient relation is needed across a program,
|
|
4278
|
+
``local_clustering_coefficient()`` is typically more efficient; this is the typical
|
|
4279
|
+
case. Use ``local_clustering_coefficient(of=subset)`` only when small subsets of the
|
|
4280
|
+
local clustering coefficient relationship are needed collectively across the program.
|
|
4281
|
+
|
|
4145
4282
|
|
|
4146
4283
|
See Also
|
|
4147
4284
|
--------
|
|
@@ -4154,29 +4291,51 @@ class Graph():
|
|
|
4154
4291
|
raise NotImplementedError(
|
|
4155
4292
|
"`local_clustering_coefficient` is not applicable to directed graphs"
|
|
4156
4293
|
)
|
|
4294
|
+
|
|
4295
|
+
if of is not None:
|
|
4296
|
+
self._validate_node_subset_parameter(of)
|
|
4297
|
+
return self._local_clustering_coefficient_of(of)
|
|
4157
4298
|
return self._local_clustering_coefficient
|
|
4158
4299
|
|
|
4159
4300
|
@cached_property
|
|
4160
4301
|
def _local_clustering_coefficient(self):
|
|
4302
|
+
"""Lazily define and cache the self._local_clustering_coefficient relationship."""
|
|
4303
|
+
_local_clustering_coefficient_rel = self._create_local_clustering_coefficient_relationship(nodes_subset=None)
|
|
4304
|
+
_local_clustering_coefficient_rel.annotate(annotations.track("graphs", "local_clustering_coefficient"))
|
|
4305
|
+
return _local_clustering_coefficient_rel
|
|
4306
|
+
|
|
4307
|
+
def _local_clustering_coefficient_of(self, nodes_subset: Relationship):
|
|
4161
4308
|
"""
|
|
4162
|
-
|
|
4163
|
-
|
|
4309
|
+
Create a local clustering coefficient relationship constrained to the subset of nodes
|
|
4310
|
+
in `nodes_subset`. Note this relationship is not cached; it is
|
|
4311
|
+
specific to the callsite.
|
|
4164
4312
|
"""
|
|
4165
|
-
_local_clustering_coefficient_rel = self.
|
|
4313
|
+
_local_clustering_coefficient_rel = self._create_local_clustering_coefficient_relationship(nodes_subset=nodes_subset)
|
|
4314
|
+
_local_clustering_coefficient_rel.annotate(annotations.track("graphs", "local_clustering_coefficient_of"))
|
|
4315
|
+
return _local_clustering_coefficient_rel
|
|
4166
4316
|
|
|
4167
|
-
|
|
4168
|
-
|
|
4169
|
-
|
|
4170
|
-
)
|
|
4317
|
+
def _create_local_clustering_coefficient_relationship(self, *, nodes_subset: Optional[Relationship]):
|
|
4318
|
+
"""Create a local clustering coefficient relationship, optionally constrained to a subset of nodes."""
|
|
4319
|
+
_local_clustering_coefficient_rel = self._model.Relationship(f"{{node:{self._NodeConceptStr}}} has local clustering coefficient {{coefficient:Float}}")
|
|
4171
4320
|
|
|
4172
4321
|
node = self.Node.ref()
|
|
4322
|
+
|
|
4323
|
+
if nodes_subset is None:
|
|
4324
|
+
degree_no_self_rel = self._degree_no_self
|
|
4325
|
+
triangle_count_rel = self._triangle_count
|
|
4326
|
+
node_constraint = node # No constraint on nodes.
|
|
4327
|
+
else:
|
|
4328
|
+
degree_no_self_rel = self._degree_no_self_of(nodes_subset)
|
|
4329
|
+
triangle_count_rel = self._triangle_count_of(nodes_subset)
|
|
4330
|
+
node_constraint = nodes_subset(node) # Nodes constrained to given subset.
|
|
4331
|
+
|
|
4173
4332
|
degree_no_self = Integer.ref()
|
|
4174
4333
|
triangle_count = Integer.ref()
|
|
4175
4334
|
where(
|
|
4176
|
-
|
|
4335
|
+
node_constraint,
|
|
4177
4336
|
_lcc := where(
|
|
4178
|
-
|
|
4179
|
-
|
|
4337
|
+
degree_no_self_rel(node, degree_no_self),
|
|
4338
|
+
triangle_count_rel(node, triangle_count),
|
|
4180
4339
|
degree_no_self > 1
|
|
4181
4340
|
).select(
|
|
4182
4341
|
2.0 * triangle_count / (degree_no_self * (degree_no_self - 1.0))
|
|
@@ -4191,11 +4350,32 @@ class Graph():
|
|
|
4191
4350
|
Lazily define and cache the self._degree_no_self relationship,
|
|
4192
4351
|
a non-public helper for local_clustering_coefficient.
|
|
4193
4352
|
"""
|
|
4353
|
+
return self._create_degree_no_self_relationship(nodes_subset=None)
|
|
4354
|
+
|
|
4355
|
+
def _degree_no_self_of(self, nodes_subset: Relationship):
|
|
4356
|
+
"""
|
|
4357
|
+
Create a self-loop-exclusive degree relationship constrained to
|
|
4358
|
+
the subset of nodes in `nodes_subset`. Note this relationship
|
|
4359
|
+
is not cached; it is specific to the callsite.
|
|
4360
|
+
"""
|
|
4361
|
+
return self._create_degree_no_self_relationship(nodes_subset=nodes_subset)
|
|
4362
|
+
|
|
4363
|
+
def _create_degree_no_self_relationship(self, *, nodes_subset: Optional[Relationship]):
|
|
4364
|
+
"""
|
|
4365
|
+
Create a self-loop-exclusive degree relationship,
|
|
4366
|
+
optionally constrained to a subset of nodes.
|
|
4367
|
+
"""
|
|
4194
4368
|
_degree_no_self_rel = self._model.Relationship(f"{{node:{self._NodeConceptStr}}} has degree excluding self loops {{num:Integer}}")
|
|
4195
4369
|
|
|
4196
4370
|
node, neighbor = self.Node.ref(), self.Node.ref()
|
|
4371
|
+
|
|
4372
|
+
if nodes_subset is None:
|
|
4373
|
+
node_constraint = node # No constraint on nodes.
|
|
4374
|
+
else:
|
|
4375
|
+
node_constraint = nodes_subset(node) # Nodes constrained to given subset.
|
|
4376
|
+
|
|
4197
4377
|
where(
|
|
4198
|
-
|
|
4378
|
+
node_constraint,
|
|
4199
4379
|
_dns := count(neighbor).per(node).where(self._no_loop_edge(node, neighbor)) | 0,
|
|
4200
4380
|
).define(_degree_no_self_rel(node, _dns))
|
|
4201
4381
|
|
|
@@ -4279,6 +4459,7 @@ class Graph():
|
|
|
4279
4459
|
which only applies to undirected graphs.
|
|
4280
4460
|
"""
|
|
4281
4461
|
_average_clustering_coefficient_rel = self._model.Relationship("The graph has average clustering coefficient {{coefficient:Float}}")
|
|
4462
|
+
_average_clustering_coefficient_rel.annotate(annotations.track("graphs", "average_clustering_coefficient"))
|
|
4282
4463
|
|
|
4283
4464
|
if self.directed:
|
|
4284
4465
|
raise NotImplementedError(
|
|
@@ -4419,6 +4600,7 @@ class Graph():
|
|
|
4419
4600
|
def _reachable_from(self):
|
|
4420
4601
|
"""Lazily define and cache the self._reachable_from relationship."""
|
|
4421
4602
|
_reachable_from_rel = self._model.Relationship(f"{{node_a:{self._NodeConceptStr}}} reaches {{node_b:{self._NodeConceptStr}}}")
|
|
4603
|
+
_reachable_from_rel.annotate(annotations.track("graphs", "reachable_from"))
|
|
4422
4604
|
|
|
4423
4605
|
node_a, node_b, node_c = self.Node.ref(), self.Node.ref(), self.Node.ref()
|
|
4424
4606
|
define(_reachable_from_rel(node_a, node_a))
|
|
@@ -4561,9 +4743,12 @@ class Graph():
|
|
|
4561
4743
|
def _distance(self):
|
|
4562
4744
|
"""Lazily define and cache the self._distance relationship."""
|
|
4563
4745
|
if not self.weighted:
|
|
4564
|
-
|
|
4746
|
+
_distance_rel = self._distance_non_weighted
|
|
4565
4747
|
else:
|
|
4566
|
-
|
|
4748
|
+
_distance_rel = self._distance_weighted
|
|
4749
|
+
|
|
4750
|
+
_distance_rel.annotate(annotations.track("graphs", "distance"))
|
|
4751
|
+
return _distance_rel
|
|
4567
4752
|
|
|
4568
4753
|
@cached_property
|
|
4569
4754
|
def _distance_weighted(self):
|
|
@@ -4689,6 +4874,7 @@ class Graph():
|
|
|
4689
4874
|
def _weakly_connected_component(self):
|
|
4690
4875
|
"""Lazily define and cache the self._weakly_connected_component relationship."""
|
|
4691
4876
|
_weakly_connected_component_rel = self._model.Relationship(f"{{node:{self._NodeConceptStr}}} is in the connected component {{id:{self._NodeConceptStr}}}")
|
|
4877
|
+
_weakly_connected_component_rel.annotate(annotations.track("graphs", "weakly_connected_component"))
|
|
4692
4878
|
|
|
4693
4879
|
node, node_v, component = self.Node.ref(), self.Node.ref(), self.Node.ref()
|
|
4694
4880
|
node, component = union(
|
|
@@ -4812,6 +4998,8 @@ class Graph():
|
|
|
4812
4998
|
"""
|
|
4813
4999
|
_diameter_range_min_rel = self._model.Relationship("The graph has a min diameter range of {value:Integer}")
|
|
4814
5000
|
_diameter_range_max_rel = self._model.Relationship("The graph has a max diameter range of {value:Integer}")
|
|
5001
|
+
_diameter_range_min_rel.annotate(annotations.track("graphs", "diameter_range_min"))
|
|
5002
|
+
_diameter_range_max_rel.annotate(annotations.track("graphs", "diameter_range_max"))
|
|
4815
5003
|
|
|
4816
5004
|
component_node_pairs = self._model.Relationship(f"component id {{cid:{self._NodeConceptStr}}} has node id {{nid:{self._NodeConceptStr}}}")
|
|
4817
5005
|
nodeid, cid, degreevalue = self.Node.ref(), self.Node.ref(), Integer.ref()
|
|
@@ -4872,16 +5060,22 @@ class Graph():
|
|
|
4872
5060
|
|
|
4873
5061
|
@include_in_docs
|
|
4874
5062
|
def is_connected(self):
|
|
4875
|
-
"""Returns a
|
|
5063
|
+
"""Returns a unary relationship containing whether the graph is connected.
|
|
4876
5064
|
|
|
4877
5065
|
A graph is considered connected if every node is reachable from every
|
|
4878
5066
|
other node in the underlying undirected graph.
|
|
4879
5067
|
|
|
4880
5068
|
Returns
|
|
4881
5069
|
-------
|
|
4882
|
-
|
|
4883
|
-
A
|
|
4884
|
-
|
|
5070
|
+
Relationship
|
|
5071
|
+
A unary relationship containing a boolean indicator of whether the graph
|
|
5072
|
+
is connected.
|
|
5073
|
+
|
|
5074
|
+
Relationship Schema
|
|
5075
|
+
-------------------
|
|
5076
|
+
``is_connected(connected)``
|
|
5077
|
+
|
|
5078
|
+
* **connected** (*Boolean*): Whether the graph is connected.
|
|
4885
5079
|
|
|
4886
5080
|
Supported Graph Types
|
|
4887
5081
|
---------------------
|
|
@@ -4899,8 +5093,6 @@ class Graph():
|
|
|
4899
5093
|
--------
|
|
4900
5094
|
**Connected Graph Example**
|
|
4901
5095
|
|
|
4902
|
-
The following query will produce a result because the graph is connected.
|
|
4903
|
-
|
|
4904
5096
|
>>> from relationalai.semantics import Model, define, select
|
|
4905
5097
|
>>> from relationalai.semantics.reasoners.graph import Graph
|
|
4906
5098
|
>>>
|
|
@@ -4918,17 +5110,14 @@ class Graph():
|
|
|
4918
5110
|
... Edge.new(src=n4, dst=n3),
|
|
4919
5111
|
... )
|
|
4920
5112
|
>>>
|
|
4921
|
-
>>> # 3.
|
|
4922
|
-
>>> select(
|
|
5113
|
+
>>> # 3. Select and inspect the relation
|
|
5114
|
+
>>> select(graph.is_connected()).inspect()
|
|
4923
5115
|
▰▰▰▰ Setup complete
|
|
4924
|
-
|
|
4925
|
-
0
|
|
5116
|
+
is_connected
|
|
5117
|
+
0 True
|
|
4926
5118
|
|
|
4927
5119
|
**Disconnected Graph Example**
|
|
4928
5120
|
|
|
4929
|
-
The following query will produce no results because the graph is not
|
|
4930
|
-
connected.
|
|
4931
|
-
|
|
4932
5121
|
>>> from relationalai.semantics import Model, define, select
|
|
4933
5122
|
>>> from relationalai.semantics.reasoners.graph import Graph
|
|
4934
5123
|
>>>
|
|
@@ -4946,22 +5135,31 @@ class Graph():
|
|
|
4946
5135
|
... Edge.new(src=n4, dst=n5), # This edge creates a separate component
|
|
4947
5136
|
... )
|
|
4948
5137
|
>>>
|
|
4949
|
-
>>> # 3.
|
|
4950
|
-
>>> select(
|
|
5138
|
+
>>> # 3. Select and inspect the relation
|
|
5139
|
+
>>> select(graph.is_connected()).inspect()
|
|
4951
5140
|
▰▰▰▰ Setup complete
|
|
4952
|
-
|
|
4953
|
-
|
|
4954
|
-
Index: []
|
|
5141
|
+
is_connected
|
|
5142
|
+
0 False
|
|
4955
5143
|
|
|
4956
5144
|
"""
|
|
4957
|
-
|
|
4958
|
-
|
|
4959
|
-
|
|
4960
|
-
|
|
4961
|
-
|
|
5145
|
+
return self._is_connected
|
|
5146
|
+
|
|
5147
|
+
@cached_property
|
|
5148
|
+
def _is_connected(self):
|
|
5149
|
+
"""Lazily define and cache the self._is_connected relationship."""
|
|
5150
|
+
_is_connected_rel = self._model.Relationship("'The graph is connected' is {is_connected:Boolean}")
|
|
5151
|
+
_is_connected_rel.annotate(annotations.track("graphs", "is_connected"))
|
|
5152
|
+
|
|
5153
|
+
where(
|
|
4962
5154
|
self._num_nodes(0) |
|
|
4963
5155
|
count(self._reachable_from_min_node(self.Node.ref())) == self._num_nodes(Integer.ref())
|
|
4964
|
-
)
|
|
5156
|
+
).define(_is_connected_rel(True))
|
|
5157
|
+
|
|
5158
|
+
where(
|
|
5159
|
+
not_(_is_connected_rel(True))
|
|
5160
|
+
).define(_is_connected_rel(False))
|
|
5161
|
+
|
|
5162
|
+
return _is_connected_rel
|
|
4965
5163
|
|
|
4966
5164
|
|
|
4967
5165
|
@include_in_docs
|
|
@@ -5127,6 +5325,7 @@ class Graph():
|
|
|
5127
5325
|
def _jaccard_similarity(self):
|
|
5128
5326
|
"""Lazily define and cache the self._jaccard_similarity relationship."""
|
|
5129
5327
|
_jaccard_similarity_rel = self._model.Relationship(f"{{node_u:{self._NodeConceptStr}}} has a similarity to {{node_v:{self._NodeConceptStr}}} of {{similarity:Float}}")
|
|
5328
|
+
_jaccard_similarity_rel.annotate(annotations.track("graphs", "jaccard_similarity"))
|
|
5130
5329
|
|
|
5131
5330
|
if not self.weighted:
|
|
5132
5331
|
node_u, node_v = self.Node.ref(), self.Node.ref()
|
|
@@ -5378,6 +5577,7 @@ class Graph():
|
|
|
5378
5577
|
def _cosine_similarity(self):
|
|
5379
5578
|
"""Lazily define and cache the self._cosine_similarity relationship."""
|
|
5380
5579
|
_cosine_similarity_rel = self._model.Relationship(f"{{node_u:{self._NodeConceptStr}}} has a cosine similarity to {{node_v:{self._NodeConceptStr}}} of {{score:Float}}")
|
|
5580
|
+
_cosine_similarity_rel.annotate(annotations.track("graphs", "cosine_similarity"))
|
|
5381
5581
|
|
|
5382
5582
|
if not self.weighted:
|
|
5383
5583
|
node_u, node_v = self.Node.ref(), self.Node.ref()
|
|
@@ -5498,6 +5698,7 @@ class Graph():
|
|
|
5498
5698
|
def _adamic_adar(self):
|
|
5499
5699
|
"""Lazily define and cache the self._adamic_adar relationship."""
|
|
5500
5700
|
_adamic_adar_rel = self._model.Relationship(f"{{node_u:{self._NodeConceptStr}}} and {{node_v:{self._NodeConceptStr}}} have adamic adar score {{score:Float}}")
|
|
5701
|
+
_adamic_adar_rel.annotate(annotations.track("graphs", "adamic_adar"))
|
|
5501
5702
|
|
|
5502
5703
|
node_u, node_v, common_neighbor = self.Node.ref(), self.Node.ref(), self.Node.ref()
|
|
5503
5704
|
neighbor_count = Integer.ref()
|
|
@@ -5596,6 +5797,7 @@ class Graph():
|
|
|
5596
5797
|
def _preferential_attachment(self):
|
|
5597
5798
|
"""Lazily define and cache the self._preferential_attachment relationship."""
|
|
5598
5799
|
_preferential_attachment_rel = self._model.Relationship(f"{{node_u:{self._NodeConceptStr}}} and {{node_v:{self._NodeConceptStr}}} have preferential attachment score {{score:Integer}}")
|
|
5800
|
+
_preferential_attachment_rel.annotate(annotations.track("graphs", "preferential_attachment"))
|
|
5599
5801
|
|
|
5600
5802
|
node_u, node_v = self.Node.ref(), self.Node.ref()
|
|
5601
5803
|
count_u, count_v = Integer.ref(), Integer.ref()
|